On Wed, Mar 23, 2005 at 11:08:17PM +0200, Yuval Kogman wrote:
: On Wed, Mar 23, 2005 at 11:53:06 -0800, Larry Wall wrote:
: > This seems a little backwards--I think all positionals should be bound
: > before you start binding named pairs, if currying is to be consistent with
: > "ordinary" binding.
: 
: Currying and ordinary binding are implemented in the same
: function... =P

I think that's good, though it's presumably possible for the function
in question to know whether it's the final binding, and make
allowances for the difference of intent.  Or perhaps there could
be options to .assuming, though we'd have to change the signature.
Probably should do that anyway, or we'll never be able to have a
variant of .assuming that can take options.  So I suspect the first
two positional arguments to .assuming should be a [...] list of
positionals followed by a [...] list of pairs.  Then we're free to
have options as the 3rd and subsequent parameters.  Alternately we
just pass one [...] list as the first argument, which we parse as an
ordinary argument list with a transition from positionals to named.
That makes .assuming and binding more like the same operation, modulo
the extra indirection in the first argument.

I suppose one could keep the current syntax if you distinguish the
case of the first positional argument being an anonymous array, in
which case if you wanted to bind an array as the first assumed argument,
you'd have to double bracket it.  I can argue it both ways.

: > Otherwise you can't catch binding explicitly to
: > a positional Pair argument.
: 
: soo... how would you diambiguate that in the general case?

In the current formulation it's disambiguated by whether the positional
argument is explicitly declared as Pair, though we could possibly
relax that to any type that "does" Pair, presuming it's not a role
that is going to sneak into a lot of types.  If it does sneak into
a lot of types, then we'd probably better keep it as an explicit Pair
declaration.

: Can I pass a named for an optional?

Certainly, as long as the optional isn't declared Pair, and as long as
you've made the transition from processing positional arguments to
named arguments, and don't expect to go back to positional processing.

: or is that always a positionally bound pair?

It is positionally bound if declared Pair.  Otherwise a pair is always
taken to be the transition from positional to named processing, and
that and any subsequent optional positionals are immediatly bound to
their defaults in the final binding.  Here is where it breaks down
if you're trying to use the exact same function for currying, since
currying shouldn't force the defaults to resolve.  Either your function
has to distinguish the cases, or the actual binding of defaults has
to be an additional step between your function and the actual call.

Another difference is potentially the binding of slurpies.  I'm not sure
it's practical to do a partial binding of a slurpy array.  The slurpy
array is really a kind of named binding underneath, so it seems kind
of all or nothing, and we could go as far as to require named notation
to bind a slurpy argument with .assuming.  Maybe that's overkill, but
we probably need to do something to avoid the situation of trying to
direct two different pipes into the function, unless we can come up
with a clean and efficient semantics for the curried pipe to be exhausted
before the user-supplied pipe is read from.

: I'm reading through S06 a bit more in detail, and I see
: 
:       Arguments destined for positional parameters must come before
:       those bound to named parameters.
: 
: Do pairs get slurped until there are no more required positionals to
: fill in? In that case, how do you actually assign by name? Can you
: mix positional and named params?

Step A: For each positional parameter, if the next supplied argument is:

    1) a non-pair
    2) a pair, and this parameter is explicitly declared Pair, or
    3) a hash, and this parameter is declared Hash, either explicitly,
       or implicitly with a % sigil,

then bind positionally.  Otherwise, you're done with positional processing.

Step B: Now we do named processing. If there are no pairs or hashes
as the next supplied argument, skip to Step C.  For each supplied
pair or hash, bind arguments by name, but bind positionals only if
they weren't already bound in Step A.  If you find a pair name that
doesn't correspond to a parameter, it's an error for a sub unless
it declares *%.  For subs and methods with explicitly declared *%
and methods with implied *%, shove unbound arg into *%.  (It would
be good if we can optimize away the "shove" by reusing the existing
argument structure, and we trying to take that into account in
allowing duplicates in *% for otherwise bound parameters).  If Step B
bound an argument to the slurpy array, skip Step C.

Step C:  By definition, the next available argument is not a pair
or hash.  (Or if it is, it was protected by a pipe operator or a named
binding to the slurpy array.)  The rest of the arguments are treated
as a potentially infinite list, so we don't want to look for the end
of it to bind anything.  We do analyze and/or evaluate the lazy list
sufficiently to bind any unbound slurpy scalars, and then the rest
of the list is bound to the slurpy array.

Note, the adverbial :{...} is defined as a named binding to the first
*& parameter (or first *$ parameter if there isn't a slurpy *&), so
it's already bound by Step C, even if it occurred later syntactically.
That is, adverbs are inserted at the end of named processing.  If you say

    $object.method($p1,$p2,$p3, :n1($n1), :n2($n2) <== @pipe) :{...}

the adverbial block is treated as if you'd passed it as an explicit pair
at the end of the pair list:

    $object.method($p1,$p2,$p3, :n1($n1), :n2($n2), :{...} <== @pipe)

: Going down further, I see:
: 
:       sub formalize($text, +$case, +$justify)
: 
: This can we name text => "foo"? or will $text actually contain that
: pair, as bound positionally?

It gets bound by name in Step B, since $text is not declared Pair.

: > I also think doing it the other way leads to weird situations
: > where the first two positional arguments can bind to, say, the
: > first and third formal parameters, which is a situation I'm trying
: > to avoid.
: 
: Yeah, that's why I raised this stuff here. Once I managed to
: actually read Autrijus's code (not his fault!) I got thinking, and
: wasn't sure the way it was done is the good way, since it seemed to
: work, but was not really strechable to corner cases.

The overriding design question is which of those corner cases we
want to define vs which cases we want to outlaw and relegate to
user-supplied parsing of of @_ (or whatever).  My inclination as
indicated in the current design is to outlaw any "relative positionals"
and make people parse their own list for those if they want them, in
which case they'll be no worse off than they are right now in Perl 5.

: > But it's possible that .assuming() should be allowed to violate this,
: > if we let it do positionals at all.  It would be a convenience to
: > allow positionals, but for a known function, you can get the same
: > behavior with named arguments.
: 
: Since we have .arity, why don't we make this a little more
: introspectable too?

Sure, but introspection can get kind of slow if you inflict it on the
common case at run time.  I suspect that when someone calls .assuming
we can presume that the one call to .assuming will be amortized
over a number of calls to the derived function, whether it occurs
at compile or run time.  (Though compile time is obviously better,
since you probably also amortize over multiple runs of the program.)

: > :   foo(x => 1, x => 2); # yields $x = 1, %_<x> = 2
: > 
: > I think that should probably be an error for a sub because it doesn't
: > have a signature that enables %_.
: 
: And if it had a slurpy hash?

Then it works like your comment says.

: > :   foo(1, x => 2); # $x = 2, @_[0] = 1
: > 
: > There's where your scheme comes out backwards to me.  I think it should
: > absolutely bind the 1 to $x, and then the x turns out to be extra, an
: > error in the case of a sub, and put into %_ in the case of a method.
: > But we can't have the situation of a positional argument being bound
: > to anything but the next positional parameter.
: 
: So again, how do we unambiguously tell apart a positional from a
: named, when it's a pair? What's not yet bound? where we are in the
: binding?

If it's explicitly declared Pair, and we're in Step A.

: I think some concrete examples would be very good for me.
: 
: > :   (&foo.assuming(1))(x => 2); # $x = 1, %_<x> = 1
: > 
: > Would also be illegal for a sub, and put x=>2 into %_ for methods.
: 
: Ok. that's the consistency i want to attain. By that I mean:
: 
:       foo( .... )
: 
: is the same as
: 
:       (&foo.assuming .).(...)
:                      ..  ..
: 
:       (&foo.assuming(.).assuming(.).assuming(.)).(.)
: 
: etc, given any syntax in '.' that stays the same.

As I say, you might have to write those

    &foo.assuming([.])(...)
    ...
    &foo.assuming([.]).assuming([.]).assuming([.])(.)

to allow for the possibility of passing options to .assuming.  Doubtless
some wag will define subscripting on &foo as a shorthand so you can say

    &foo[.](...)
    ...
    &foo[.][.][.](.)

instead.  As long as we keep the return type of .assuming([])/.[] distinct
from the return type of .(), I don't mind.  (Though I'm probably forgetting
some other use that's already been postulated for &foo.[]...)  It's the
langauges that confusticate function types with final return types that
I find a little too currifying.

: > But more than anything I want to be able to curry a module or class.
: > And that's probably almost exclusively a named-argument thing,
: > unless you just happen to define every sub in your module to have a
: > consistent first argument type.  But maybe, as with partial binding,
: > it's just sort of a partial dispatch, so say, if you curry a class with
: > an object as the first argument, it would curry the instance methods
: > but not the class methods.  This seems pretty dwimmy to a human being.
: > (Or it could curry the class methods too if an object is allowed to
: > play the role of its class.)
: 
: I sort of agree with that, but i'm not sure... what if a named param
: is missing? is it slurped? if we go that far, perhaps positionals
: are the same?

I suspect the first pass at it should simply curry only those functions
or methods that have a parameter matching the pair name exactly, and
leave everything else unchanged.  It think it's much more likely that
a module or class will have named all like parameters similarly than it
will have place like parameters in the same position.  Maybe positional
binding of modules is exactly the sort of thing that should be enabled
by option, but not by default.

Larry

Reply via email to