Currying positionals

2 views
Skip to first unread message

Yuval Kogman

unread,
Mar 23, 2005, 10:43:52 AM3/23/05
to perl6-l...@perl.org
Hola... I've spend some time these last few days slowly getting
currying to work in pugs.

L<S06/Currying> states: "takes a series of named arguments"

The way binding is implemented in pugs does not seem to require
limiting that usage case. We have 2 functions, that operate on a
subroutine (which knows it's params):

bind some params
finalize bindings

bind some params will return a derived subroutine, with some params
prebound. assuming does just this.

finalize bindings will bind defaults to optionals, and check that we
have enough invocants and requireds bound, and return a derived
subroutine, with all params bound.

This is passed to the code that applies the sub.

The algorithmic approach to binding some params:

bind invocants

bind named parameters, and keep leftover pairs for %_

treat nonpairs as positionals, and bind them sequentially. Left
over nonpairs get put in @_

if we have slurpy params, they act as %_ and @_.

package a sub with the bindings, and the unbound params, and
return it.

and, well... that's it.

This has some funny properties, like:

foo ($x) { ... }

foo(x => 1, x => 2); # yields $x = 1, %_<x> = 2

foo(1, x => 2); # $x = 2, @_[0] = 1

(&foo.assuming(1))(x => 2); # $x = 1, %_<x> = 1

Are these bugs? I could live with them, but they are a bit
perl5-ish.

I think we need a little more guidance, as the exact semantics of
currying are not that well defined.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &
/\ kung foo master: *shu*rik*en*sh*u*rik*en*s*hur*i*ke*n*: neeyah!!!!

Yuval Kogman

unread,
Mar 23, 2005, 1:24:48 PM3/23/05
to perl6-l...@perl.org
On Wed, Mar 23, 2005 at 17:43:52 +0200, Yuval Kogman wrote:
> Hola... I've spend some time these last few days slowly getting
> currying to work in pugs.

It should also be mentioned that I made magical $?SUB et al unbind
the sub.

In a curried sub, should that happen?

It looks more consistent for me, because a sub shouldn't know it's
curried, but you never know in p6 ;-)

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM: neeyah!

Larry Wall

unread,
Mar 23, 2005, 2:53:06 PM3/23/05
to perl6-l...@perl.org
On Wed, Mar 23, 2005 at 05:43:52PM +0200, Yuval Kogman wrote:
: The algorithmic approach to binding some params:

:
: bind invocants
:
: bind named parameters, and keep leftover pairs for %_
:
: treat nonpairs as positionals, and bind them sequentially. Left
: over nonpairs get put in @_

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. Otherwise you can't catch binding explicitly to
a positional Pair argument. 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. I'd like the transition from positional to named
processing to be irrevocable, at least in the case of ordinary binding.

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.

: if we have slurpy params, they act as %_ and @_.


:
: package a sub with the bindings, and the unbound params, and
: return it.

: and, well... that's it.
:
: This has some funny properties, like:
:
: foo ($x) { ... }
:
: 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 %_.

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

: (&foo.assuming(1))(x => 2); # $x = 1, %_<x> = 1

Would also be illegal for a sub, and put x=>2 into %_ for methods.

: Are these bugs? I could live with them, but they are a bit


: perl5-ish.
:
: I think we need a little more guidance, as the exact semantics of
: currying are not that well defined.

Another thing to think about is whether you allow application of
.assuming to a generic multimethod before it is dispatched. It would
be nice to be able to do that, and I can see that if multis don't
name their first parameters consistently, currying on position would
allow a dispatch that named currying wouldn't be able to express.

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

Larry

Larry Wall

unread,
Mar 23, 2005, 3:01:25 PM3/23/05
to perl6-l...@perl.org
On Wed, Mar 23, 2005 at 08:24:48PM +0200, Yuval Kogman wrote:

: On Wed, Mar 23, 2005 at 17:43:52 +0200, Yuval Kogman wrote:
: > Hola... I've spend some time these last few days slowly getting
: > currying to work in pugs.
:
: It should also be mentioned that I made magical $?SUB et al unbind
: the sub.
:
: In a curried sub, should that happen?
:
: It looks more consistent for me, because a sub shouldn't know it's
: curried, but you never know in p6 ;-)

I agree, the sub should not have to know it's been curried. (For
instance, it would certainly call itself assuming its full signature.)
It would be a violation of "excapsulation" to require the sub to
know how it's being used. Context dependencies should generally be
optional, not forced--anything beyond simple scalar/list dependencies
should probably be the province of explicit calls to want() and
caller(). (Which calls are likely to pessimize various optimizations.)

Larry

Yuval Kogman

unread,
Mar 23, 2005, 4:08:17 PM3/23/05
to perl6-l...@perl.org
On Wed, Mar 23, 2005 at 11:53:06 -0800, Larry Wall wrote:
> On Wed, Mar 23, 2005 at 05:43:52PM +0200, Yuval Kogman wrote:
> : The algorithmic approach to binding some params:
> :
> : bind invocants
> :
> : bind named parameters, and keep leftover pairs for %_
> :
> : treat nonpairs as positionals, and bind them sequentially. Left
> : over nonpairs get put in @_
>
> 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

> Otherwise you can't catch binding explicitly to
> a positional Pair argument.

soo... how would you diambiguate that in the general case?

Can I pass a named for an optional? or is that always a positionally
bound pair?

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?

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?

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

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

> : 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?

> : 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?

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.

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

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me climbs a brick wall with his fingers: neeyah!

Yuval Kogman

unread,
Mar 23, 2005, 4:19:39 PM3/23/05
to perl6-l...@perl.org
More!

can you have several slurpy params, of the same type, which are
assigned contiguous sequences of the thing they can slurp?

foo(*@a, *%a, *@b)
foo(1, 2, 3, a => b, c => d, 4, 5, 6);

for me that makes sense for slurpy blocks, but not anything else

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me sneaks up from another MIME part: neeyah!!!!!

Luke Palmer

unread,
Mar 24, 2005, 6:35:12 AM3/24/05
to Yuval Kogman, perl6-l...@perl.org
Yuval Kogman writes:
> More!
>
> can you have several slurpy params, of the same type, which are
> assigned contiguous sequences of the thing they can slurp?
>
> foo(*@a, *%a, *@b)
> foo(1, 2, 3, a => b, c => d, 4, 5, 6);
>
> for me that makes sense for slurpy blocks, but not anything else

I don't think so. If the signature didn't give you an error for doing
that (which it would), we could expect these bindings:

@a : [1, 2, 3, a => b, c => d, 4, 5, 6]
%a : {}
@b : []

Slurpy array is a greedy bastard. Slurpy hash is only greedy for pairs,
and it stops trying after it sees something that is not a pair.

But that makes me wonder whether you can do this:

sub foo ($a, $b, *%options, &code)

Given how I've described the slurpy hash above, that should work fine.
But given the current semantics, that doesn't work.

Perhaps, in the absence of accepting my more general proposal, we could
just define a left-to-right matching order. Named arguments would be
assumed to be part of a slurpy hash (and must always come immediately
before a slurpy hash if there is one). Likewise with slurpy scalars.
But it would allow things like this:

sub svn (*%svnopts, $command, *%commandopts)

Which authors would have their own reasons for writing.

Luke

Larry Wall

unread,
Mar 24, 2005, 3:58:32 PM3/24/05
to perl6-l...@perl.org
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

Larry Wall

unread,
Mar 24, 2005, 4:31:16 PM3/24/05
to perl6-l...@perl.org
On Thu, Mar 24, 2005 at 12:58:32PM -0800, Larry Wall wrote:
: 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.

Hmm, that's ambiguous, so s:w/by Step C/by Step B before Step C happens/.

Larry

Luke Palmer

unread,
Mar 24, 2005, 5:09:37 PM3/24/05
to perl6-l...@perl.org
Larry Wall writes:
> 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,

Wait, so:

sub foo (?$x, *%opts) {...}

foo @a; # $x = \@a, %opts = ();
foo %a; # $x = undef, %opts = %a;

That's asymmetrical, and I think in a bad way.

Luke

Larry Wall

unread,
Mar 24, 2005, 7:09:30 PM3/24/05
to perl6-l...@perl.org

Yeah, I agree. How 'bout we go with something like you have to *
the actual hash (if it's the first thing) to make it look like a
list of pairs to the parser, and we can just get rid of 3 there.

Larry

Thomas Sandlaß

unread,
Mar 29, 2005, 10:16:44 AM3/29/05
to perl6-l...@perl.org
Larry Wall wrote:
> Yeah, I agree. How 'bout we go with something like you have to *
> the actual hash (if it's the first thing) to make it look like a
> list of pairs to the parser, and we can just get rid of 3 there.

I'm not sure if 3) was superflous depending on the definition of "non-pair".
Did you mean none(Pair, Hash) or just none(Pair)? And is there something
like a parsed pair---I mean fat arrow in the parse tree as opposed to
an instance of Pair? How is

my Pair $p = (x => 3);

foo $p; # $x = undef, %opts = { x => 3 }

handled in comparison to

foo x => 3; # $x = 3, %opts = undef

Cases 2) and 3) actually look more like special cases of a
type check to me. And does the rule say that type errors are
shoved into the slurpy hash if present? Which would give a
nice way to implement unchecked sigs by adding an implicit
*%_ parameter? IIRC it was mentioned as a feature somewhere
to have more than one slurpy array or hash with different
types.

Regards,
--
TSa (Thomas Sandlaß)

Reply all
Reply to author
Forward
0 new messages