Luke wrote:
> Well, we'd better document that [junctive arity values] pretty damn
> well then, and provide min_arity and max_arity, too.
Unnecessary. The C<max> and C<min> builtins should be overloaded to Just
Work on junctive values:
if min &code.arity < 2 {...}
> 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.
No. Neither autothreading nor junctive logic is "evil". They're just
"different". I know that many people can't tell the difference between
"evil" and "different", but you two are smart enough to be able to.
> 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?
It's not surprising at all. The order of C<when> tests (usually) matters,
because a series of C<when> statements (usually) short-circuits.
> 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));
BTW, that should be &code.arity.values, not &code.arity.states.
> take &code([EMAIL PROTECTED]);
> }
> }
>
> Not quite so friendly anymore.
Only because you're doing it the unfriendly way. Instead of, for example:
sub map (&code, [EMAIL PROTECTED]) {
gather {
my @args = @list.splice(0, min &code.arity);
take &code([EMAIL PROTECTED]);
}
}
which is perfectly friendly, and also clearer. Or, perhaps more usefully, a
multi should be illegal as a map block:
sub map (&code, [EMAIL PROTECTED]) {
croak "Can't use multi as map block" if &code.arity.values > 1;
gather {
my @args = @list.splice(0, &code.arity);
take &code([EMAIL PROTECTED]);
}
}
> Junctions are logical travesty,
Well, that's very emotive, but I don't believe it's either a useful or an
accurate characterization. I would agree that junctions can be logically
*sophisticated*, but then I'd argue that *all* programming constructs are that.
> and it seems to me that they cease to be useful in all but the
> situations where the coder knows *everything*.
What does that mean, exactly? How can anyone *ever* write sensible code
without knowing what kind of values they're processing?
> 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
Yes. Bad. Because now I can't specify that I specifically want to test with
C<==> or C<eq> or my new C<^%$!> operator.
> 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.
Otherwise hard things that junctions make a lot easier:
if 0 <= @coefficients < 1 {...}
if 0 <= all(@new_coefficients) < all(@prev_coefficients) < 1 {...}
if 0 <= all(@new_coefficients) != all(@prev_coefficients) < 1 {...}
if 0 <= any(@new_coefficients) != all(@prev_coefficients) < 1 {...}
if ! defined one(@inputs) {...}
sub roots ($A, $B, $C) {
return (-$B + (1|-1)*sqrt($B**2 - 4*$A*$C) ) / 2*$A;
}
&constraints := all gather {
# various config tests resulting in varieties of...
take sub {...};
}
# and later...
if constraints($value) {...}
> $a <= any($a, $b).
> any($a, $b) <= $b.
> Therefore, $a <= $b.
No. This is a simple fallacy. You're ignoring the inherent logical
asymmetry of the implicit alternative. Consider:
Luke is no smarter than (Luke or a rock).
(Luke or a rock) is no smarter than a rock.
Therefore Luke is no smarter than a rock.
Clearly a false conclusion. ;-)
But the logical error is made clearer when the distribution is made explicit:
Luke is no smarter than Luke or Luke is no smarter than a rock.
Luke is no smarter than a rock or a rock is no smarter than a rock.
Remove the false assertions (since nothing correct can ever be deduced
from a false assertion):
Luke is no smarter than Luke
a rock is no smarter than a rock.
But now the remaining assertions support *no* new conclusion.
Damian