> -----Original Message-----
> From: Larry Wall [mailto:[EMAIL PROTECTED]
> Sent: Thursday, December 11, 2003 1:04 PM

> [Warning: speculation ahead.]

Noted.

> 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.


Supertypes, eh? An enum is a limited-range thingy (int, usually), which fits
the description of a role, but also has magic words involved.

  package RGB;

  enum Color does Int[0..0xFF_FF_FF] {
    has $Red   = 0xFF_00_00;
    has $Green = 0x00_FF_00;
    has $Blue  = 0x00_00_FF;
  }

Since the possible values are "possibles", I don't like C<has> unless we
provide that C<has> means "possible value" in an enum-decl.

Is C<enum> a primitive type only?

Or can we enumerate objects? As in,

  class RGB {
    has byte $.red;
    has byte $.green;
    has byte $.blue;
  }

  enum Color does RGB {
    has $Red    = RGB[red => 0xFF, blue => 0x00, green => 0x00];
    has $Green  = RGB[red => 0xFF, blue => 0x00, green => 0x00];  # Squares
mean compile time (const)?
    has $Blue   = RGB[red => 0xFF, blue => 0x00, green => 0x00];
  }


> 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];

I don't understand the purpose of your [Int ?$val] "parameter". Doesn't the
C<does Int[0..255]> declare the constraint entirely?

   role PHB does Employee[$.job_title == "manager" && $.experience > 5];



> 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...

  macro does is parsed(/ <class> [ <given_block ] /) {...}

> 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.

This is bad. Better to treat a "role" as a matchable constraint, and ask:

  if $foo ~~ true {...}

  given $bar { when red {...}}

  unless $baz ~~ Byte {...}

We've already got a constraint matching syntax. But for cases where you want
to be explicit, or when you risk confusion using ~~, we can recycle C<does>
somehow:


  if $foo.does(true)
or
  if $foo.can(true)
or
  if $foo.would(true)
or

   role ChuckWood does WoodChuck[does &.chuck(Wood)];   # Internal C<does>
for has-method-p?

   my $how_much is Quantity of Wood = $woodchuck.can_chuck if
$woodchuck.could(ChuckWood)

(Sorry)

> 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.)

Ugh.

UghUgh.

If a role is just a constraint, then there's no need to have a method at
all -- the type doesn't change.

  role FitsInByte does Int[0..255];

  my Byte $b;
  my Int  $i;

  $b = $i if $i ~~ FitsInByte;

Which leaves the .as() method for conversions:

  role ClassByte does Class[Byte];
  multi method Int::as(Int $i: ClassByte) returns Byte { $i % 256; }

  my Byte $b = $i.as(Byte);

(BTW: Is this use of roles to select a [range of] value for a parameter,
considered okay?)

> 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.)

Or use C<if $foo> or C<if +$foo>.

> 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.

Or the other way around.

But if roles can be extended to nontrivial classes, like RGB above:

  role Color does [has $.red and has $.green and has $.blue];

That implies either that doing

   my $pixel = $rainbow.Color;

will somehow figure out the right way to extract the red, green, and blue
components from $rainbow, and bundle them up for $pixel. That doesn't seem
hard until someone adds a constraint that references another member in
passing that SHOULDN'T be part of the conversion:

  role Color does [has $.red and has $.green and has $.blue and $.gamma !=
0];

Autogeneration of conversion methods sounds cool (and the PL/I meter goes
++) but it makes me awfully nervous about the AAD/PLS (*) risks.

> 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.

Smartmatch.

=Austin

(*) Action At a Distance / Principle of Least Surprise

Reply via email to