HaloO,

Autrijus Tang wrote:
So I'm starting to write the inferencer.  Immediately I encounter the
problem that every type can potentially contain "undef":

This is the reason why I've bounded Undef below Item disjoint with
Value in my type lattice. Of course all types are applicable as
type parameters. How implicit that is or if an explicit

  class IO does Undef[IO] {...}

is needed, I can't decide. And I just realised today that decisions
are made in places and at times where I am not. And they are not
announced here either. But that belongs to the cabal thread. Sorry
that I mention it here.


    my IO $x = open('/etc/passwd');
    $x = undef;

Hmm, that should dispatch to &infix{'='}:( IO, Undef of IO )
which is a type error if Undef of IO doesn't exist or pragmas
or some such prevent its auto-generation. A nice implementor
of IO would be so kind to close the file immediately in this
method.


    $x.close;

This raises the runtime error:

    *** Can't call method "close" on an undefined value.

The dispatch should go to &close:( Undef of IO --> Void ) and again
the nice implementor of IO.does(Undef of IO) would provide a noop
implementation. A careless implementor however might leave his
users with the above error ;)


The "undef" literal can defeat the type checker freely:

    my Int @x := undef;
    say @x.end; # boom, undefined list generator, per S04

I understand $Larry had rejected RFC192 "Undef Values ne Value", on the
ground that "undef" can now carry more interesting information.

Yes, but I think this should now be read as !Value.does(Undef) and
Item.does(Undef).



However, sometime it is better if we can have a clear boundary so
the dreaded NullPointerException can be raised as early as possible.

I fully agree. Especially when it hits innocent users.


This is certainly too static for Perl6.  However, it would be nice to
be at least able to declare a variable so that all undef reaching it
will instantly explode.  So I propose the "is defined" trait:

I would leave that up to the type information stored at ::IO.
The user has to know what it does anyhow. And if it is a Value
subtype it'll instantly explode even on

  my IO $x; # boom unless IO provides default value, but how?

If the inclined user intends to handle Undef even when
the implementor of IO didn't this is how:

  my IO|Undef[IO] $x; # proper supertype
  my IO^Undef[IO] $y; # decisive, this is easier for the type checker

    # must be assigned before use
    # if open() returns undef, die right there.

Even worse or better depending on your perspective or stricture
in effect. If IO.does(Value) and &open:( Str --> IO^Undef )
then

    my IO $x is defined = open('/etc/passwd');

Doesn't even compile. Not so if you take the Widera approach
where the intersection of the return type of &open and the
type constraint of $x is non-empty because it contains IO.
But if your approach it the other way round, the potential error
set isn't empty either. This is what I got from the
(Book of Widera).does(Paper) so far. Well, and the fact that
he calls things Frame that are elsewhere known as Record or
Dictionary.

OTOH, the implementor of &open could choose to guarantee
a return type of IO given an arbitrary Str. In the event
that there is no file of that name the returned object
is a proper IO but e.g. immediately returns EOF when read
or responds with false to .defined calls. But this is not
a problem of the type system but of interface design. The
type system is there to enforce the designer's decisions
or at least point out mismatches to actual use.


    $x = undef;     # this fails at compile time

For the compiler, $x is compiled to the real "IO" type, while the
ordinary "my IO $x" is compiled to "Either Error IO" type.

Furthermore, it would be nice to tell the inferencer to infer the type
of a certain variable, instead of having to either write them out by
hand, or accepting their fate as Any.  I'd like to see "is typed":

I don't understand the purpose of that other than explicitly switching
the inferencer on a per variable basis. Should that mean that a variable
takes on a constraint as specific as the inferencer can statically
determine? I don't see the point of it, sorry.


    {
        my $x is typed; # Infer $x's type (Str) for me
        $x = "Hello";
        print $x;
    }

The optimizer might here just end up with { print "Hello"; }
and drop $x completely. Or is that anti-social to some
class sitting behind $-sigiled variables?


Finally, it would get tedious to write them out by hand.  So a lexical
"traits" pragma may help:

    {
        # Entering the realm of referential transparency...
        use traits < defined typed constant >;
        my $x;  # automagically receives the three traits

        {
            # Falls back to the dynamic world...
            no traits < typed constant >;
            my $y;
        }
    }

Does this sound sane?

To me it sounds more superfluous. What distinguishes
'referential transparency' from 'the dynamic world'?
Do you mean pre-calculated dispatch targets?
--
$TSa.greeting := "HaloO"; # mind the echo!

Reply via email to