Re: "Implied types, first try." Or "Its amazing what you can do with potatoes"

2001-07-10 Thread John Porter

David L. Nicol wrote:
> First off, I'm going to pound on one of my deceased horses a bit:

I'll add one obligatory thwack with my own barbed flog, David. Hear hear.


> > my Num$dec = 4.0;
> > my Int$int = $dec;  # Num -> Int okay since 4.0 truncates to 4
> > # with no(?) information lost
> 
> You have lost information.  You have lost one digit of precision.
> That is not insignificant.

Correction: You have added an infinite number of digits of precision.
Integers have infinite precision.


> This would be a nice thing to have a pragmata for, what hash refs 
> stringify to.

Um, one pragma.


-- 
John Porter




Re: "Implied types, first try." Or "Its amazing what you can do with potatoes"

2001-07-10 Thread Steve Fink

Michael G Schwern wrote:

> On Mon, Jul 09, 2001 at 08:54:49PM -0700, Steve Fink wrote:
> 
>>my Num $x = 3.0;
>>$x++;
>>my Int $y = $x;
>>
>>Could be compile-time, if you do constant folding first.
>>
> 
> Alot of how much checking we can do at compile-time depends on how
> long we have to compile, and thus optimize.  If you precompile your
> Perl 6 program, then we have all the time in the world.  If you're
> compiling on the fly (ie. how we do it now) then you can't spend so
> much time doing constant folding.
> 
> Dunno yet.


Right. But currently whether or not it does constant folding is 
completely invisible to the user. This would make it visible. On the 
other hand, strong typing always has a tradeoff between safety and 
flexibility; this adds performance into the mix.

If you're experimenting, I suggest for the first version completely 
sidestepping the question: don't distinguish between integers and 
numbers. Especially in perl6, it's probably not a very useful 
distinction anyway (if integers are constantly autopromoting to bigints).

>>>but we like to mix types up in hashes.  One way we can allow this is
>>>to have a generic type, Value (or perhaps Scalar) that every type will
>>>cast into.  Hash keys would be implicitly of type Value.
>>>
>>> my Value %hash = (foo => 1, bar => 2, baz => 'yarrow');
>>>
>>>but that's pretty much the same as switching off strong typing.  So
>>>obviously hashes have to have per-key types.  I will leave the syntax
>>>of that as an exercise for the reader.  It would have to make it easy
>>>to declare lists of keys as being a single type.  If we come up with
>>>something nice here, it can probably be backported onto arrays.
>>>
>>It's only switching off strong types for that variable, so it's a 
>>valuable thing to do.
>>
> 
> Its switching off types for pretty much all hashes, since hashes very
> often have mixed type values (think objects and structs).  We've
> definately got to have per-key hash types.


Sure. If you stick to the type == subset of values framework, then those 
keys are the singleton subsets containing the strings 'foo', 'bar', and 
'baz'. Call them foo_t, bar_t, and baz_t. Then the type of %hash is

{ foo_t => Int, bar_t => Int, baz_t => String }

or perhaps

{ foo_t => Int, bar_t => Int, baz_t => String, (String-foo_t-bar_t-baz_t) => Value }


where String-foo_t-bar_t-baz_t is the subset containing all strings 
except those listed.

>>my qt(String => DontEvenThinkAboutIt) %tree = (id => 1);
>>$tree{parent} = \%tree;
>>
>>(or if you think you can tackle that)
>>
>>$tree{parent_with_distance} = [ 0, \%tree ];
>>
>>a map from strings to numbers and lists of two elements, where the first 
>>element is a number and the second is a reference to a map from strings 
>>to numbers and lists of two elements...
>>
> 
> I don't follow.


I'm just pointing out that if you do have a way of specifying per-key 
types, then you have to deal with some very hairy, possibly 
self-referential types, some of which are too difficult to deal with, so 
you'll always need some sort of Value fallback.

>>How is a regex different from any other function that makes sense with 
>>multiple types?
>>
> 
> Most functions don't change a variable in place.  s/// does.  substr()
> is also a problem.  .= another.


$foo =~ s/A/B/ is the same as $foo = substitute($foo, A, B).

> 
> my $foo = 42;   # Int type implied
> my String $bar = read_line($some_file);
> $foo .= $bar;
> 
> That expands out to 
> 
> $foo = $foo . $bar;
> 
> $foo . $bar is fine, since Ints can auto-cast to Strings.  The result
> is a new string which is reassigned to $foo.  If that string looks
> like an integer, its okay.  Otherwise its a run-time type violation.
> 
> Not having auto-casts would solve this problem, but then it would make
> life suck in so many other ways.
> 
> 
> 
>>my Int $foo = 42;
>>my Num $bar = 4.2;
>>my Int $foo2 = increment($foo);
>>my Num $bar2 = increment($bar);
>>my Sub $sub2 = increment(sub { 42 });
>>
>>sub increment {
>>  my $x = shift;
>>  if (ref $x eq 'CODE') {
>>  return sub { 1 + $x->(); }
>>  } else...
>>}
>>
> 
> I'm going to presume for the moment that functions must have declared
> types, otherwise they simply take and return Values.  I haven't really
> thought about making function signatures implicit yet.


I wasn't going there, I was just pointing out that functions that return 
multiple types are fairly common. On the other hand, I suspect you could 
probably capture most of them with SML-like type expressions. For 
example, increment would have type 'a -> 'a.

>>But it needs to resolve the type of an arbitrarily nasty expression 
>>anyway, doesn't it?
>>
>>my $foo = (time % 2) ? "one" : 3; # $foo is a String
>>
> 
> I don't know how far we can do with this.  Again, it depends alot on
> how much time we have to compile.
> 
> 
> 
>>Or consider
>>
> 
> Yep, these are all nasty possiblities.  mjd sugge

Re: "Implied types, first try." Or "Its amazing what you can do with potatoes"

2001-07-09 Thread Michael G Schwern

On Mon, Jul 09, 2001 at 08:54:49PM -0700, Steve Fink wrote:
> User-defined types?

Haven't even thought about them yet.


> > I'm pondering this being okay:
> > 
> > my Num$dec = 4.0;
> > my Int$int = $dec;  # Num -> Int okay since 4.0 truncates to 4
> > # with no(?) information lost
> > 
> > but I think it will complicate my Grand Plan which I'm not ready to
> > reveal.
> 
> Compile-time or run-time?

It would have to be a run-time type check if we allowed Num to cast to
Int.  Similar to allowing Strings to cast to Ints and Nums.  Is that
run-time nature that worries me, might get in the way of implied
types.


> my Num $x = 3.0;
> $x++;
> my Int $y = $x;
> 
> Could be compile-time, if you do constant folding first.

Alot of how much checking we can do at compile-time depends on how
long we have to compile, and thus optimize.  If you precompile your
Perl 6 program, then we have all the time in the world.  If you're
compiling on the fly (ie. how we do it now) then you can't spend so
much time doing constant folding.

Dunno yet.


> So Int is a subset of Num, and '4' and '4.0' are considered to be
> exactly equivalent.

Yes, that's my thinking.  I'm going to have to play with it and see
how annoying it is if Nums don't auto-cast to Ints.


> > but we like to mix types up in hashes.  One way we can allow this is
> > to have a generic type, Value (or perhaps Scalar) that every type will
> > cast into.  Hash keys would be implicitly of type Value.
> > 
> > my Value %hash = (foo => 1, bar => 2, baz => 'yarrow');
> > 
> > but that's pretty much the same as switching off strong typing.  So
> > obviously hashes have to have per-key types.  I will leave the syntax
> > of that as an exercise for the reader.  It would have to make it easy
> > to declare lists of keys as being a single type.  If we come up with
> > something nice here, it can probably be backported onto arrays.
> 
> It's only switching off strong types for that variable, so it's a 
> valuable thing to do.

Its switching off types for pretty much all hashes, since hashes very
often have mixed type values (think objects and structs).  We've
definately got to have per-key hash types.


> my qt(String => DontEvenThinkAboutIt) %tree = (id => 1);
> $tree{parent} = \%tree;
> 
> (or if you think you can tackle that)
> 
> $tree{parent_with_distance} = [ 0, \%tree ];
> 
> a map from strings to numbers and lists of two elements, where the first 
> element is a number and the second is a reference to a map from strings 
> to numbers and lists of two elements...

I don't follow.


> How is a regex different from any other function that makes sense with 
> multiple types?

Most functions don't change a variable in place.  s/// does.  substr()
is also a problem.  .= another.

my $foo = 42;   # Int type implied
my String $bar = read_line($some_file);
$foo .= $bar;

That expands out to 

$foo = $foo . $bar;

$foo . $bar is fine, since Ints can auto-cast to Strings.  The result
is a new string which is reassigned to $foo.  If that string looks
like an integer, its okay.  Otherwise its a run-time type violation.

Not having auto-casts would solve this problem, but then it would make
life suck in so many other ways.


> my Int $foo = 42;
> my Num $bar = 4.2;
> my Int $foo2 = increment($foo);
> my Num $bar2 = increment($bar);
> my Sub $sub2 = increment(sub { 42 });
> 
> sub increment {
>   my $x = shift;
>   if (ref $x eq 'CODE') {
>   return sub { 1 + $x->(); }
>   } else...
> }

I'm going to presume for the moment that functions must have declared
types, otherwise they simply take and return Values.  I haven't really
thought about making function signatures implicit yet.


> sub escape_cgi_param { return quotemeta(shift()) }
> 
> my Int $id = escape_cgi_param(...);
> my String $name = escape_cgi_param(...);

The latter is fine.  The former might be a run-time type violation if
escape_cgi_param returns something that doesn't look like an integer.


> But it needs to resolve the type of an arbitrarily nasty expression 
> anyway, doesn't it?
> 
> my $foo = (time % 2) ? "one" : 3; # $foo is a String

I don't know how far we can do with this.  Again, it depends alot on
how much time we have to compile.


> Or consider

Yep, these are all nasty possiblities.  mjd suggested I go through
some real-world code and see how much of it I can make implicit, what
problems are encountered, if there are any restrictions I might have
to make, etc...  I'm going to do that a bit and come back.  Functions
will be tough.


> (you wrote your ramblings, I wrote mine. I spent much time thinking in 
> circles about this stuff during the RFC phase and didn't really come up 
> with much. So now I'm seeing what I come up with without thinking. :-) )

Yeah, not thinking can sometimes reap interesting ideas. :)


-- 

Michael G. Schwern   <[EMAIL PROTECTED]>http://www.pobox.com

Re: "Implied types, first try." Or "Its amazing what you can do with potatoes"

2001-07-09 Thread Michael G Schwern

On Mon, Jul 09, 2001 at 11:00:45PM -0500, David L. Nicol wrote:
> [EMAIL PROTECTED] wrote:
> Why not drop the sigil on things with declared types?

A VERY SHORT DIGRESSION INTO SIGILS

I'm going to say you need sigils for this:

print "Hello, my name is $name\n";

You're going to say this:

print "Hello, my name is $(name)\n";

And then I'm going to say that looks like crap and then 12 other
people are going to join in and then nothing useful will get done.

So in the interest of keeping this thread down to just one massively
controversial topic, let's assume the sigils are going to stay for the
purposes of this discussion.

THIS ENDS THE VERY SHORT DIGRESSION INTO SIGILS.  PLEASE DO NOT START
ARGUING ABOUT THEM IN THIS THREAD.  THANK YOU.


> > I'm pondering this being okay:
> > 
> > my Num$dec = 4.0;
> > my Int$int = $dec;  # Num -> Int okay since 4.0 truncates to 4
> > # with no(?) information lost
> 
> You have lost information.  You have lost one digit of precision.  That is
> not insignificant. Although that information is currently carried in STRING
> types and not in FLOAT types.

Sig figs isn't really the issue here.  You can convert back and forth
between 4, 4.0, '4' and '4.0' all day and still get the same value
(I'm ignoring for the moment the possibility of floating point error,
I don't know how much of an issue it is).

The real problem isn't that.  The real problem is how much this
complicates the implicit typing rules.  I'm going to have to play
around a bit and see which way works best.


> > You'd have to do an explicit typecast (syntax left as an exercise).
> > Given that most times when you try to use a reference as a string
> > you're making a mistake, this shouldn't be a big deal.
> 
> This would be a nice thing to have a pragmata for, what hash refs 
> stringify to.

Why?  Example of use?


> > Now, here's an example of something that might be really annoying to
> > get right.  Let's say localtime() returns a hash in Perl 6 (sensible).

> localtime would return a magic read-only hash reference.


I just picked localtime() as a sufficiently complicated example.  I
don't really want to discuss its interface here.  See sigils above.

The real question:  variables implying their types from function
signatures... is it sane?


> > my $foo;# *error* forgot to declare a type.
> > 
> > We could have Perl go through heroics to try and find $foo's first
> > assignment and imply a type from that, but I think that will rapidly
> > get Messy and Surprising.
> 
>   my $foo;# PMAW

PMAW?


-- 

Michael G. Schwern   <[EMAIL PROTECTED]>http://www.pobox.com/~schwern/
Perl6 Quality Assurance <[EMAIL PROTECTED]>   Kwalitee Is Job One
Realize this, sweetheart, I'm squeezing my poodle.



Re: "Implied types, first try." Or "Its amazing what you can do with potatoes"

2001-07-09 Thread David L. Nicol

[EMAIL PROTECTED] wrote:


First off, I'm going to pound on one of my deceased horses a bit:

Why not drop the sigil on things with declared types?
Then $foo keeps its status as Perl's Magic Autoconverting Wondertype
and without it, we know we aren't dealing with the PMAW and we
won't make mistakes based on thinking that we are.

With it, we still have to remember what types things are declared
as, so it becomes meaningless.

Thanks for listening.



> I'm pondering this being okay:
> 
> my Num$dec = 4.0;
> my Int$int = $dec;  # Num -> Int okay since 4.0 truncates to 4
> # with no(?) information lost

You have lost information.  You have lost one digit of precision.  That is
not
insignificant. Although that information is currently carried in STRING
types and not in FLOAT types.


> You'd have to do an explicit typecast (syntax left as an exercise).
> Given that most times when you try to use a reference as a string
> you're making a mistake, this shouldn't be a big deal.

This would be a nice thing to have a pragmata for, what hash refs 
stringify to.

 
> Now, here's an example of something that might be really annoying to
> get right.  Let's say localtime() returns a hash in Perl 6 (sensible).
> Let's also say that not only does it return the year, mday, hour, min,
> sec, etc... as integers, it also returns things like the name of the
> month, name of the day, etc...
> 
> my %time = localtime;
> # "Today is Monday, July 9, 2001"
> printf "Today is %s, %s %d, %d", $time{qw(dow month mday year)};
> 
> What should the return signature of localtime look like?  Obviously it
> should return a hash, so that much checks out.  But each of the keys
> should be typed as well, String or Num.  It would be really, really
> annoying to have to set up the hash and all its keys just right so it
> can accept the return value from localtime.  Instead, perhaps Perl can
> simply imply its type from the declaration:
> 
> my %time = localtime;   # %time's type declaration is implied by
> #localtime's return signature AT
> #COMPILE TIME!
> %time = gmtime; # ok, localtime and gmtime have the same
> #signature.
> $time{mday}++;  # ok, $time{mday} is an Int
> $time{mday} .= 'foofer' # *run-time error*  Implicit $time{mday}
> #Int -> String cast ok for the string
> #append, but trying to convert the
> #String "10foofer" back to an Int fails.


localtime would return a magic read-only hash reference.  the dow lookup
would be deferred until printf needs it and no sooner.  And if we don't
use %time again for anything else in the block, it doesn't even get
memoized.


> 
> my $foo;# *error* forgot to declare a type.
> 
> We could have Perl go through heroics to try and find $foo's first
> assignment and imply a type from that, but I think that will rapidly
> get Messy and Surprising.

my $foo;# PMAW
my int count;   # an integer (maybe an automatic big-int)

...
count = int($foo);  # redundant, it's implied, unless
# using strict of some kind
...
$foo = count;   # legal too

if you insist that $ only means "there's only one of this" you have
made it completely meaningless.  The scalar sigil should be dropped
from strongly typed variables.



-- 
   David Nicol 816.235.1187
   "Perl was born in downtown Hell!" -- Matt Youell




Re: "Implied types, first try." Or "Its amazing what you can do with potatoes"

2001-07-09 Thread Steve Fink

[EMAIL PROTECTED] wrote:

> Here's me thinking out loud.  I'm thinking about how to avoid alot of
> explicit type casting without entering a maze of twisty typecasing
> rules, all different.
> 
> 
> Imagine we have a typing system where types are allowed to
> automatically cast AS LONG AS NO INFORMATION IS LOST.


User-defined types? Do you tell them what they can accept without loss, 
or just have them in the big dusty Other box?

This really just forms a type hierarchy, where child-of requires 
can-be-losslessly-converted. May as well just create an explicit 
hierarchy, and maybe make it extensible with a strong suggestion that 
any additions follow certain rules.

>   my Int$int = 4; # Int -> Int assignment, ok
>   my String $foo = $int;  # implicit Int -> String cast, ok
> 
>   print $int; # implicit Int -> String cast, ok
>   print $int + $foo;  # $foo is implicitly cast from String -> Int
>   # since no information is lost.
>   # $int + $foo is integer math.
>   # The result is cast to a String.
>   my Num$dec = 4.2;   # Num -> Num assignment, ok
>   $int = $dec;# *error* 4.2 would have to be truncated
>   # to fit into $int.
>   $int = int($dec);   # This is ok.  int takes a Num and returns
>   # an Int.
>   $dec = $int;# implicit Int -> Num cast, ok.


Ok, fine.

> I'm pondering this being okay:
> 
>   my Num$dec = 4.0;
>   my Int$int = $dec;  # Num -> Int okay since 4.0 truncates to 4
>   # with no(?) information lost
> 
> but I think it will complicate my Grand Plan which I'm not ready to
> reveal.


Compile-time or run-time?

my Num $x = 3.0;
$x++;
my Int $y = $x;

Could be compile-time, if you do constant folding first.

Allow me to ramble:

You can think of having a universe of (an infinite number of) 'values'. 
A type is a subset of those values. Things like Int, Num, and String are 
just labels of subsets. So rather than thinking of a subtype (or a 
safely castable type) of another type, think of a subset. I'm not 
originating this, btw. So Int is a subset of Num, and '4' and '4.0' are 
considered to be exactly equivalent.

> Now, obviously there's no need to declare hashes, arrays and scalars
> as being such, we have the sigils.  But references we do.
> 
>   my HASH  $ref = \%hash; # ok
>   my ARRAY $ref = \%hash; # *error*
> 
> HASH, ARRAY, SCALAR, GLOB, etc... references do not cast.


...although in perl6 that's my HASH $ref = %hash, but sure.

>   my SCALAR $ref = \$foo;
>   print $ref; # *error* implicit SCALAR -> String cast
>   #illegal.
> 
> You'd have to do an explicit typecast (syntax left as an exercise).
> Given that most times when you try to use a reference as a string
> you're making a mistake, this shouldn't be a big deal.


my SCALAR $ref is stringify_sub(&f) = \$foo

currently known as

use overload '"' => \$f

> 
> For arrays, its probably enough that the whole array be of a single type:
> 
>   my Int @array = (1..10);# array of integers
>   $array[20] = 4.2;   # *error*, Nums not allowed.
> 
> provides some nice optimizations.  You can do the same for hashes:
> 
>   my Int %hash = (foo => 1, bar => 2);# hash of integers
>   $hash{baz} = 'yarrow';  # *error* Strings not allowed
> 
> but we like to mix types up in hashes.  One way we can allow this is
> to have a generic type, Value (or perhaps Scalar) that every type will
> cast into.  Hash keys would be implicitly of type Value.
> 
>   my Value %hash = (foo => 1, bar => 2, baz => 'yarrow');
> 
> but that's pretty much the same as switching off strong typing.  So
> obviously hashes have to have per-key types.  I will leave the syntax
> of that as an exercise for the reader.  It would have to make it easy
> to declare lists of keys as being a single type.  If we come up with
> something nice here, it can probably be backported onto arrays.


It's only switching off strong types for that variable, so it's a 
valuable thing to do.

my qt(String => DontEvenThinkAboutIt) %tree = (id => 1);
$tree{parent} = \%tree;

(or if you think you can tackle that)

$tree{parent_with_distance} = [ 0, \%tree ];

a map from strings to numbers and lists of two elements, where the first 
element is a number and the second is a reference to a map from strings 
to numbers and lists of two elements...

> 
> 
> Regexes pose an interesting problem.  m// and s/// would take a
> String.  Num and Int can all autocast to a string fine, but
> consider...
> 
>   my Int $foo = 42;
>   $foo =~ s/\d+/Basset Hounds/;   # *run-time error*
> 
> Sure, $foo will cast to a String and be operated on, but then the
> regex changes it to 'Basset Hounds' and t

"Implied types, first try." Or "Its amazing what you can do with potatoes"

2001-07-09 Thread schwern

Here's me thinking out loud.  I'm thinking about how to avoid alot of
explicit type casting without entering a maze of twisty typecasing
rules, all different.


Imagine we have a typing system where types are allowed to
automatically cast AS LONG AS NO INFORMATION IS LOST.

So let's start with something simple.  Int, Num [1], String.

All Ints can auto-cast to Numbers, no information lost.

All Ints and Numbers can auto-cast to Strings, again nothing is lost.
This is similar to how Perl works right now.

Strings can only auto-cast to Integers if the string looks like an integer.
Ditto String->Float.  Again, similar to how Perl works right now.
Obviously this will be a run-time type check.

So:

my Int$int = 4; # Int -> Int assignment, ok
my String $foo = $int;  # implicit Int -> String cast, ok

print $int; # implicit Int -> String cast, ok
print $int + $foo;  # $foo is implicitly cast from String -> Int
# since no information is lost.
# $int + $foo is integer math.
# The result is cast to a String.
my Num$dec = 4.2;   # Num -> Num assignment, ok
$int = $dec;# *error* 4.2 would have to be truncated
# to fit into $int.
$int = int($dec);   # This is ok.  int takes a Num and returns
# an Int.
$dec = $int;# implicit Int -> Num cast, ok.

About the only thing remotely radical there is that Num -> Int won't
work.  The key is, never lose information.

I'm pondering this being okay:

my Num$dec = 4.0;
my Int$int = $dec;  # Num -> Int okay since 4.0 truncates to 4
# with no(?) information lost

but I think it will complicate my Grand Plan which I'm not ready to
reveal.


Now, obviously there's no need to declare hashes, arrays and scalars
as being such, we have the sigils.  But references we do.

my HASH  $ref = \%hash; # ok
my ARRAY $ref = \%hash; # *error*

HASH, ARRAY, SCALAR, GLOB, etc... references do not cast.

my SCALAR $ref = \$foo;
print $ref; # *error* implicit SCALAR -> String cast
#illegal.

You'd have to do an explicit typecast (syntax left as an exercise).
Given that most times when you try to use a reference as a string
you're making a mistake, this shouldn't be a big deal.

For arrays, its probably enough that the whole array be of a single type:

my Int @array = (1..10);# array of integers
$array[20] = 4.2;   # *error*, Nums not allowed.

provides some nice optimizations.  You can do the same for hashes:

my Int %hash = (foo => 1, bar => 2);# hash of integers
$hash{baz} = 'yarrow';  # *error* Strings not allowed

but we like to mix types up in hashes.  One way we can allow this is
to have a generic type, Value (or perhaps Scalar) that every type will
cast into.  Hash keys would be implicitly of type Value.

my Value %hash = (foo => 1, bar => 2, baz => 'yarrow');

but that's pretty much the same as switching off strong typing.  So
obviously hashes have to have per-key types.  I will leave the syntax
of that as an exercise for the reader.  It would have to make it easy
to declare lists of keys as being a single type.  If we come up with
something nice here, it can probably be backported onto arrays.


Regexes pose an interesting problem.  m// and s/// would take a
String.  Num and Int can all autocast to a string fine, but
consider...

my Int $foo = 42;
$foo =~ s/\d+/Basset Hounds/;   # *run-time error*

Sure, $foo will cast to a String and be operated on, but then the
regex changes it to 'Basset Hounds' and tries to put that back into
$foo.  *BANG* Run time type error.  I think that's the best we can do
with regexes.  Perhaps there could be an option for the typing system
which says "s/// only takes Strings and does not cast" turning the
above into a compile-time error.

At least it does prevent some silly errors:

my HASH $ref = \%hash;
$ref =~ m/.../; # *error*


Now, here's an example of something that might be really annoying to
get right.  Let's say localtime() returns a hash in Perl 6 (sensible).
Let's also say that not only does it return the year, mday, hour, min,
sec, etc... as integers, it also returns things like the name of the
month, name of the day, etc...

my %time = localtime;
# "Today is Monday, July 9, 2001"
printf "Today is %s, %s %d, %d", $time{qw(dow month mday year)};

What should the return signature of localtime look like?  Obviously it
should return a hash, so that much checks out.  But each of the keys
should be typed as well, String or Num.  It would be really, really
annoying to have to set up the