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