TSa Thomas.Sandlass-at-barco.com |Perl 6| wrote:
sub incr (Any $x is rw)
{
if $x.VAR.WHAT ~~ Str {...} # "-100" -> "-101"
else {...} # "-100" -> "-99"
}
This doesn't work because $x.VAR accesses the inner container and
that has constraint Any which effectively is 'where {True}'. So,
the natural solution to get at the constraint of the container the
caller uses to keep the argument value is capturing it:
Huh?
if you call
$q = "aaa";
incr($q);
then the value passed in is a Str. The static type is Any, the dynamic
type is Str.
$x.VAR.WHAT will be the prototype object for Scalar, which has nothing
to do with the value inside it.
$x.WHAT is the Str prototype, the same thing Str as an argument listop
returns. So
if $x.WHAT ~~ Str
will be True.
sub incr (Any ::Type $x is rw)
{
if Type ~~ Str {...} # "-100" -> "-101"
else {...} # "-100" -> "-99"
}
Same thing, only trusting smartmatch to compare a protoobject with a
metaobject.
BTW, calling incr with an rvalue can be handled as well
sub incr (Any ::Type $x is rw)
{
my $ret;
if Type ~~ Str {...; $ret = ...}
else {...; $ret = ...}
::T $x = $ret if T ~~ Type; # don't violate caller's constraint
return $ret;
}
T is undefined.
say incr(5); # print 6, no error
Compile-time error; can't bind 5 to an rw.
my int8 $i = 5;
incr($i); say $i; # prints 6
$i = 127; incr($i); say $i; # prints 127, no error
I wonder if the constraint check can be written as I did. A version
with explicit WHAT is defined to have a bad smell, though. I believe
capturing the caller's container type is implicitly done by the
compiler to check constraint violation of rw parameters.
Why not just let MMD do it? Switching on the type is bad smell.
Polymorphism should do that.
multi sub incr (Str $x is rw --> Str) { ... } # do the string
version.
multi sub incr (Numeric ::T $x is rw --> T)
{
$x += 1;
return $x;
}
I'm postulating the existence of a Numeric role that will constrain T to
Int, Num, Rat, etc. and ignoring any progressive details concerning
F-bounds.
multi sub incr (Enum ::T $x is rw --> T) { ... }
Again, should match anything that is created as an enumeration, ignoring
progressive details about bases that are not supertypes because of
variance violations in the derived class. But assume there will be a
way to say "any kind of Enum" with higher-order matching. But I'm
getting ahead of myself. The body can be written without explicit
testing against types, just using the generics to get to the real
enumeration type with its interrogation methods.
Here's one riddle for the linguists. We have '::Any ::= where {True}'
as the constraint that permits any value. Would then '::All ::= where
{False}' work as expected? That is does it intuitively mean that it
forbids all values?
I've not plumbed the depths, but I naivly expect that the constraint is
evaluated when assignment is made. If it returns False, the assignment
fails. You always return False, so no value will ever be assigned
without error, or no value will ever match if used in MMD.
However I've no clever idea how to denote a contra-variant constraint
on the caller's container's constraint in an rw sub. Perhaps
sub foo (FooOut <-- FooIn $x is rw) {...}
The arrow is of course optional. Its absence means that the caller has
to provide an unconstraint container to be type save. The visual problem
with this arrow is that is cousin --> is not per parameter but for the
whole sub. Alternatively it could be a signature on the trait.
I think I see what you're getting at. But I don't see what it has to do
with the container. The value bound to $x needs to be at least as
general as FooIn. Hmm, or are you thinking of subset constrained
containers trying to bind to $x? Currently, there is no mechanism to
propagate the constraint in. If the binding chooses to work by
"copy/return" mechanism, then it will be checked when the value is
copied back to the original container, using that container's SET,
assuming copy/return is designed and intended to reference the original
container not just the differently-typed value. I can see the need for
that, such as with tied variables in general. Or maybe the container
for $x aliases the original container -- that might be what "is ref" is
supposed to mean.