John M. Dlugosz wrote:
In your example above, @a is a read-only container, so the assignment would fail and this should be noticed at compile time.

Fully agree.

Now what if the parameter was declared as ref? The compiler gets passed the body of store17 and continues to the call. The default assumption is that @s will not match the signature.

Agreement here as well.

In Perl 6, it's not that simple. We can move between typed and untyped code, at the very least. Typing does require run-time enforcement.

I was under the impression that untyped just means that you use the
ultimately wide type Any. The interfacing of narrower types to that
should be handled properly. This is necessary to protect the intent
behind using a narrow type in the first place. So the scope of typing
support must not go into the direction to allow the untyped code things
that the programmer wanted to prevent with the type in the first place.

Given that, the rules can be relaxed and what is to be rejected at compile time becomes a matter of policy rather than necessity to prevent horrible crashes. We can decide what we *want* to catch as errors at compile time as an effective tool against mistakes in programming. Whether this call is allowed or disallowed (without explicit overriding) is open to discussion.

Oh yeah. These discussions always end with defining some pragma. So
which one is it for type protection and what is the default?

What about this related example?

 sub store17b (@a is ref)     # no type constraint
     @a[17] = Animal.new; # assignment error?
 my Dog @dog;
 store17b(@s); # bind error?
 @dog[17].bark(); # dispatch error

There is nothing the compiler can say about the assignment when compiling store17b. Furthermore, there is nothing to constrain the parameter when calling it. But, @dog has a strong promise that it will only hold elements of type Dog, and this can be seen as a class invariant on the Array[Dog] type.

My opinion here is to disallow the call. Note that this is a feature
of using the Dog type on @dog *not* a restriction put onto store17b.
As such the restriction is lexically scoped the same as @dog. I consider
this a good thing. But note that this forces e.g. the mutating sort
method into being parametric with respect to the type of the array

> So the assignment to @a[17] must fail, and this can only be found at
> run time, and may vary from call to call.

If the code is allowed to call store17b at all, of course. I would
opt for signaling the error there.

Meanwhile, how can a RO-Proxy for Array work? For Scalar it is simple: just block the STORE. The Array, though, just returns a delayed binding object when subscripted. Rather than blocking the subscripting, it must modify the return value from it! The binding object has the LValue role. So you can stick a RO Item Proxy in front of that. That's inefficient, so I think that the possibility should be built into the underlying subscripting function. The proxy then just adds an adverb and passes the call through.

I don't fully understand who provides the proxy for the array. Does
that get created when a sub call is bound? How does the proxy interact
with the dispatcher? Does the dispatched to target see the readonlyness
of the array? Or is that stored in the proxy that is not handed down?
I assume you mean the implementation of postcircumfix:<[ ]> is not
seeing the proxy and just creates a rw LValue all the time. Then the
proxy is given this after the call and copies the readonly flag from
the array. Right?

But, it means that postcircumfix:<[ ]> can't simply be tagged as an accessor or mutator. You can't just block it as falling into the latter category. Rather, its semantics must be understood and a suitable wrapper created. That can't be done automatically in the fully general case.

I think it can be embedded into the type analyzer. The return type of
postcircumfix:<[ ]> is conditionalized on the readonlyness of the array
parameter. The only question is how this dependency is written. I
proposed that the readonly trait has a parameter where the value
comes from. The problem with that approach is that it is nicely human
readable and easily implemented as far as the setting of the trait on
the return values goes. But the extraction of the readonlyness as a
static property is difficult because type capturing into ::T is a
generic mechanism. Now the compiler needs to perform a semantic analysis
of what this T is used for. Here as a parameter to the readonly trait.
IOW, I want a more declarative style. BTW, one could also use the
variable directly without a type capture:

   multi method postcircumfix:<[ ]>
      (Positional $a: Int $i --> LValue is readonly($a))

But again the compiler has to dig into the semantics. It isn't fully
driven by syntax. But perhaps this is not a big deal. But at least
it means that the readonly trait is not fully generic. It is explicitly
recognized by the compiler and stored in its static information about
the code.

BTW, note that I've used $a there because 'Positional @a' would
mean an array of items that each do the Positional role. Is this

But, the previous paragraph suggests a protocol that could be followed. A third category, delayed, would imply that the method takes that standard adverb. So the automatic RO Proxy would block mutators, pass through accessors, and insert the adverb but otherwise pass through 'delayed'. Hmm, that needs to have a name that's the right part of speech. But you get the idea.

I don't think we need this third mode. postcircumfix:<[ ]> would be
classified as accessor. Only the usage of the returned value needs
to be restricted for a readonly array. Just as in @a[1].foo the call
of postcircumfix:<[ ]> has already happened when .foo is dispatched,
the same applies in @a[1] = 42 where postcircumfix:<[ ]> has nothing
to do with the assignment anymore. That is, it is a misconception to
regard that as an assignment to the array, even though I admit it
strongly looks as such ;) I would say it's an assignment into the
array by means of an LValue that happens to know the array.

On the same token binding goes into the array not to the array.
That is $x := @a[1] binds $x to the returned LValue and subsequent
assignment to $x hits the array as expected. But not via
postcircumfix:<[ ]>. That is past at assignment time. Which is why
you want to call it delayed. But I don't see that as a property
of the index operator.

Note that I assume that there are readonly LValues! Your code snippet

$param = $result if VAR($param) ~~ LValue; # update caller, if
                                               # possible.

wouldn't work. But readonly is a role so you just write

   $param = $result unless VAR($param) ~~ readonly;

which also reads better. Or without negation

   $param = $result if VAR($param) ~~ rw;

The LValue type has to have a generic conversion routine to the
rvalue type. That is the signature of the index operator has to
be a bit more complicated:

   multi method postcircumfix:<[ ]>
      (Positional[::T] $a: Int $i --> LValue[T] is readonly($a))

and then something along the lines of

   role LValue[::T]
      method prefix:<T> ($self: --> T) # or is this a submethod?

Or how are these type conversion routines declared? The above
form can hardly work because the T in prefix:<T> is just a String
and not the type parameter of the role. Would prefix:«::T» work
by interpolating the type parameter? But :: is not recognized as
special in interpolation. Then perhaps prefix:«{T}»? Note that
S14 just has methods with name T but how does that work at all.
Note that a type cast is written as a prefix call and the sub
dispatcher is not failing over to method dispatch anymore.

Regards, TSa.

"The unavoidable price of reliability is simplicity" -- C.A.R. Hoare
"Simplicity does not precede complexity, but follows it." -- A.J. Perlis
1 + 2 + 3 + 4 + ... = -1/12  -- Srinivasa Ramanujan

Reply via email to