PostScript == operator - operators

The value of == is not an operator, but rather a built-in procedure.
PLRM page 526 (pdf page 540).
a built-in procedure, does this mean that this is a PostScript procedure:
{ something here }
or an actual function e.g. in Java that you have to define yourself?

You can peek at ghostscript's implementation with /== load ==.
Or get the book Inside Postscript by Frank Meritt Braswell. It has an entire chapter about how the == procedure works in Adobe's printer implementations.
But the simple framework is based around the properties of the type operator which yields a typename. More specifically, it yields an executable name which designates the type of the argument. So you can easily implement a type-switch with a dictionary.
<<
/integertype { } % handle integer case
/realtype { } % handle floating-point case
/arraytype { } % handle array case
>> begin
5 type exec
2.0 type exec
{a b c} type exec
The different typed objects cause different procedures to be executed.
Using this, we can handle the different types that might be passed to the procedure.
/my== {
<<
/integertype { =string cvs print }
/realtype { =string cvs print }
/arraytype { dup xcheck {
({ ) print {my==} forall (} ) print
}{
([ ) print {my==} forall (] ) print
} }
>> begin dup type exec
} def
=string (described in the book) is a pre-allocated 128-byte scratch buffer used by the = and == procedures for this exact purpose: supplying the argument to cvs.

That's a procedure.
I don't speak Java, but in PostScript if its executable, and its not an operator (part of the language) then you have to define it yourself.
Note that in PostScript it is also possible to redefine operators, so just because it has the name of an operator, doesn't mean it is an operator, it might still be a procedure.

Related

Binding of private attributes: nqp::bindattr vs :=

I'm trying to find how the binding operation works on attributes and what makes it so different from nqp::bindattr. Consider the following example:
class Foo {
has #!foo;
submethod TWEAK {
my $fval = [<a b c>];
use nqp;
nqp::bindattr( nqp::decont(self), $?CLASS, '#!foo',
##!foo :=
Proxy.new(
FETCH => -> $ { $fval },
STORE => -> $, $v { $fval = $v }
)
);
}
method check {
say #!foo.perl;
}
}
my $inst = Foo.new;
$inst.check;
It prints:
$["a", "b", "c"]
Replacing nqp::bindattr with the binding operator from the comment gives correct output:
["a", "b", "c"]
Similarly, if foo is a public attribute and accessor is used the output would be correct too due to deconterisation taking place within the accessor.
I use similar code in my AttrX::Mooish module where use of := would overcomplicate the implementation. So far, nqp::bindattr did the good job for me until the above problem arised.
I tried tracing down Rakudo's internals looking for := implementation but without any success so far. I would ask here either for an advise as to how to simulate the operator or where in the source to look for its implementation.
Before I dig into the answer: most things in this post are implementation-defined, and the implementation is free to define them differently in the future.
To find out what something (naively) compiles into under Rakudo Perl 6, use the --target=ast option (perl6 --target=ast foo.p6). For example, the bind in:
class C {
has $!a;
submethod BUILD() {
my $x = [1,2,3];
$!a := $x
}
}
Comes out as:
- QAST::Op(bind) :statement_id<7>
- QAST::Var(attribute $!a) <wanted> $!a
- QAST::Var(lexical self)
- QAST::WVal(C)
- QAST::Var(lexical $x) $x
While switching it for #!a like here:
class C {
has #!a;
submethod BUILD() {
my $x = [1,2,3];
#!a := $x
}
}
Comes out as:
- QAST::Op(bind) :statement_id<7>
- QAST::Var(attribute #!a) <wanted> #!a
- QAST::Var(lexical self)
- QAST::WVal(C)
- QAST::Op(p6bindassert)
- QAST::Op(decont)
- QAST::Var(lexical $x) $x
- QAST::WVal(Positional)
The decont instruction is the big difference here, and it will take the contents of the Proxy by calling its FETCH, thus why the containerization is gone. Thus, you can replicate the behavior by inserting nqp::decont around the Proxy, although that rather begs the question of what the Proxy is doing there if the correct answer is obtained without it!
Both := and = are compiled using case analysis (namely, by looking at what is on the left hand side). := only works for a limited range of simple expressions on the left; it is a decidedly low-level operator. By contrast, = falls back to a sub call if the case analysis doesn't come up with a more efficient form to emit, though in most cases it manages something better.
The case analysis for := inserts a decont when the target is a lexical or attribute with sigil # or %, since - at a Perl 6 level - having an item bound to an # or % makes no sense. Using nqp::bindattr is going a level below Perl 6 semantics, and so it's possible to end up with the Proxy bound directly there using that. However, it also violates expectations elsewhere. Don't expect that to go well (but it seems you don't want to do that anyway.)

In Perl 6, is there a way to get the Pod declarator block that is attached to a specific multi sub candidate?

Perl 6 has a cool feature which allows one to get any Pod declarator block that is attached to a subroutine (or class, role, etc.), using the WHY method:
#|(Some enlightening words about myfunc.)
sub myfunc (Int $i) { say "You provided an integer: $i"; };
#=(Some more words about myfunc.)
say &myfunc.WHY;
This displays:
Some enlightening words about myfunc.
Some more words about myfunc.
Unfortunately, when one has multiple candidates for a subroutine, one can't just invoke .WHY on the subroutine name:
#|(myfunc accepts an integer.)
multi myfunc (Int $i) { say "You provided an integer $i"; };
#|(myfunc accepts a string.)
multi myfunc (Str $s) { say "You provided a string $s"; };
say &myfunc.WHY;
The result:
No documentation available for type 'Sub'.
Perhaps it can be found at https://docs.perl6.org/type/Sub
Is there a way to get the Pod declarator block that is attached to a specific multi sub candidate? Is there a way to do so for all a subroutine's candidates?
You look up the multi with candidates or cando.
When initially posted I couldn't find a canned method for looking up a multi sub by signature but Christoph remedied that.
#| Initiate a specified spell normally
multi sub cast(Str $spell) {
say "casting spell $spell";
}
#= (do not use for class 7 spells)
#| Cast a heavy rock etc in irritation
multi sub cast(Str $heavy-item, Int $n) {
say "chucking $n heavy $heavy-item";
}
say "doc for cast spell";
say &cast.candidates[0].WHY;
say "doc for throwing rocks";
say &cast.candidates[1].WHY;
say "find doc for throwing things";
for &cast.candidates {
if .signature ~~ :( Str, Int ) {
say .WHY;
}
}
# more advanced
say &cast.cando(\(Str, Int))>>.WHY; # thanks to Christoph
&cast.candidates.first: { .signature ~~ :(Str, Int) } andthen .WHY.say;
OUTPUT:
doc for cast spell
Initiate a specified spell normally
(do not use for class 7 spells)
doc for throwing rocks
Cast a heavy rock etc in irritation
find doc for throwing things
Cast a heavy rock etc in irritation
... repeated for variants ...
Get all documentation via candidates:
&myfunc.candidates>>.WHY
Get documentation of narrowest matching candidate via cando:
&myfunc.cando(\(42)).first.WHY
This does not really answer your question, but tries to explain why using WHY on a multi does not work; it's mainly because it points to the proto of the multi
#|(my-multi-func accepts either an integer or a string)
proto my-multi-func (|) {*}
#|(myfunc accepts an integer.)
multi my-multi-func (Int $i) { say "You provided an integer $i"; };
#|(myfunc accepts a string.)
multi my-multi-func (Str $s) { say "You provided a string $s"; };
say "my-multi-func is a {&my-multi-func.perl} and does {&my-multi-func.WHY}";
I attach the {&my-multi-func.perl} here because that is what gave me the hint. If you don't define a proto, it returns
my-multi-func is a sub my-multi-func (;; Mu | is raw) { #`(Sub|59650976) ... }
, which is none of the defined multis, ergo the proto. Of course if you want to access those particular definitions of the candidates, #Christopher Bottoms answer is just perfect.
This is a little indirect, but ...
You can store each multi myfunc in a variable and call WHY on that variable, yet still call myfunc as before:
#!/bin/env perl6
#|(myfunc accepts an integer.)
my $func_int = multi myfunc (Int $i) { say "You provided an integer $i"; }
#=(More about Int version of myfunc)
#|(myfunc accepts a string.)
my $func_string = multi myfunc (Str $s) { say "You provided a string $s"; }
#=(More about Str version of myfunc)
myfunc(10); # myfunc works as normal
say $func_int.WHY; # show POD declarator block
say ''; # Blank line to separate output into two groups
myfunc("bar");
say $func_string.WHY;
Resulting in this output:
You provided an integer 10
myfunc accepts an integer.
More about Int version of myfunc
You provided a string bar
myfunc accepts a string.
More about Str version of myfunc
This is using Rakudo Star 2018.01 on CentOS 6.7.

how to create and export dynamic operators

I have some classes (and will need quite a few more) that look like this:
use Unit;
class Unit::Units::Ampere is Unit
{
method TWEAK { with self {
.si = True;
# m· kg· s· A ·K· mol· cd
.si-signature = [ 0, 0, 0, 1, 0, 0, 0 ];
.singular-name = "ampere";
.plural-name = "ampere";
.symbol = "A";
}}
sub postfix:<A> ($value) returns Unit::Units::Ampere is looser(&prefix:<->) is export(:short) {
return Unit::Units::Ampere.new( :$value );
};
sub postfix:<ampere> ($value) returns Unit::Units::Ampere is looser(&prefix:<->) is export(:long) {
$value\A;
};
}
I would like to be able to construct and export the custom operators dynamically at runtime. I know how to work with EXPORT, but how do I create a postfix operator on the fly?
I ended up basically doing this:
sub EXPORT
{
return %(
"postfix:<A>" => sub is looser(&prefix:<->) {
#do something
}
);
}
which is disturbingly simple.
For the first question, you can create dynamic subs by returning a sub from another. To accept only an Ampere parameter (where "Ampere" is chosen programmatically), use a type capture in the function signature:
sub make-combiner(Any:U ::Type $, &combine-logic) {
return sub (Type $a, Type $b) {
return combine-logic($a, $b);
}
}
my &int-adder = make-combiner Int, {$^a + $^b};
say int-adder(1, 2);
my &list-adder = make-combiner List, {(|$^a, |$^b)};
say list-adder(<a b>, <c d>);
say list-adder(1, <c d>); # Constraint type check fails
Note that when I defined the inner sub, I had to put a space after the sub keyword, lest the compiler think I'm calling a function named "sub". (See the end of my answer for another way to do this.)
Now, on to the hard part: how to export one of these generated functions? The documentation for what is export really does is here: https://docs.perl6.org/language/modules.html#is_export
Half way down the page, they have an example of adding a function to the symbol table without being able to write is export at compile time. To get the above working, it needs to be in a separate file. To see an example of a programmatically determined name and programmatically determined logic, create the following MyModule.pm6:
unit module MyModule;
sub make-combiner(Any:U ::Type $, &combine-logic) {
anon sub combiner(Type $a, Type $b) {
return combine-logic($a, $b);
}
}
my Str $name = 'int';
my $type = Int;
my package EXPORT::DEFAULT {
OUR::{"&{$name}-eater"} := make-combiner $type, {$^a + $^b};
}
Invoke Perl 6:
perl6 -I. -MMyModule -e "say int-eater(4, 3);"
As hoped, the output is 7. Note that in this version, I used anon sub, which lets you name the "anonymous" generated function. I understand this is mainly useful for generating better stack traces.
All that said, I'm having trouble dynamically setting a postfix operator's precedence. I think you need to modify the Precedence role of the operator, or create it yourself instead of letting the compiler create it for you. This isn't documented.

What is the point of coercions like Int(Cool)?

The Perl 6 Web site on functions says
Coercion types can help you to have a specific type inside a routine, but accept wider input. When the routine is called, the argument is automatically converted to the narrower type.
sub double(Int(Cool) $x) {
2 * $x
}
say double '21'; # 42
say double Any; # Type check failed in binding $x; expected 'Cool' but got 'Any'
Here the Int is the target type to which the argument will be coerced, and Cool is the type that the routine accepts as input.
But what is the point for the sub? Isn't $x just an Int? Why would you restrict the caller to implement Cool for the argument?
I'm doubly confused by the example because Int already is Cool. So I did an example where the types don't share a hierarchy:
class Foo { method foomethod { say 'foomethod' } }
class Bar {}
class Quux is Foo {
# class Quux { # compile error
method Bar { Bar.new }
}
sub foo(Bar(Foo) $c) {
say $c.WHAT; # (Bar)
# $c.foomethod # fails if uncommented: Method 'foomethod' not found for invocant of class 'Bar'
}
foo(Quux.new)
Here the invocant of foo is restricted to provide a Foo that can be converted to a Bar but foo cannot even call a method of Foo on $c because its type is Bar. So why would foo care that the to-be-coerced type is a Foo in the first place?
Could someone shed some light on this? Links to appropriate documentation and parts of the spec are appreciated as well. I couldn't find anything useful there.
Update Having reviewed this answer today I've concluded I had completely misunderstood what #musiKk was getting at. This was revealed most clearly in #darch's question and #musiKk's response:
#darch: Or is your question why one might prefer Int(Cool) over Int(Any)? If that's the case, that would be the question to ask.
#musiKk: That is exactly my question. :)
Reviewing the many other answers I see none have addressed it the way I now think it warrants addressing.
I might be wrong of course so what I've decided to do is leave the original question as is, in particular leaving the title as is, and leave this answer as it was, and instead write a new answer addressing #darch's reformulation.
Specify parameter type, with no coercion: Int $x
We could declare:
sub double (Int $x) { ... } # Accept only Int. (No coercion.)
Then this would work:
double(42);
But unfortunately typing 42 in response to this:
double(prompt('')); # `prompt` returns the string the user types
causes the double call to fail with Type check failed in binding $x; expected Int but got Str ("42") because 42, while looking like a number, is technically a string of type Str, and we've asked for no coercion.
Specify parameter type, with blanket coercion: Int() $x
We can introduce blanket coercion of Any value in the sub's signature:
sub double (Int(Any) $x) { ... } # Take Any value. Coerce to an Int.
Or:
sub double (Int() $x) { ... } # Same -- `Int()` coerces from Any.
Now, if you type 42 when prompted by the double(prompt('')); statement, the run-time type-check failure no longer applies and instead the run-time attempts to coerce the string to an Int. If the user types a well-formed number the code just works. If they type 123abc the coercion will fail at run-time with a nice error message:
Cannot convert string to number: trailing characters after number in '123⏏abc'
One problem with blanket coercion of Any value is that code like this:
class City { ... } # City has no Int coercion
my City $city;
double($city);
fails at run-time with the message: "Method 'Int' not found for invocant of class 'City'".
Specify parameter type, with coercion from Cool values: Int(Cool) $x
We can choose a point of balance between no coercion and blanket coercion of Any value.
The best class to coerce from is often the Cool class, because Cool values are guaranteed to either coerce nicely to other basic types or generate a nice error message:
# Accept argument of type Cool or a subclass and coerce to Int:
sub double (Int(Cool) $x) { ... }
With this definition, the following:
double(42);
double(prompt(''));
works as nicely as it can, and:
double($city);
fails with "Type check failed in binding $x; expected Cool but got City (City)" which is arguably a little better diagnostically for the programmer than "Method 'Int' not found for invocant of class 'City'".
why would foo care that the to-be-coerced type is a Foo in the first place?
Hopefully it's now obvious that the only reason it's worth limiting the coerce-from-type to Foo is because that's a type expected to successfully coerce to a Bar value (or, perhaps, fail with a friendly message).
Could someone shed some light on this? Links to appropriate documentation and parts of the spec are appreciated as well. I couldn't find anything useful there.
The document you originally quoted is pretty much all there is for enduser doc. Hopefully it makes sense now and you're all set. If not please comment and we'll go from there.
What this does is accept a value that is a subtype of Cool, and tries to transform it into an Int. At that point it is an Int no matter what it was before.
So
sub double ( Int(Cool) $n ) { $n * 2 }
can really be thought of as ( I think this is how it was actually implemented in Rakudo )
# Int is a subtype of Cool otherwise it would be Any or Mu
proto sub double ( Cool $n ) {*}
# this has the interior parts that you write
multi sub double ( Int $n ) { $n * 2 }
# this is what the compiler writes for you
multi sub double ( Cool $n ) {
# calls the other multi since it is now an Int
samewith Int($n);
}
So this accepts any of Int, Str, Rat, FatRat, Num, Array, Hash, etc. and tries to convert it into an Int before calling &infix:<*> with it, and 2.
say double ' 5 '; # 25
say double 2.5; # 4
say double [0,0,0]; # 6
say double { a => 0, b => 0 }; # 4
You might restrict it to a Cool instead of Any as all Cool values are essentially required to provide a coercion to Int.
( :( Int(Any) $ ) can be shortened to just :( Int() $ ) )
The reason you might do this is that you need it to be an Int inside the sub because you are calling other code that does different things with different types.
sub example ( Int(Cool) $n ) returns Int {
other-multi( $n ) * $n;
}
multi sub other-multi ( Int $ ) { 10 }
multi sub other-multi ( Any $ ) { 1 }
say example 5; # 50
say example 4.5; # 40
In this particular case you could have written it as one of these
sub example ( Cool $n ) returns Int {
other-multi( Int($n) ) * Int($n);
}
sub example ( Cool $n ) returns Int {
my $temp = Int($n);
other-multi( $temp ) * $temp;
}
sub example ( Cool $n is copy ) returns Int {
$n = Int($n);
other-multi( $n ) * $n;
}
None of them are as clear as the one that uses the signature to coerce it for you.
Normally for such a simple function you can use one of these and it will probably do what you want.
my &double = * * 2; # WhateverCode
my &double = * × 2; # ditto
my &double = { $_ * 2 }; # bare block
my &double = { $^n * 2 }; # block with positional placeholder
my &double = -> $n { $n * 2 }; # pointy block
my &double = sub ( $n ) { $n * 2 } # anon sub
my &double = anon sub double ( $n ) { $n * 2 } # anon sub with name
my &double = &infix:<*>.assuming(*,2); # curried
my &double = &infix:<*>.assuming(2);
sub double ( $n ) { $n * 2 } # same as :( Any $n )
Am I missing something? I'm not a Perl 6 expert, but it appears the syntax allows one to specify independently both what input types are permissible and how the input will be presented to the function.
Restricting the allowable input is useful because it means the code will result in an error, rather than a silent (useless) type conversion when the function is called with a nonsensical parameter.
I don't think an example where the two types are not in a hierarchical relationship makes sense.
Per comments on the original question, a better version of #musiKk's question "What is the point of coercions like Int(Cool)?" turned out to be:
Why might one prefer Int(Cool) over Int(Any)?
A corollary, which I'll also address in this answer, is:
Why might one prefer Int(Any) over Int(Cool)?
First, a list of various related options:
sub _Int_strong (Int $) {} # Argument must be Int
sub _Int_cool (Int(Cool) $) {} # Argument must be Cool; Int invoked
sub _Int_weak (Int(Any) $) {} # Argument must be Any; Int invoked
sub _Int_weak2 (Int() $) {} # same
sub _Any (Any $) {} # Argument must be Any
sub _Any2 ( $) {} # same
sub _Mu (Mu $) {} # Weakest typing - just memory safe (Mu)
_Int_strong val; # Fails to bind if val is not an Int
_Int_cool val; # Fails to bind if val is not Cool. Int invoked.
_Int_weak val; # Fails to bind if val is not Any. Int invoked.
_Any val; # Fails to bind if val is Mu
_Mu val; # Will always bind. If val is a native value, boxes it.
Why might one prefer Int(Cool) over Int(Any)?
Because Int(Cool) is slightly stronger typing. The argument must be of type Cool rather than the broader Any and:
Static analysis will reject binding code written to pass an argument that isn't Cool to a routine whose corresponding parameter has the type constraint Int(Cool). If static analysis shows there is no other routine candidate able to accept the call then the compiler will reject it at compile time. This is one of the meanings of "strong typing" explained in the last section of this answer.
If a value is Cool then it is guaranteed to have a well behaved .Int conversion method. So it will not yield a Method not found error at run-time and can be relied on to provide a good error message if it fails to produce a converted to integer value.
Why might one prefer Int(Any) over Int(Cool)?
Because Int(Any) is slightly weaker typing in that the argument can be of any regular type and P6 will just try and make it work:
.Int will be called on an argument that's passed to a routine whose corresponding parameter has the type constraint Int(...) no matter what the ... is. Provided the passed argument has an .Int method the call and subsequent conversion has a chance of succeeding.
If the .Int fails then the error message will be whatever the .Int method produces. If the argument is actually Cool then the .Int method will produce a good error message if it fails to convert to an Int. Otherwise the .Int method is presumably not a built in one and the result will be pot luck.
Why Foo(Bar) in the first place?
And what's all this about weak and strong typing?
An Int(...) constraint on a function parameter is going to result in either:
A failure to type check; or
An.Int conversion of the corresponding argument that forces it to its integer value -- or fails, leaving the corresponding parameter containing a Failure.
Using Wikipedia definitions as they were at the time of writing this answer (2019) this type checking and attempted conversion will be:
strong typing in the sense that a type constraint like Int(...) is "use of programming language types in order to both capture invariants of the code, and ensure its correctness, and definitely exclude certain classes of programming errors";
Currently weak typing in Rakudo in the sense that Rakudo does not check the ... in Int(...) at compile time even though in theory it could. That is, sub double (Int $x) {}; double Date; yields a compile time error (Calling double(Date) will never work) whereas sub double (Int(Cool) $x) {}; double Date; yields a run time error (Type check failed in binding).
type conversion;
weak typing in the sense that it's implicit type conversion in the sense that the compiler will handle the .Int coercion as part of carrying out the call;
explicit type conversion in the sense that the Int(...) constraint is explicitly directing the compiler to do the conversion as part of binding a call;
checked explicit type conversion -- P6 only does type safe conversions/coercions.
I believe the answer is as simple as you may not want to restrict the argument to Int even though you will be treating it as Int within the sub. say for some reason you want to be able to multiply an Array by a Hash, but fail if the args can't be treated as Int (i.e. is not Cool).
my #a = 1,2,3;
my %h = 'a' => 1, 'b' => 2;
say #a.Int; # 3 (List types coerced to the equivalent of .elems when treated as Int)
say %h.Int; # 2
sub m1(Int $x, Int $y) {return $x * $y}
say m1(3,2); # 6
say m1(#a,%h); # does not match
sub m2(Int(Cool) $x, Int(Cool) $y) {return $x * $y}
say m2('3',2); # 6
say m2(#a,%h); # 6
say m2('foo',2); # does not match
of course, you could also do this without the signature because the math operation will coerce the type automatically:
sub m3($x,$y) {return $x * $y}
say m3(#a,%h); # 6
however, this defers your type check to the inside of the sub, which kind of defeats the purpose of a signature and prevents you from making the sub a multi
All subtypes of Cool will be (as Cool requires them to) coerced to an Int. So if an operator or routine internal to your sub only works with Int arguments, you don't have to add an extra statement/expression converting to an Int nor does that operator/routine's code need to account for other subtypes of Cool. It enforces that the argument will be an Int inside of your sub wherever you use it.
Your example is backwards:
class Foo { method foomethod { say 'foomethod' } }
class Bar {}
class Quux is Bar {
method Foo { Foo.new }
}
sub foo(Foo(Bar) $c) {
#= converts $c of type Bar to type Foo
#= returns result of foomethod
say $c.WHAT; #-> (Foo)
$c.foomethod #-> foomethod
}
foo(Quux.new)

Determine type of a variable in Tcl

I'm looking for a way to find the type of a variable in Tcl. For example if I have the variable $a and I want to know whether it is an integer.
I have been using the following so far:
if {[string is boolean $a]} {
#do something
}
and this seems to work great for the following types:
alnum, alpha, ascii, boolean, control, digit, double, false, graph, integer, lower, print, punct, space, true, upper, wordchar, xdigit
However it is not capable to tell me if my variable might be an array, a list or a dictionary. Does anyone know of a way to tell if a variable is either of those three?
Tcl's variables don't have types (except for whether or not they're really an associative array of variables — i.e., using the $foo(bar) syntax — for which you use array exists) but Tcl's values do. Well, somewhat. Tcl can mutate values between different types as it sees fit and does not expose this information[*]; all you can really do is check whether a value conforms to a particular type.
Such conformance checks are done with string is (where you need the -strict option, for ugly historical reasons):
if {[string is integer -strict $foo]} {
puts "$foo is an integer!"
}
if {[string is list $foo]} { # Only [string is] where -strict has no effect
puts "$foo is a list! (length: [llength $foo])"
if {[llength $foo]&1 == 0} {
# All dictionaries conform to lists with even length
puts "$foo is a dictionary! (entries: [dict size $foo])"
}
}
Note that all values conform to the type of strings; Tcl's values are always serializable.
[EDIT from comments]: For JSON serialization, it's possible to use dirty hacks to produce a “correct” serialization (strictly, putting everything in a string would be correct from Tcl's perspective but that's not precisely helpful to other languages) with Tcl 8.6. The code to do this, originally posted on Rosetta Code is:
package require Tcl 8.6
proc tcl2json value {
# Guess the type of the value; deep *UNSUPPORTED* magic!
regexp {^value is a (.*?) with a refcount} \
[::tcl::unsupported::representation $value] -> type
switch $type {
string {
# Skip to the mapping code at the bottom
}
dict {
set result "{"
set pfx ""
dict for {k v} $value {
append result $pfx [tcl2json $k] ": " [tcl2json $v]
set pfx ", "
}
return [append result "}"]
}
list {
set result "\["
set pfx ""
foreach v $value {
append result $pfx [tcl2json $v]
set pfx ", "
}
return [append result "\]"]
}
int - double {
return [expr {$value}]
}
booleanString {
return [expr {$value ? "true" : "false"}]
}
default {
# Some other type; do some guessing...
if {$value eq "null"} {
# Tcl has *no* null value at all; empty strings are semantically
# different and absent variables aren't values. So cheat!
return $value
} elseif {[string is integer -strict $value]} {
return [expr {$value}]
} elseif {[string is double -strict $value]} {
return [expr {$value}]
} elseif {[string is boolean -strict $value]} {
return [expr {$value ? "true" : "false"}]
}
}
}
# For simplicity, all "bad" characters are mapped to \u... substitutions
set mapped [subst -novariables [regsub -all {[][\u0000-\u001f\\""]} \
$value {[format "\\\\u%04x" [scan {& } %c]]}]]
return "\"$mapped\""
}
Warning: The above code is not supported. It depends on dirty hacks. It's liable to break without warning. (But it does work. Porting to Tcl 8.5 would require a tiny C extension to read out the type annotations.)
[*] Strictly, it does provide an unsupported interface for discovering the current type annotation of a value in 8.6 — as part of ::tcl::unsupported::representation — but that information is in a deliberately human-readable form and subject to change without announcement. It's for debugging, not code. Also, Tcl uses rather a lot of different types internally (e.g., cached command and variable names) that you won't want to probe for under normal circumstances; things are rather complex under the hood…
The other answers all provide very useful information, but it's worth noting something that a lot of people don't seem to grok at first.
In Tcl, values don't have a type... they question is whether they can be used as a given type. You can think about it this way
string is integer $a
You're not asking
Is the value in $a an integer
What you are asking is
Can I use the value in $a as an integer
Its useful to consider the difference between the two questions when you're thinking along the lines of "is this an integer". Every integer is also a valid list (of one element)... so it can be used as either and both string is commands will return true (as will several others for an integer).
If you want to deal with JSON then I highly suggest you read the JSON page on the Tcl wiki: http://wiki.tcl.tk/json.
On that page I posted a simple function that compiles Tcl values to JSON string given a formatting descriptor. I also find the discussion on that page very informative.
For arrays you want array exists
for dicts you want dict exists
for a list I don't think there is a built in way prior to 8.5?, there is this from http://wiki.tcl.tk/440
proc isalist {string} {
return [expr {0 == [catch {llength $string}]}]
}
To determine if a variable is an array:
proc is_array {var} {
upvar 1 $var value
if {[catch {array names $value} errmsg]} { return 1 }
return 0
}
# How to use it
array set ar {}
set x {1 2 3}
puts "ar is array? [is_array ar]"; # ar is array? 1
puts "x is array? [is_array x]"; # x is array? 0
For the specific case of telling if a value is usable as a dictionary, tcllib's dicttool package has a dict is_dict <value> command that returns a true value if <value> can act as one.