On 9/3/05, Damian Conway <[EMAIL PROTECTED]> wrote: > Hmmmm. The arity of a given multi might be 3 or 4 or 5. > > If *only* there were a way to return a single value that was simultaneously > any of 3 or 4 or 5. > > Oh, wait a minute...
Well, we'd better document that pretty damn well then, and provide min_arity and max_arity, too. This is one of those places where Yuval is spot on about autothreading being evil. This is also one of those places where I am spot on about Junctive logic being evil. It looks like returning a junction is the dwimmiest thing we can do here: given &code.arity { when 1 { code(1) } when 2 { code(1,2) } } So if &code is a multimethod that has variants that take two parameters or three parameters, great, we call it with (1,2), which will succeed. And if it has variants that take one parameter or three parameters, great, we call it with (1), which will succeed. And if it has variants that take one parameter or two parameters, um..., great, we call it with (1), which will succeed. In that last case though, this is not equivalent to the above: given &code.arity { when 2 { code(1,2) } when 1 { code(1) } } That may be a little... surprising. Still, it's fixed to succeed either way, so that's probably okay, right? But let's do a little thinking here. You're asking a code reference for its arity. It's pretty likely, that unless you are doing dwimminess calculations (which aren't that uncommon, especially in Damian's modules), that you have no idea what the arguments are either. An example of a function that uses arity is &map. sub map (&code, [EMAIL PROTECTED]) { gather { my @args = @list.splice(0, &code.arity); take &code([EMAIL PROTECTED]); } } In the best case (depending on our decision), this fails with "can't assign a junction to an array". In the worst case, it autothreads over splice, returning a junction of lists, makes @args a junction of lists, and then returns a list of junctions for each arity the multimethod could have taken on. I don't think that's correct... at all. The correct way to have written that function is: sub map (&code, [EMAIL PROTECTED]) { gather { my @args = @list.splice(0, min(grep { ?$_ } &code.arity.states)); take &code([EMAIL PROTECTED]); } } Not quite so friendly anymore. In order to use this, we had to access the states of the junction explicitly, which pretty much killed the advantage of it being a junction. Junctions are logical travesty, and it seems to me that they cease to be useful in all but the situations where the coder knows *everything*. But I still like them. Here's how I'm thinking they should work. This is a minimalistic approach: that is, I'm defining them in the safest and most limited way I can, and adding useful cases, instead of defining them in the richest and most general way possible and forbidding cases that are deemed "unsafe". Throw out all your notions about how Junctions work. We're building from scratch. Things that come on the right side of smart match do a role called Pattern, which looks like this: role Pattern { method match(Any --> Bool) {...} } Among the things that do pattern are Numbers, Strings, Arrays, ... (equivalence); Types, Sets, Hashes (membership); Bools, Closures (truth); and Junctions (pattern grep). That is, a Junction is just a collection of Patterns together with a logical operation. It, in turn, is a pattern that can be smart-matched against. Therefore, we sidestep the issue of the existence of junctions making every ordered set have exactly one element[1]. because there is nothing illogical against testing against a pattern. You can also safely pass it to functions, and they can use it in their smart matches, and everything is dandy. That's it for the base formulation. A junction is just a pattern, and it makes no sense to use it outside of the smart-match operator. But that means that we have to change our idioms: if $x == 1 | 2 | 3 {...} # becomes if $x ~~ 1 | 2 | 3 {...} # not so bad if $x < $a | $b {...} # becomes if ($x ~~ { $_ < $a } | { $_ < $b }) {...} # eeeyuck if $x < $a || $x < $b {...} # back to square one # from E06 if any(@newvals) > any(@oldvals) { say "Already seen at least one smaller value"; } # becomes if (grep { my $old = $_; grep { $_ > $old } @newvals } @oldvals) { say "I don't remember what I was going to say because the condition took so long to type" } So, that sucks. But I'm beginning to wonder whether we're really stuck there. The two yucky examples above can be rewritten, once we take advantage of some nice properties of orderings: if $x < max($a, $b) {...} if min(@newvals) > min(@oldvals) {...} But that's a solution to the specific comparison problems, not the general threaded logic problem. Yeah... so, that's what I think about junctions. This is definitely a tentative proposal, so if there are cases where junctions in their current state make a certain problem a lot easier (and I mean conceptual changes, not minor syntactic issues), then I want to hear them so we can try to expand this proposal to include that functionality. Also remember about hyper-argument foo(>>1,2,3<<) which has your autothreading base covered. Luke [1] $a <= any($a, $b). any($a, $b) <= $b. Therefore, $a <= $b. Everything is <= everything else (we made no assumptions about $a and $b), and < is anticommutative, so every object is equal to every other object, and there is only one object.