On Thu, Dec 11, 2003 at 02:48:06PM +0100, Stéphane Payrard wrote:
: Hi,
: 
: I don't remember anything about enums and bitenums in the
: apocalypses. This is probably not very difficult to roll out
: something using macros but I feel that should belong to the
: standard language.

[Warning: speculation ahead.]

I've been thinking that enums might just be subtypes of roles/properties.
After all, when you say

    0 but true

it might really mean

    0 but Boolean[1]

whereas

    1 but false

means

    1 but Boolean[0]

That is, type boolean can be thought of as enum(false,true).

So we might have a role declaration somewhere that says something like:

    role *Boolean[Bit ?$val] does Property {
        bit $.boolean = $val;
    }
    role *false does Boolean[0];
    role *true does Boolean[1];

That's what the semantics might look like, but of course people would
probably want an enum or bitenum wrapper macro for real code.  No reason
it couldn't be a standard macro though.  Or even part of the grammar--we've
never shied away from defining one construct in terms of another for
teaching purposes.  We define C<use> in terms of C<BEGIN> and C<require>,
and C<require> in terms of C<do>.  Perl 6 will do more of that, because
it makes things easy to learn.

A lot of this is negotiable, but I think it's a feature that you
can't have an enum without it being associated with a unique type
name/property.  It's also appealing to me that enum "values" live
in the type namespace, since they're really subtypes (that is, types
that restrict values ranges of "real" types).  There's nothing says
a subtype has to have only one value:

    role Bit[Int ?$val] does Int[0|1];

Of course, I'm handwaving a lot here by using a junction.  For
integer subtypes you'd typically want a range:

    role Byte[Int ?$val] does Int[0..255];

That implies that the argument to Int[] has to be able to take a range.
I confess I don't know what the real declaration of "Int" looks like
yet.  Certainly the first arg isn't simply an "Int".  That only works
for enums. The arg has to represent some kind of generic constraint.
Pity the optimizer...

Anyway, this all implies that use of a role as a method name defaults to
returning whether the type in question matches the subtype.  That is,
when you say

    $foo.true

it's asking whether the Boolean property fulfills the true constraint.
When you say

    $bar.red

it's asking whether the Color property fulfills the red constraint.
I suppose when you say

    $baz.Byte

it's asking whether the Int property fulfills the Byte constraint, but
that's getting kind of strange.

Another implication is that, if properties are subtypes, we can't use
the same name as a cast method.  Since

    $baz.Byte

only returns true or false, we'd need something like (yuck)

    $baz.asByte

Since class names are valid as bare names, perhaps we can improve
that to

    $baz.as(Byte)

(That doesn't mean you have to write an "as" method that contains a
switch.  More likely "as" is a multi method within the class of $baz.)

Another possibility is that .Byte would have to be context sensitive.
In which case we have the small problem that

    if $foo.boolean { print "true" } else { print "false" }

prints "true" even if $foo is 0.  You'd have to say

    if +$foo.boolean { print "true" } else { print "false" }

to force a numeric context on the type.  (Or use $foo.true.)

Alternately, we use the type name as a cast always, and distinguish
the boolean test:

    if $baz.does(Byte) { my $byte = $baz.Byte }

I kinda like that.

Since "does" is a generalization of "isa", we probably need to generalize
smart matching to test "does" rather than "isa" for class/type names.

On the other hand, it'd be a major pain to have to write $foo.does(true).
I suppose a cast could return undef if it fails, which means that
$foo.true would return 1 or undef.  Except $foo.false would return 0 or
undef.  Urg.

I suspect the right solution from a linguistic point of view is to make it 
context sensitive but also make it easy to disambiguate.

Explicitly:

    $bar.does(Color)    # does $bar know how to be a Color?
    $bar.as(Color)      # always cast to Color

Implicitly boolean:

    $bar ~~ Color       # $bar.does(Color)
    ?$bar.Color         # $bar.does(Color)
    if $bar.Color       # if $bar.does(Color)

Implicitly non-boolean:

    +$bar.Color         # +$bar.as(Color)
    ~$bar.Color         # ~$bar.as(Color)
    $($bar.Color)       # $($bar.as(Color))
    @($bar.Color)       # @($bar.as(Color))

Then $foo.true and $foo.false work as expected.  Still have to be careful
about ?$foo.boolean though.

Interestingly, we have

    $foo = $bar.Color || 0xfff;

that is equivalent to

    $foo = $bar.does(Color) ?? $bar.as(Color) :: 0xfff;

which means $foo could validly end up 0.  Of course, // gets there by
a different route:

    $foo = $bar.Color // 0xfff;

means

    $foo = defined($bar.as(Color)) ?? $bar.as(Color) :: 0xfff;

I think I'm happy with that.  Maybe.

Larry

Reply via email to