$x = any(3,4,5);
@l = $x.values.sort;
Leaves us with @l == (3,4,5), and that makes a fair amount of sense.
What do the following evaluate to:
@l1 = all(3,4,5).values.sort;
@l2 = one(3,4,5).values.sort;
@l3 = none(3,4,5).values.sort;
@l4 = any(all(1,2),all(3,4)).values.sort;
If C<.values> doesn't it cut it for these cases, what other forms of
introspection are we going to allow on junctions, to determine what they
are?
On a slightly different topic, do the following equivalences work:
any($a, $a) == $a
any($a,$a,$b) == any($a,$b)
any(any($a,$b),any($c,$d)) == any($a,$b,$c,$d)
all($a, $a) == $a
all($a,$a,$b) == all($a,$b)
all(all($a,$b),any($c,$d)) == all($a,$b,$c,$d)
none($a, $a) == undef
none($a,$a,$b) == none($a,$b)
none(none($a,$b),none($c,$d)) == none($a,$b,$c,$d)
one($a, $a) == false
one($a,$a,$b) == ($a == $b ?? undef :: $b)
-- Rod Adams
Isn't this one false in the case when $a is undef?
-Scott
--
Jonathan Scott Duff
du...@pobox.com
> Okay, so we've established that:
>
> $x = any(3,4,5);
> @l = $x.values.sort;
>
> Leaves us with @l == (3,4,5), and that makes a fair amount of sense.
>
>
> What do the following evaluate to:
>
> @l1 = all(3,4,5).values.sort;
Same.
> @l2 = one(3,4,5).values.sort;
Same.
> @l3 = none(3,4,5).values.sort;
Same.
> @l4 = any(all(1,2),all(3,4)).values.sort;
= (all(1,2),all(3,4)).sort
= (all(1,2),all(3,4))
> If C<.values> doesn't it cut it for these cases, what other forms of
> introspection are we going to allow on junctions, to determine what they
> are?
.values tells you what raw values are inside the junction. The other kind of
introspection that's desirable is: "what raw values can *match* this
junction". There would probably be a .states method for that.
To see the difference between the two, consider:
my $ideal_partner = all( any(«tall dark rich»),
any(«rich old frail»),
any(«Australian rich»),
);
$ideal_partner.values would return the three distinct values in the junction:
( any(«tall dark rich»),
any(«rich old frail»),
any(«Australian rich»),
);
But $ideal_partner.states would return only those non-junctive values that
(smart-)match the junction. Namely, "rich".
> On a slightly different topic, do the following equivalences work:
(I will assume in all my answers that $a, $b, $c, $d have different values,
except where it doesn't matter either way).
> any($a, $a) == $a
True.
> any($a,$a,$b) == any($a,$b)
True.
> any(any($a,$b),any($c,$d)) == any($a,$b,$c,$d)
True.
> all($a, $a) == $a
True.
> all($a,$a,$b) == all($a,$b)
False. The autothreading makes that:
all( $a==$a, $a==$b, $b==$b, $b==$a)
and they're not (in general) all true.
> all(all($a,$b),any($c,$d)) == all($a,$b,$c,$d)
False. Because all($a,$b) != all($a,$b,$c,$d)
> none($a, $a) == undef
True.
> none($a,$a,$b) == none($a,$b)
True.
> none(none($a,$b),none($c,$d)) == none($a,$b,$c,$d)
True.
>
> one($a, $a) == false
True.
> one($a,$a,$b) == ($a == $b ?? undef :: $b)
True.
Damian
>>>none($a, $a) == undef
>>
>>True.
>
> Isn't this one false in the case when $a is undef?
Yes. Apologies for not being more precise.
Damian
>
> .values tells you what raw values are inside the junction. The other
> kind of introspection that's desirable is: "what raw values can
> *match* this junction". There would probably be a .states method for
> that.
>
> To see the difference between the two, consider:
>
> my $ideal_partner = all( any(«tall dark rich»),
> any(«rich old frail»),
> any(«Australian rich»),
> );
>
> $ideal_partner.values would return the three distinct values in the
> junction:
>
> ( any(«tall dark rich»),
> any(«rich old frail»),
> any(«Australian rich»),
> );
>
> But $ideal_partner.states would return only those non-junctive values
> that (smart-)match the junction. Namely, "rich".
One would need a method of determining whether the C<.values> are in an
any/all/none/one relationship. Trivial, but necessary. That and
C<.values> are likely enough introspection. Leave the C<.state> for CPAN
to solve.
>
>> On a slightly different topic, do the following equivalences work:
>
>
> (I will assume in all my answers that $a, $b, $c, $d have different
> values, except where it doesn't matter either way).
>
>> all($a,$a,$b) == all($a,$b)
>
>
> False. The autothreading makes that:
>
> all( $a==$a, $a==$b, $b==$b, $b==$a)
>
> and they're not (in general) all true.
I knew I was typing this all wrong. Whipping out math symbols, replace
the == with ≡, or identical to.
In other words, I was not asking how C< if all($a,$a,$b) == all($a,$b)
{...} > evaluated, but instead if
if all($a,$a,$b) {...}
if all($a,$b) {...}
would both evaluate the same.
And I was making no assumptions about the values of $a .. $d.
>
>> all(all($a,$b),any($c,$d)) == all($a,$b,$c,$d)
>
> False. Because all($a,$b) != all($a,$b,$c,$d)
Should have been
all(all($a,$b), all($c,$d)) ≡ all($a,$b,$c,$d)
Sorry for any confusion. It's just a real pain to type chars that are
not bound on your keyboard.
As for the undef's, I didn't know what else to call the empty junctive.
-- Rod Adams
Scott already caught (and Damian acknowledged) this one, it's false
if $a == undef.
> >none($a,$a,$b) == none($a,$b)
> True.
Okay, I follow this one -- here's the derivation.
-> none($a == none($a, $b),
$b == none($a, $b))
-> none(none($a==$a, $a==$b),
none($b==$a, $b==$b))
-> none(none(1, 0), none(0, 1))
-> none(0, 0)
-> true
> >none(none($a,$b),none($c,$d)) == none($a,$b,$c,$d)
> True.
Hmmmm...
-> none(none($a,$b) == none($a,$b,$c,$d),
none($c,$d) == none($a,$b,$c,$d))
-> none(none($a == none($a,$b,$c,$d),
$b == none($a,$b,$c,$d)),
none($c == none($a,$b,$c,$d),
$d == none($a,$b,$c,$d)))
-> none(none(none($a==$a, $a==$b, $a==$c, $a==$d),
none($b==$a, $b==$b, $b==$c, $b==$d)),
none(none($c==$a, $c==$b, $c==$c, $c==$d),
none($d==$a, $d==$b, $d==$c, $d==$d)))
-> none(none(none(1, 0, 0, 0),
none(0, 1, 0, 0)),
none(none(0, 0, 1, 0),
none(0, 0, 0, 1)))
-> none(none(0, 0), none(0, 0))
-> none(1,1)
-> false
Ummm, what am I missing? To state it another way... we can
show that none($a,$b) == none($a, $b, $c, $d) is true, so
none( none($a,$b), none($c,$d) ) == none($a, $b, $c, $d)
is equivalent to
none( none($a,$b) == none($a, $b, $c, $d),
none($c,$d) == none($a, $b, $c, $d))
which is none(1, 1), or false. Did I autothread wrongly here?
And for completeness...
> >one($a,$a,$b) == ($a == $b ?? undef :: $b)
> True.
...except when $a == $b == undef.
Pm
No, upon closer looking, you're right that it should both evaluate
false, and that they are not equivalent in what I meant to be asking.
none(none($a,$b),none($c,$d)) ? all(any($a,$b),any($c,$d))
Should work, though. Not that it helps simplify matters at all.
In essence, what I'm doing is attempting to create a set of rules
whereby one can simplify a junction, by removing the nestedness of it,
or removing terms outright. In the process, I'm making sure that I
understand what they mean.
-- Rod Adams
>>>none(none($a,$b),none($c,$d)) == none($a,$b,$c,$d)
>>
>>True.
>
>
> Hmmmm...
>
> -> none(none($a,$b) == none($a,$b,$c,$d),
> none($c,$d) == none($a,$b,$c,$d))
RHS distributes first. So the expansion is this instead...
-> none(none(none($a,$b),none($c,$d)) == $a,
none(none($a,$b),none($c,$d)) == $b,
none(none($a,$b),none($c,$d)) == $c,
none(none($a,$b),none($c,$d)) == $d,
)
-> none(none(none($a,$b)=$a, none($c,$d)=$a),
none(none($a,$b)=$b, none($c,$d)=$b),
none(none($a,$b)=$c, none($c,$d)=$c),
none(none($a,$b)=$d, none($c,$d)=$d),
)
-> none(none(0, 1),
none(0, 1),
none(1, 0),
none(1, 0),
)
-> none(0,
0,
0,
0,
)
-> true
>
> Ummm, what am I missing? To state it another way... we can
> show that none($a,$b) == none($a, $b, $c, $d) is true,
RHS distributes first again:
none($a,$b) == none($a, $b, $c, $d)
-> none(
none($a,$b)==$a,
none($a,$b)==$b,
none($a,$b)==$c,
none($a,$b)==$d,
)
-> none(
0,
0,
1,
1,
)
-> false
Damian
Ummm, okay, but that's not what I interpreted from S09, where
it says that the "left-most" conjunction or injunction is
autothreaded first. In this I've also assumed that
$a == $b
is equivalent to
&infix:<==>($a, $b)
so I'm missing the piece that says to distribute the RHS first.
Pm
>>RHS distributes first. So the expansion is this instead...
>
> Ummm, okay, but that's not what I interpreted from S09, where
> it says that the "left-most" conjunction or injunction is
> autothreaded first. In this I've also assumed that
>
> $a == $b
>
> is equivalent to
>
> &infix:<==>($a, $b)
>
> so I'm missing the piece that says to distribute the RHS first.
Probably because that piece is still only in my head.
Or because I got that bit of S09 wrong.
Or because Larry or Luke overrode me on it and I haven't updated my
Perl6::Junctions module accordingly.
;-)
So forget what I said, and go with what Patrick said. Since it's easy to argue
the outcome of these corner cases either way (depending on how you
backtranslate them to English), it will be best to make the rules consistent
(i.e. left-most first for both functions and operators).
Apologies all...and thanks Patrick.
Damian
> On Wed, Feb 16, 2005 at 12:17:35PM +1100, Damian Conway wrote:
>> >none($a, $a) == undef
>>
>> True.
>
> Isn't this one false in the case when $a is undef?
Since it is numerical comparison, it is false as long as $a == 0.
(I would hope.)
Eirik
--
"So this is the Sword of Immortality? Huh?
What's it doin' in a CRYPT?!"
--- John S. Novak, III, quoting an unnamed player
I, and I think many others, have been trying to follow along on the discussions
regarding junctions, and I have to say that for the most part, much of it goes
<<insert graphic of open hand, palm down, waving to and fro above head>>.
Any chance that you could provide one or two simple but realistic examples of
using Junctions and their operators?
I see individual snippets of use and they appear to make sense, but when I try
envisage using them in code I have recently written, I find nothing leaping off
the page at me as an obvious candidate.
Thanks.
I'll give it a shot, but keep in mind that I'm somewhat new to this
also. :-)
First, junctions are an easy way to represent the relationships
"any", "all", "one", or "none". So, where we once had to say:
if ($x==$a or $x==$b or $x==$c or $x==$d) { ... }
we can now say
if $x == any($a, $b, $c, $d) { ... }
Similarly, where we once had to say
if (grep { $x == $^z } @list) { ... }
if (grep { $x == $^z } @list) == 0 { ... }
we can now say
if $x == any(@list) { ... }
if $x == none(@list) { ... }
And for fun, try writing the equivalent of
if $x == one($a, $b, $c, $d) { ... }
without a junction. (Okay, one can cheat with C<grep>.)
A programmer can easily use these without having to worry about the
notions of "autothreading" or "superpositions" or the like, and
their translations to English are fairly obvious. I suspect that
constructs like the above will be the majority of use for junctions.
Things start to get "weirder" when we start storing junctions into
variables, and/or passing those variables/junctions into subroutines.
But I think this is not *too* far removed from the idea of placing a
subroutine or a rule into a scalar -- i.e., using a scalar to represent
something more than a single-valued primitive type. Thus, just as one
can write
$x = new CGI;
$y = rule { \d+ };
$z = sub { ... };
and then use $x, $y, and $z later on for an object, rule, and sub
(which may have a variety of side-effects associated with them), it makes
sense that one can write
$w = any(@list);
and then use $w to refer to the junction later. And a programmer who
does this is taking on some responsibility for understanding that $w
isn't one of the "trivial" scalars anymore (same as for $x, $y, and $z).
However, one difference is that when one typically uses an object, rule,
or subroutine in a scalar there is some syntax that makes their nature
apparent. Some feel that Junctions might be just a bit "too magical"
in this respect (e.g., Luke has made some syntactic suggestions to try
make the existence/threading of a junction more apparent).
Pm
I'd write the P5 none() case as
if ( grep { $x != $_ } @list ) { ... }
or
unless( grep{ $x == $_ } @list ) { ... }
which tends to reduce the sugaryness of the new syntax slightly.
> And for fun, try writing the equivalent of
>
> if $x == one($a, $b, $c, $d) { ... }
I'm also not entirely sure at this point whether that means
if( grep( { $x == $_ } $a, $b, $c, $d ) == 1 ) { ... }
or something different?
>
> without a junction. (Okay, one can cheat with C<grep>.)
>
But most of those examples pretty clear and understandable. I'm not sure that I
see their convenience alone as a convincing arguement for the existance of
Junctions. I keep thinking that there is something altogether more fundementally
useful and ...well... cleverer underlying their inclusion, but at this stage I
am not seeing it.
In this usage, they appear to be replicating functionality that is already being
provided via the inclusion of the hyper-operators.
('scuse me if I skip the unicode and/or get the syntax wrong!)
if( $x ==<< @list ) { ... } ## any?
if( $x !=<< @list ) { ... } ## none?
if( ( $x ==<< @list ) == 1 ) { ... } ## one?
if( ( $x ==<< @list ) == @list ) { ... } ## all?
It would very much depend upon what a hyper operator returns in a boolean
context, but they /could/ be made to work as I've indicated I think.
If the hyper operator returned one boolean result for each comparison it made,
and if a list of boolean values in a boolean context collapsed to a count of the
trues/1s it contained, I think those would work. You would also get the bonus of
if( ( $x ==<< @list ) == 2 ) { ... } ## two() ( three(), ten() etc. )
if( ( $x <=<< @list ) == @list/2 ) { ... } ## fify percent below...
But I think the promise of Junctions appears to come from using them in
conjunction with each other.
$x = any( @list1 );
$y = all( @list2 );
$z = none( @list3 );
if( $x <= $y ) { ... }
if( any( $x != $y ) or all( $y == $z ) ) { ... }
except that I haven't got a clue what those mean?
I've sat and starred at the worked examples given elsewhere in this and the
other thread, and whilst I can follow how the results are derived when someone
lays them out for me--though I fell into the trap of accepting the one where
Damian made a mistake, which probably means I wasn't paying close enough
attention--when I get to the the last line and see that the answer it true (or
false), I look back at the original unexpanded statement and think:
Yes! But what does it mean? When and where is that going to be useful?
I see the dangers of scalars that aren't oridinary scalars--I also accept your
premise that Junctions are no different from references in that regard. At this
stage my "big question" is:
What are they going to do for me?
And whilst the simple examples show some syntatic sugaryness, I think that it
will not be until I begin to see how to apply the more complex cases to real-
world problems that I could reach a conclusion about whether the value of their
inclusion outweights the onus of awareness the impose upon the programmer.
>
> Pm
Regards. njs.
Yes, that's essentially correct.
> In this usage, they appear to be replicating functionality that is
> already being provided via the inclusion of the hyper-operators.
> ('scuse me if I skip the unicode and/or get the syntax wrong!)
>
> if( $x ==<< @list ) { ... } ## any?
> if( $x !=<< @list ) { ... } ## none?
> if( ( $x ==<< @list ) == 1 ) { ... } ## one?
> if( ( $x ==<< @list ) == @list ) { ... } ## all?
Ouch, this makes my head hurt. :-)
A syntax note -- C<==> is an infix operator, thus the correct
syntax would have to be
> if( $x >>==<< @list ) { ... } ## any?
> if( $x >>!=<< @list ) { ... } ## none?
and the result of $x >>==<< @list would end up being a list of
true/false values.
> If the hyper operator returned one boolean result for each
> comparison it made, and if a list of boolean values in a
> boolean context collapsed to a count of the trues/1s it contained,
> I think those would work.
Part of the problem is that we don't have a "list of boolean values"
that is somehow distinguishable from "list of values". (And a list
evaluated in boolean context is true if the list is non-empty.)
If we create a "list of boolean values" type, it effectively becomes
a junction. :-)
> But I think the promise of Junctions appears to come from using them in
> conjunction with each other.
>
> $x = any( @list1 );
> $y = all( @list2 );
> $z = none( @list3 );
> if( $x <= $y ) { ... }
> if( any( $x != $y ) or all( $y == $z ) ) { ... }
>
> except that I haven't got a clue what those mean?
Well,
if ($x <= $y) { ... }
is better written out as:
if ( any(@list1) <= all(@list2) ) { ... }
which reads "if any of the elements of list1 are less than or equal to
all of the elements of list2". One could arguably claim/show that this
is mathematically equivalent to
if ( min(@list1) <= min(@list2) ) { ... }
except that junctions could potentially work with relationships that
aren't transitive as C<< <= >> is.
I'll have to think about your second C<if> statement a bit -- I agree
that it's not immediately obvious what it means, but I'm not sure that
we could come up with an alternate way of writing it which is also
immediately obvious. Phrased another way -- regardless of the
construct being discussed we can probably write an expression using
that construct that isn't easily deciphered at first.
> I've sat and starred at the worked examples given elsewhere in this
> and the other thread, and [...] I look back at the original
> unexpanded statement and think:
>
> Yes! But what does it mean? When and where is that going to be useful?
Part of the reason for this may simply be that many of the examples
discussed in these threads are in fact "edge cases" that have been
designed to illustrate a point rather than to be useful. Indeed,
they're discussed simply because they aren't obvious.
> And whilst the simple examples show some syntatic sugaryness, I think that it
> will not be until I begin to see how to apply the more complex cases
> to real-world problems that I could reach a conclusion about whether
> the value of their inclusion outweights the onus of awareness the
> impose upon the programmer.
I think one can make this observation for many features slated for
Perl 6 -- i.e., the simple cases look simple but the full application
of complex cases isn't always immediately obvious. I know I get this
feeling from looking at things like roles, mixins, slurpy arrays,
slurpy hashes, C<temp> declarations, hypotheticals, hyperoperators, etc.
However, just as I don't have to be aware of every CPAN module that
exists in order to write Perl programs, I don't think having these
features built into the language will require a great deal of
awareness on the programmer until the programmer chooses to use them.
Others could reasonably disagree with this statement. And to
anticipate the followup question of "Well, why not make features such
as junctions into optional modules?", I think a partial answer is that
features like these really need deep language support to work
effectively in their complex applications. Otherwise we'd just
stick with Perl 5. :-)
Pm
Well, I don't think anyone can argue that having ~~ behave properly
with junctions is wrong. After all, we can make it do whatever we
want.
$foo ~~ 1 | 2 | 3
Perhaps we can make ~~ an even more magical comparison operator.
$x ~~ $y is a huffmanized version of $x ~==~ $y
@x ~<~ $y is a smart-match based on < instead of ==
Or perhaps you can choose on which side the magic works:
@x ~< $y
OK, enough craziness for today.
$foo ~<~ $bar # uses C<lt> for strings, C<+<>for numbers, voodoo for
junctions, etc...
Ashley
>On Wed, Feb 16, 2005 at 06:04:37PM +0000, Nigel Sandever wrote:
>
>>If the hyper operator returned one boolean result for each
>>comparison it made, and if a list of boolean values in a
>>boolean context collapsed to a count of the trues/1s it contained,
>>I think those would work.
>>
>>
>
>Part of the problem is that we don't have a "list of boolean values"
>that is somehow distinguishable from "list of values". (And a list
>evaluated in boolean context is true if the list is non-empty.)
>
>If we create a "list of boolean values" type, it effectively becomes
>a junction. :-)
>
What if we instead redefine any/all/one/none to be functions (and/or
list methods) that take a list, evaluates each element in boolean
context, and returns boolean?
Therefore, Nigels's examples become:
if any( $x »==« @list) { ... }
if none($x »==« @list) { ... }
if all( $x »==« @list) { ... }
if one( $x »==« @list) { ... }
Then case that I see happening most often will then be:
if any($x »==« (1,2,10,100,200,1000,2000)) { ... }
I'll also throw in the argue that anything much more complex than this
definitely falls into the category of "edge case", and is of dubious
utility, and IMHO, a lot of danger.
It occurred to me that junctions felt a lot like typeglobs. Very
powerful thing, that little *. But also something that experienced
programmers tell inexperienced ones something like "Don't mess with them
unless you really understand them." Then throw in that most programmers
use a heavy dose of lexicals, which aren't typeglobable. In the end, you
are left with only one useful idiom left:
*NewFunc = sub {...};
Which will usually appears inside an AUTOLOAD.
(yes, I'm sure that Damian and some others on this list can create
exceptionally cool use of typeglobs, in ways that would warp our minds,
but I'm talking overall utility).
How does this relate to junctions? All I think anyone ever *asked* for
out of a junction was an easier way to write:
if ($x==$a or $x==$b or $x==$c or $x==$d) { ... }
The rest of the invention feels like "Magic", and is something the
experienced will tell the inexperienced not to mess with until they
really understand the consequences of using them. And even then, few of
the experienced will make more use of it than the simple case.
-- Rod Adams
>That, and we'd like a novice to be able to write
>
> given $x {
> when 1 | 2 | 3 {...}
> when 4 | 5 | 6 {...}
> }
>
Or just change C<when> to accept a list of things to compare against,
followed by a coderef.
-- Rod Adams
That, and we'd like a novice to be able to write
given $x {
when 1 | 2 | 3 {...}
when 4 | 5 | 6 {...}
}
without having to know there are junctions there underneath. Forcing
them to cargo-cult a "use junctions" declaration merely to get switch
alternatives would not solve any problems I can think of. Nor would
inventing a separate switch syntax for alternatives be appropriate when
we can make junctions work for that in a semi-orthogonal fashion. As I
said elsewhere, the training wheels probably belong on the variable's
type checking, not on the underlying type space. Variable types are
*views* into the actual underlying object types bound to the variable,
and so variable types can place arbitrary restrictions on the use of
those underlying general types.
Larry
And change if, unless, while and until to do the same thing. And lose
the ability to say:
when none(1, 2, 3) { ... }
when 1 ^ 2 ^ 3 { ... } # More useful with classes, regexen, etc.
when 1 & 2 & 3 { ... } # Likewise
All so that a newbie doesn't confuzzle himself.
Personally, I'd rather have a chain saw than a nail trimmer, even if
I'm less likely to hurt myself with the nail trimmer. And it looks
like we'll have a warning or stricture to keep newbies from chopping
their legs off anyway.
--
Brent 'Dax' Royal-Gordon <br...@brentdax.com>
Perl and Parrot hacker
"I used to have a life, but I liked mail-reading so much better."
>Rod Adams <r...@rodadams.net> wrote:
>
>
>>Larry Wall wrote:
>>
>>
>>>That, and we'd like a novice to be able to write
>>>
>>> given $x {
>>> when 1 | 2 | 3 {...}
>>> when 4 | 5 | 6 {...}
>>> }
>>>
>>Or just change C<when> to accept a list of things to compare against,
>>followed by a coderef.
>>
>>
>And change if, unless, while and until to do the same thing.
>
>
Actually, upon further investigation, I believe we get this all of this
without junctions.
According to S03, "The scalar comma |,| now constructs a list reference
of its operands."
Then S04 mentions that C< $scalar ~~ @array > is true if $scalar is in
@array.
Since C< given ... when > uses smart matching for it's evaluation, one
should be able to write the above as:
given $x {
when 1, 2, 3 {...} # at worst, this is: when (1,2,3) {...}
when 4, 5, 6 {...}
}
The simple if is:
if $x ~~ (1,2,3,4) {...} # parens needed here since , is lower than ~~
in precedence.
Same for unless/while/until. And all of this from the entirely useful C<
~~ >. The S04 code describing @Array ~~ $Scalar (for Num/Str) uses
junctions, but I'd argue a better implementation would be a short
circuiting C< for > loop, even if junctions exist. It's just plain
faster that way.
So what I see now for utility of junctions is thus:
- Common cases which C< ~~ > appears to handle for us suitably well.
- Edge cases which, IMHO, do not merit the huffman level of several
single character operators. All of which can be accomplished without the
use of junctions, though not as gracefully.
I see no need for junctions in core.
>And lose the ability to say:
> when none(1, 2, 3) { ... }
> when 1 ^ 2 ^ 3 { ... } # More useful with classes, regexen, etc.
> when 1 & 2 & 3 { ... } # Likewise
>
>All so that a newbie doesn't confuzzle himself.
>
>
You can always write your switch statements the Perl 5 way.
Or you could write:
when ({$^a ~~ /1/ && $^a ~~ /2/ && $^a ~~/3/}) {...}
And the like. Look up what C< $scalar ~~ $coderef > does if you're not
convinced.
(side question: what is the proper syntax for using a closure as the
evaluation expression in a C< when > statement?)
hmm, since the $_ is set, you could likely get away with:
when ({/1/ && /2/ && /3/}) {...}
in the case of RE's.
>Personally, I'd rather have a chain saw than a nail trimmer, even if
>I'm less likely to hurt myself with the nail trimmer. And it looks
>like we'll have a warning or stricture to keep newbies from chopping
>their legs off anyway.
>
>
I can understand your sentiments here, but I'd be a lot more sympathetic
to your cause if the alternative ways of accomplishing these things were
actually difficult. I think it's been demonstrated that any of the
junction evaluations could be done with a call to C< grep >, with
numerous other ways to perform them as well.
-- Rod Adams
That is asking if $x is a list containing 1,2,3,4.
: Same for unless/while/until. And all of this from the entirely useful C<
: ~~ >. The S04 code describing @Array ~~ $Scalar (for Num/Str) uses
: junctions, but I'd argue a better implementation would be a short
: circuiting C< for > loop, even if junctions exist. It's just plain
: faster that way.
Junctions can short circuit when they feel like it, and might in some
cases do a better job of picking the evaluation order than a human.
: So what I see now for utility of junctions is thus:
:
: - Common cases which C< ~~ > appears to handle for us suitably well.
Only if we make lists second-class citizens. The need for junctions
first became evident when we found ourselves filling the ~~ tables
with various sorts of weird non-symmetries.
: - Edge cases which, IMHO, do not merit the huffman level of several
: single character operators. All of which can be accomplished without the
: use of junctions, though not as gracefully.
Grace is important. Even more important is mapping naturally to human
linguistic structures, to the extent that it can be done unambiguously.
: I see no need for junctions in core.
I do, and I'm not likely to change my mind on this one. Sorry.
Larry
Hmm, yes, there is an interesting interaction with lazy
evaluation ranges here.
$x = any( 1 .. 1_000_000_000 );
if( $y == $x ) { ... }
It would be nice if the junction equality test here was much
smarter than a for loop (regardless of whether the for loop
short circuited - suppose $y happens to be -1!).
A range need not enumerate all of its components to be used
in a useful way.
--
>On Thu, Feb 17, 2005 at 02:18:55AM -0600, Rod Adams wrote:
>: The simple if is:
>:
>: if $x ~~ (1,2,3,4) {...} # parens needed here since , is lower than ~~
>: in precedence.
>
>That is asking if $x is a list containing 1,2,3,4.
>
>
Quoting S04:
$_ $x Type of Match Implied Matching Code
====== ===== ===================== =============
Array Array arrays are identical match if $_ »~~« $x
Array any(list) list intersection match if any(@$_) ~~ any(list)
Array Rule array grep match if any(@$_) ~~ /$x/
Array Num array contains number match if any($_) == $x
Array Str array contains string match if any($_) eq $x
And since there are no tell tell *'s on these saying that they are _not_
reversible, I must assume they _are_.
In C< if $x ~~ (1,2,3,4) {...} >, if $x is type Num or Str, then I see
no way of reconciling your statement above.
>: Same for unless/while/until. And all of this from the entirely useful C<
>: ~~ >. The S04 code describing @Array ~~ $Scalar (for Num/Str) uses
>: junctions, but I'd argue a better implementation would be a short
>: circuiting C< for > loop, even if junctions exist. It's just plain
>: faster that way.
>
>Junctions can short circuit when they feel like it, and might in some
>cases do a better job of picking the evaluation order than a human.
>
>
I was afraid someone was going to say that. And I now must convert my
reservations about junction autothreading from "very disturbing" to
"you've got to be kidding".
According to Patrick, and since no one has corrected him, I will assume
he is right,
perl6 -e "say1 'cat'|'dog'; sub say1 ($x) {say $x}"
Should output "cat\ndog\n" or "dog\ncat\n". Now, if we allow Junctions to short circuit, and since there is no fixed order of values in a junction, we could get any of "cat\ndog\n", "dog\ncat\n", "cat\n", or "dog\n".
What's that get us? non-deterministic output! I could handle non-deterministic *order* of output, because by the simple fact that you were using a junction, you didn't care about order.
Non-deterministic output! how fun!
Not to mention it contradicts S09:
"... that routine is "autothreaded", meaning the routine will be called automatically as many times as necessary to process the individual scalar elements of the junction in parallel."
Now there is some wiggle room in there for short circuiting, but not very much.
>: So what I see now for utility of junctions is thus:
>:
>: - Common cases which C< ~~ > appears to handle for us suitably well.
>
>Only if we make lists second-class citizens. The need for junctions
>first became evident when we found ourselves filling the ~~ tables
>with various sorts of weird non-symmetries.
>
>
So what other semantic makes sense for:
Str ~~ Array
Num ~~ Array
which would better appeal to your sense of symmetry?
Besides, people were telling me that my Sets were not needed, because
they could be rendered with Arrays and Hashes. I fail to see how
junctions are that different.
>: - Edge cases which, IMHO, do not merit the huffman level of several
>: single character operators. All of which can be accomplished without the
>: use of junctions, though not as gracefully.
>
>Grace is important. Even more important is mapping naturally to human
>linguistic structures, to the extent that it can be done unambiguously.
>
>
In my experience, English tends not to superimpose several values on a
given noun at once.
>: I see no need for junctions in core.
>
>I do, and I'm not likely to change my mind on this one. Sorry.
>
>
I realized that fairly early on. The situation I'm in is that while I
don't agree with all the design initiatives that come out here, it's
been the case that the more I think about them, the more I like them. I
always achieve at least a state of ambivalence or better about it.
With Junctions, it's been the case that at first I thought they were
useful and cool, but the more I think about them, the more I dislike them.
I'll argue my case for a few more days, and if I haven't gotten anywhere
by then, I'll likely give up.
-- Rod Adams
The "wiggle room" is that the junction knows when it's being asked to
collapse into a Boolean, and can know if there's no possible way the
function it's running will have side effects. (That's why we're
declaring ties now. There may be cases where we can't know for sure
if there will be side effects or not--Halting Problem stuff--but we
can make sure the junction optimizer is conservative. The Halting
Problem becomes a lot easier if you ask whether a program *might* halt
instead of whether it *will*.)
In general, it seems to simply be an amazingly bad idea to autothread
a function with side effects. In fact, I'd recommend that we warn if
a side effect occurs during autothreading.
> Besides, people were telling me that my Sets were not needed, because
> they could be rendered with Arrays and Hashes. I fail to see how
> junctions are that different.
Junctions are intended to be used mainly within conditionals and other
statements; it's sort of a happy accident that they can be assigned to
variables. At the intra-statement level, there's nothing else (short
of involving a function like C<grep>) that'll do the job. From what I
saw, your sets are mainly designed to be used at the inter-statement
level, where we have arrays and hashes to do that sort of thing.
I think junctions are important at the statement level because they
help make similar things look similar. Consider these two statements:
if($foo == $bar) { .. }
if(grep { $foo == $_ } $bar, $baz) { ... }
What makes these two statements so fundamentally different from each
other that they should be expressed in ways that *look* so different?
> >: - Edge cases which, IMHO, do not merit the huffman level of several
> >: single character operators. All of which can be accomplished without the
> >: use of junctions, though not as gracefully.
> >
> >Grace is important. Even more important is mapping naturally to human
> >linguistic structures, to the extent that it can be done unambiguously.
> >
> >
> In my experience, English tends not to superimpose several values on a
> given noun at once.
No, but nor does it have a concept quite like a variable. There's a
reason many people have trouble understanding what 'x' and 'y' are all
about in algebra. (And remember, algebra variables can sometimes have
multiple values--consider "y = x ** 2", for example.)
Junctions are equivalent to the English sentence "Get eggs, bacon, and
toast from the store". (In Perl, that'd be something like C<<
$store->get("eggs" & "bacon" & "toast") >>.) It's just a bit of
orthogonality that allows you to give "eggs, bacon, and toast" a name
and use it later.
> Junctions are equivalent to the English sentence "Get eggs, bacon, and
> toast from the store". (In Perl, that'd be something like C<<
> $store->get("eggs" & "bacon" & "toast") >>.) It's just a bit of
> orthogonality that allows you to give "eggs, bacon, and toast" a name
> and use it later.
Junctions are "grocery lists", then.
Regards,
David
If the set of these "other statements" is limited, consider creating a
Junction class (which needs a "use Junction;" to activate), which
overloads the various comparison operators for when a Junction is
involved, and defines any/all/none/one as constructors? Throw in some
overloading or bindings for |/^/&, and it looks like you get everything
your asking for. Having a method evaluate: C< 5 == any(4,5,6) > means
that the interpreter doesn't need to autothread. This also makes it
where only functions that were explicitly designed to use Junctions, or
ones which didn't declare their signature, and thus are making no
assumptions about what they are given, will perform the Junctive
evaluations.
This way, you get all the happy functionality, at the cost of typing
"use Junction;", and I get loads of protection against the evil side of
autothreading. Doing Junctions this way also makes the them extensible.
If someone figures out what a "not" junction is, they can add it in
later. And people like me can't complain about what the module is doing
to the language, because "all's fair if you predeclare" would be in effect.
>I think junctions are important at the statement level because they
>help make similar things look similar. Consider these two statements:
>
> if($foo == $bar) { .. }
> if(grep { $foo == $_ } $bar, $baz) { ... }
>
>What makes these two statements so fundamentally different from each
>other that they should be expressed in ways that *look* so different?
>
>
You mean besides the shift in plurality? A shift in plurality in English
forces you to modify a hefty portion of your sentence to accommodate it.
At a minimum, you change your verb, in this case the C< == >.
Shifting plurality in programming languages also incurs changes to the
code around it.
>>>: - Edge cases which, IMHO, do not merit the huffman level of several
>>>: single character operators. All of which can be accomplished without the
>>>: use of junctions, though not as gracefully.
>>>
>>>Grace is important. Even more important is mapping naturally to human
>>>linguistic structures, to the extent that it can be done unambiguously.
>>>
>>In my experience, English tends not to superimpose several values on a
>>given noun at once.
>>
>>
>
>No, but nor does it have a concept quite like a variable.
>
Which significantly weakens the "mapping naturally to human linguistic
structures" argument, IMO.
>Junctions are equivalent to the English sentence "Get eggs, bacon, and
>toast from the store". (In Perl, that'd be something like C<<
>$store->get("eggs" & "bacon" & "toast") >>.)
>
>
Or just have C< get() > take a list, and it's:
$store->get(<<eggs bacon toast>>); # is that the latest use of <<>>?
> It's just a bit of
>orthogonality that allows you to give "eggs, bacon, and toast" a name
>and use it later.
>
@shopping list = <<eggs bacon toast>>;
gives them a name you can use later, as well.
-- Rod Adams
>The need for junctions
>first became evident when we found ourselves filling the ~~ tables
>with various sorts of weird non-symmetries.
>
>
~~ can easily be called the "DWIM compare" operator. It even looks like
you're waving your hands, asking for some strange voodoo to happen. It
can also be thought of as "Here! take these two things, do something
with them, and tell me how it went". So it's magic central. And magic is
invariably a messy thing to implement.
And once the concept is out there, is makes sense to have practically
every combination of types do _something_ useful if fed to ~~. Which
makes it where ~~ is likely destined to be one of the most overloaded
operators in the history of computing. So be it. It's amazingly useful.
It also takes a monstrous amount of the Perl 6 DWIMery and puts it all
in one place.
Not to mention, you've already defined P6 to be good at this
multi-sub/method game, so take advantage of it. It's not like this table
will be represented all in one function.... (at least I hope not)
-- Rod Adams
Okay, I think your proposal is thinning. Perl is a dynamically typed
language. I have no doubts that people will continue to ignore the type
system even though it's there. I probably will.
Let me demonstrate something:
sub is_prime($x) {
my @primes;
for 2..sqrt($x) {
if $_ % none(@primes) == 0 {
push @primes, $_;
}
}
return 1 if $x % none(@primes) == 0;
}
Run through your mind how this would be done with a junction in $x.
Particularly focus on:
2..sqrt($x)
What the hell does that mean? Do you get a junction of lists out? Or
does sqrt die because it's not expecting a junction?
When I say:
if is_prime(any(@stuff)) {...}
I expect to run the codeblock if any of my stuff is prime. Instead I
get an error, always. I could always do this:
if @stuff.grep:{ is_prime($_) } {...}
But the whole point of junctions is to get rid of obscure expressions
like that. Brent makes a fantastic case here.
The point is that when you say "makes no assumptions", you're giving the
sub writers too much credit. I think a reasonable assumption
(especially for these alledged "novices" you keep talking about) that
these two code segments are equivalent:
if $x == 2 {...}
elsif $x == 3 {...}
And:
if $x == 2 {...}
if $x == 3 {...}
No matter what the value of $x.
Yet in the presence of junctions, they are not. Also note that this is
a practical example. I like the former, I know people who like the
latter. It's a matter of style, and it's one that will bite you if you
don't know about junctions.
So what's it going to be? Avoiding the evil side of autothreading, or
not crippling the usefulness of junctions in the presence of unwary
code? It is a trade-off indeed. Java would choose the former. Perl
is choosing the latter.
Luke
Why exactly? It's just the variable-nature of variables that isn't
exactly expressed linguistically, otherwise variables are just nouns
really.
> >Junctions are equivalent to the English sentence "Get eggs, bacon, and
> >toast from the store". (In Perl, that'd be something like C<<
> >$store->get("eggs" & "bacon" & "toast") >>.)
> >
> >
> Or just have C< get() > take a list, and it's:
>
> $store->get(<<eggs bacon toast>>); # is that the latest use of <<>>?
>
> >It's just a bit of
> >orthogonality that allows you to give "eggs, bacon, and toast" a name
> >and use it later.
> >
> @shopping list = <<eggs bacon toast>>;
>
> gives them a name you can use later, as well.
Except that you've introduced a definite ordering where one isn't
needed.
This whole analogy has me wishing for an Exegesis.
-Scott
--
Jonathan Scott Duff
du...@pobox.com
>Rod Adams writes:
>
>
>>>Junctions are intended to be used mainly within conditionals and other
>>>statements;
>>>
>>>
>>>
>>If the set of these "other statements" is limited, consider creating a
>>Junction class (which needs a "use Junction;" to activate), which
>>overloads the various comparison operators for when a Junction is
>>involved, and defines any/all/none/one as constructors? Throw in some
>>overloading or bindings for |/^/&, and it looks like you get
>>everything your asking for. Having a method evaluate: C< 5 ==
>>any(4,5,6) > means that the interpreter doesn't need to autothread.
>>This also makes it where only functions that were explicitly designed
>>to use Junctions, or ones which didn't declare their signature, and
>>thus are making no assumptions about what they are given, will perform
>>the Junctive evaluations.
>>
>>
>
>Okay, I think your proposal is thinning.
>
I'm trying to salvage what I can, since people seem to be rejecting
everything I offer.
> Perl is a dynamically typed
>language. I have no doubts that people will continue to ignore the type
>system even though it's there. I probably will.
>
>
So will I.
>Let me demonstrate something:
>
> sub is_prime($x) {
> my @primes;
> for 2..sqrt($x) {
> if $_ % none(@primes) == 0 {
> push @primes, $_;
> }
> }
> return 1 if $x % none(@primes) == 0;
> }
>
>Run through your mind how this would be done with a junction in $x.
>Particularly focus on:
>
> 2..sqrt($x)
>
>What the hell does that mean? Do you get a junction of lists out? Or
>does sqrt die because it's not expecting a junction?
>
>
What on earth does C< for (2..sqrt(3|5)) {...} > mean in the current
state of junctions?
But as written, yes, sqrt() would likely throw an exception under my
proposal.
>When I say:
>
> if is_prime(any(@stuff)) {...}
>
>I expect to run the codeblock if any of my stuff is prime. Instead I
>get an error, always. I could always do this:
> if @stuff.grep:{ is_prime($_) } {...}
>
>
Should also be able to overload Junction ~~ CodeRef to make this work:
if any(@stuff) ~~ &is_prime {...}
In this manner, you are explicitly controlling when and how the
autothreading happens. There is no way for is_prime to slurp up the
junction and pass it on in this case.
However, in the current scheme, if is_prime() is written to accept a
slurpy list of parameters (either by design, or just a habit from the P5
days), we can have:
sub is_prime {
my @primes;
for 2..sqrt(@_[0]) {
if $_ % none(@primes) == 0 {
push @primes, $_;
}
}
return 1 if $x % none(@primes) == 0;
}
Pushing the autothreading directly onto the C< for (2..sqrt(3|5)) {...}
>, which as you pointed out earlier, is almost definitely _not_ what
was wanted, whatever it means. (btw, My current analysis says it will
stop at the lowest value in the junction.)
I view it as a definite plus to make the autothreading explicit. I
doesn't need it to be ugly, just present.
In fact, that's all I need to shut up on this whole issue.
Something that says: "autothreading into functions only happens when you
slap (whatever) in there."
I can live with auto-autothreading over operators (I'd rather not,
though). But calling into a function needs something stronger. Yes,
operators are functions, too, but if you're fancy enough to be writing
you're own operators, you're fancy enough to take junctions into account.
Something like:
if is_prime(»any(@stuff)«) {...}
Would more than suffice. It says that you're not attempting to pass the
junction, but instead doing something in parallel, which is what the
hyper operators are all about, anyways.
It's been the side effects of autothreading that has been my big hold
up. If you make it happen explicitly, it's just another looping
construct ( a very odd one, but one nonetheless ). If it happens
implicitly, it's way too magical and dangerous for my tastes. Give me
that, and I'll probably start liking Junctions. I've never argued that
they weren't powerful.
>But the whole point of junctions is to get rid of obscure expressions
>like that. Brent makes a fantastic case here.
>
>The point is that when you say "makes no assumptions", you're giving the
>sub writers too much credit. I think a reasonable assumption
>(especially for these alledged "novices" you keep talking about) that
>these two code segments are equivalent:
>
> if $x == 2 {...}
> elsif $x == 3 {...}
>
>And:
>
> if $x == 2 {...}
> if $x == 3 {...}
>
>No matter what the value of $x.
>
>Yet in the presence of junctions, they are not. Also note that this is
>a practical example. I like the former, I know people who like the
>latter. It's a matter of style, and it's one that will bite you if you
>don't know about junctions.
>
>
You get into this problem when you allow any form of overloading of
built in operators, not just with junctions. It'd be trivial to create
an AlwaysTrue class, which overloads all forms of comparison against it
to always return true. The utility of such a class is left as an
exercise to the reader.
I am most specifically _not_ suggesting getting rid of operator
overloads. Just implicit autothreading.
>So what's it going to be? Avoiding the evil side of autothreading, or
>not crippling the usefulness of junctions in the presence of unwary
>code? It is a trade-off indeed.
>
>
I strongly suspect there is a middle ground to be had.
-- Rod Adams
sqrt() won't die; it gets threaded and returns a Junction, I would
expect. It's the lack of an &*infix:<..>(Int, Junction) function which
causes death
I suppose you could write one which would pick a random value:
multi sub *infix:<..>(Int $x, Junction $y) {
return $x .. first { .does(Int) } $y.values;
}
Ashley Winters
> On Fri, 18 Feb 2005 12:47:51 -0700, Luke Palmer <lu...@luqui.org> wrote:
>> Run through your mind how this would be done with a junction in $x.
>> Particularly focus on:
>>
>> 2..sqrt($x)
>>
>> What the hell does that mean? Do you get a junction of lists out? Or
>> does sqrt die because it's not expecting a junction?
>
> sqrt() won't die; it gets threaded and returns a Junction, I would
> expect. It's the lack of an &*infix:<..>(Int, Junction) function which
> causes death
Why does that cause death instead of authothreading?
Eirik
--
>Ever heard of .cshrc?
That's a city in Bosnia. Right?
(Discussion in comp.os.linux.misc on the intuitiveness of commands.)
>On Fri, Feb 18, 2005 at 12:42:31PM -0600, Rod Adams wrote:
>
>
>>>No, but nor does it have a concept quite like a variable.
>>>
>>>
>>>
>>Which significantly weakens the "mapping naturally to human linguistic
>>structures" argument, IMO.
>>
>>
>
>Why exactly? It's just the variable-nature of variables that isn't
>exactly expressed linguistically, otherwise variables are just nouns
>really.
>
>
And nouns either refer to single item, or a group of items as a whole.
Not one item that behaves like a chameleon.
When you say "The French", you are referring to the entire group of
people that associate themselves with the country of France. Not all the
people of France juxtaposed into one entity. If you want that, you say
"A Frenchmen" or "One of the French", but even then you are referring to
a single person, who happens to part of the group "The French". This
last is a pure Set membership question. The same "The French" can be
used as : "The French are invading!!". With a junction, you can build
something that says "any group of people from France", "one person from
France", "everyone from France", and even "not from France", but none of
them embody the concept that we think of as "The French".
If you were to have an embodiment of "The French" (as I proposed way
back in "Sets vs Junctions"), it is fairly easy to then create all of
the above junctions. Via the introspection that Junctions are being
defined with, it's possible to get that back out, but not smoothly.
So, linguistically, a noun is either a specific entity, or a group of
things, taken as a whole.
As Damian so emphatically stated earlier in this long and twisting
thread, a Junction is not a group of things, lopped together. It is one
single thing, that has several different values at once. In addition, it
also has a boolean function (any/all/one/none), declaring how all these
values relate when evaluated. The closest linguistic parallel to this
I've see is the double entendre, which doesn't even come close.
Sets and Junctions are two different things. They are related, but quite
different, and should not be confused.
>>>Junctions are equivalent to the English sentence "Get eggs, bacon, and
>>>toast from the store". (In Perl, that'd be something like C<<
>>>$store->get("eggs" & "bacon" & "toast") >>.)
>>>
>>Or just have C< get() > take a list, and it's:
>>
>>$store->get(<<eggs bacon toast>>); # is that the latest use of <<>>?
>>
>>
>>
>>>It's just a bit of
>>>orthogonality that allows you to give "eggs, bacon, and toast" a name
>>>and use it later.
>>>
>>shopping list = <<eggs bacon toast>>;
>>
>>gives them a name you can use later, as well.
>>
>>
>
>Except that you've introduced a definite ordering where one isn't
>needed.
>
>
Yes, well, people don't get to use that argument on me after repeatedly
saying that sets could be easily stored in arrays and/or hashes. I don't
remember if you, personally said it, but it felt like a consensus vote
from over here. One I don't really disagree with.
Not to mention the definite ordering only matters if you process it in a
way that it does.
In attempts to better explain:
$store->get("eggs" & "bacon" & "toast");
$store->get(<<eggs bacon toast>>);
say two radically different things. The first statement says: "Go to the
store and get Eggs. Go to the store and get Bacon. Go to the store and
get Toast. Save the results of each trip separately, and then when
someone asks how your trip (not trips) to the store went, you then see
if all the results are the same."
The second statement says: "Go to the store once. You have the following
shopping list: Eggs, Bacon, Toast. Was your trip successful?"
-- Rod Adams
Okay, I changed my mind. I think it does it does work for ~~ matching,
but not as an iterator
given 2 {
when 1 .. sqrt(3|6) { ... } # would work
}
for(1 .. sqrt(3|6)) { ... } # would fail, since Junction doesn't "do
Iterator" or whatever
1 .. sqrt(10) -> LazyList of (1..3)
1 .. sqrt(10|20) -> Junction of any(1,2,3, 1,2,3,4)
LazyList does Iterator, but Junction does not. You'd have to use (1 ..
sqrt(3|6)).values to iterate through the possible values
semi-randomly.
More likely, I'm nuts.
Ashley
Okay, changed my mind again.
1 .. sqrt(10|20) -> Junction of any(1,2,3, 1,2,3,4)
therefore, for(1 .. sqrt(10|20)) iterates ONCE, but $_ is a junction.
Anything inside the loop which uses $_ would autothread. Anything
which doesn't use $_ would only get called once. That's insane, right?
for(1 .. sqrt(10|20) {
if($_ > 2) {} # uses junctive value
say "Here"; # called once
bar($_); # calls bar() lots of times
}
> More likely, I'm nuts.
I'm definitely crazy. I give up! I'll stop now, since I clearly don't get it. :)
Ashley Winters
>On Fri, 18 Feb 2005 12:47:51 -0700, Luke Palmer <lu...@luqui.org> wrote:
>
>
>>Run through your mind how this would be done with a junction in $x.
>>Particularly focus on:
>>
>> 2..sqrt($x)
>>
>>What the hell does that mean? Do you get a junction of lists out? Or
>>does sqrt die because it's not expecting a junction?
>>
>>
>
>sqrt() won't die; it gets threaded and returns a Junction, I would
>expect. It's the lack of an &*infix:<..>(Int, Junction) function which
>causes death
>
In the context this question was posed, the proposal I was making,
sqrt() would throw an exception. But that's likely not what this
discussion is about.
In the terms of junctions as defined, I expect that it would stop at the
lowest value of $x greater than or equal to 4. Unless we start allowing
junctive lists as well as junctive scalars...
-- Rod Adams.
P5/PDL has some junction-like aspects, in that it does autothreading.
We sidestep the issue by not allowing multiple-element PDLs in boolean
comparisons, but treating single-element PDLs as scalars whenever possible.
This flows down to '..', so (1..pdl(3)) returns (1,2,3) or does the Right
Thing in scalar context -- but (1..pdl(3,4)) probably ought to throw an
exception (it actually returns the empty set or always-false, depending on
context).
I'm no language designer, but it seems to me that certain operators simply
won't generalize well to the autothreaded case, and those operators should
simply try to pick up the pieces -- ie treat single-element junctions like
ordinary scalars, and treat empty junctions like undef values, but otherwise
do something exceptional (whether or not that involves throwing an actual
exception). My personal preference is that constructs like 1..($a|$b) should
be disallowed. It's certainly easy enough to expand the junction manually
into a list and loop over that.
Note that other weird constructs in perl5, such as (1..[2,3]), do very
surprising, architecture-dependent things.
Quoth Ashley Winters on Friday 18 February 2005 04:09 pm,
>Hmmm... It seems that this way does lie madness -- there's a fundamental
>ambiguity between autothreading happening inside or outside the declared
>loop, and there's no "least surprising" way to implement it. Certainly
>inside the loop is the easiest and most natural to implement, but that acts
>strange from an API standpoint. Outside the loop works well from an API
>standpoint but introduces all sorts of weird cases that the implementation
>has to consider.
>
>
I would think that my latest idea of turning off "autothreading" per se, and replacing it with an explicit "threading" call/operator, would solve this dilemma. The idea was specifically to have it where threading happened on function calls when a junction was surrounded by hyper operators. Otherwise, the junction is passed "as is".
So we still get:
if $x == 3|4|5|6 {...}
would thread over infix:<==> without any funkiness, since we'll assume operators are well written, and can take junctions as parameters, same as:
if is_prime(3|4|5|6) {...}
Would pass the junction to is_prime, to do with as it pleases, including throwing an exception. However,
if is_prime(»3|4|5|6«) {...}
would thread over is_prime, and collate the results outside of call.
These semantics also give us the ability to easily mix and match what we send to a function, so we can say:
if funky_test(all(@A), »any(@B)«) {...}
Basically I'm putting all the power of threading into the hands of the caller.
I will further argue that C< »junction« > should not short circuit. It should do the simple brute force "try all values" approach, since the side effects are likely desired. Operators, which should be able to accept junctions as parameters, are encouraged to short circuit, since there are no side effects possible for the evaluation of that operator. By the time the operator gets called, it should have all the parameters it needs, and there are no more side effects to be had.
As for C< .. >, I'd say that it should handle junctions being fed into it by throwing an exception.
-- Rod Adams
So basically you're proposing that, rather than do one implicit loop
that'll probably do what you want, the default should be to do an
unknown number of implicit loops in somebody else's code, and you have
to ask explicitly for the more sensible behavior. Somehow this
doesn't strike me as an improvement.
> These semantics also give us the ability to easily mix and match what we send to a function, so we can say:
>
> if funky_test(all(@A), »any(@B)«) {...}
sub funky_test ( Junction|Any @a, @b ) { ... }
> Basically I'm putting all the power of threading into the hands of the caller.
The caller is not in a position to know if the callee is internally
structured in such a way that passing in a raw junction makes sense.
The right place to say "I can handle a junction, don't autothread" is
in the callee; that's the behavior @Larry is proposing.
> As for C< .. >, I'd say that it should handle junctions being fed into it by throwing an exception.
Why is this more sensible than returning a list of junctions in list
context and a junction of arrayrefs in scalar context? (I believe
infix:<..> will return an arrayref in scalar context, though I could
be wrong.)
(The array of junctions is negotiable, by the way; whatever it is,
though, it should probably be the same as the default meaning for list
returns from an autothreaded function.)
In the current state of junctions, the autothreading is done at the
level of the call to do_prime, so $x is never a junction. Only under
your notion of junctions as just another object with no autothreading
until the operator level will $x ever be a junction.
But if it somehow *did* become a junction, I would imagine something
like this would happen:
for (2 .. sqrt( 3 | 5 )) { ... }
for (2 .. ( sqrt 3 | sqrt 5 )) { ... }
for ( ( 2 .. sqrt 3 ) | ( 2 .. sqrt 5 ) ) { ... }
for ( 2 .. sqrt 3 ) { ... } | for ( 2 .. sqrt 5 ) { ... } #notionally
However, it's clear that the last step doesn't make a whole lot of
sense, since C<for> has no return value. Maybe C<for> would be
declared with a signature that didn't allow junctions at all.
> However, in the current scheme, if is_prime() is written to accept a
> slurpy list of parameters (either by design, or just a habit from the P5
> days), we can have:
I will readily admit that the behavior of junctions in a slurpy
subroutine call is suboptimal, and it might be a good idea to
reexamine it. However, I will also point out that most newbie
programmers probably won't use the @_ behavior, and likely won't be
using slurpy parameters either, while more experienced programmers
will know better.
>Rod Adams <r...@rodadams.net> wrote:
>
>
>>Luke Palmer wrote:
>>
>>
>>> 2..sqrt($x)
>>>
>>>What the hell does that mean? Do you get a junction of lists out? Or
>>>does sqrt die because it's not expecting a junction?
>>>
>>>
>>>
>>What on earth does C< for (2..sqrt(3|5)) {...} > mean in the current
>>state of junctions?
>>
>>
>
>In the current state of junctions, the autothreading is done at the
>level of the call to do_prime, so $x is never a junction.
>
Actually, if one writes is_prime to have slurpy array passing a la
Perl5, as I wrote in a different post, $x most certainly is a junction
at this point.
Although this does beg the question: If by default one can't assign a
junction to a variable, does this apply only in cases with an assignment
operator, or does it also happen on parameter passing?
I've just reread Larry's post about normally disallowing storage of
junctions in a lvalue. He covers the point about declaring parameters
that are junctional, and the parameters are otherwise non-junctional.
What is not clear here is whether or not the S09 talk about if a
junction is inside another container (like a slurpy array), if it gets
caught or not. The array itself is not a junction, but one or more of
it's elements might be. So do we have to walk the entire reference tree
if we are given a complex data type, looking for junctions? This is not
an option, think no further than lazy lists to see why. So the other
option is to have a runtime check, and as soon as a memory store
containing a junction is encountered, then throw an exception. Even if
this happens well after the parameter passing step. Hmm. Messy, but
possible.
> Only under
>your notion of junctions as just another object with no autothreading
>until the operator level will $x ever be a junction.
>
>
For the record, I've withdrawn that proposal.
All I want now is for autothreading to be explicit.
>But if it somehow *did* become a junction, I would imagine something
>like this would happen:
>
> for (2 .. sqrt( 3 | 5 )) { ... }
> for (2 .. ( sqrt 3 | sqrt 5 )) { ... }
> for ( ( 2 .. sqrt 3 ) | ( 2 .. sqrt 5 ) ) { ... }
> for ( 2 .. sqrt 3 ) { ... } | for ( 2 .. sqrt 5 ) { ... } #notionally
>
>However, it's clear that the last step doesn't make a whole lot of
>sense, since C<for> has no return value. Maybe C<for> would be
>declared with a signature that didn't allow junctions at all.
>
>
Or is it the case that C< .. > acts a bit like the old flip flop it
used to be, and stop at the first true value it sees?
AFAIK, the concept of Junctional Lists has not been created yet, so what
else would it return? A list of junctions? all of which are ($x|$x) or
($x|undef)?
>>However, in the current scheme, if is_prime() is written to accept a
>>slurpy list of parameters (either by design, or just a habit from the P5
>>days), we can have:
>>
>>
>
>I will readily admit that the behavior of junctions in a slurpy
>subroutine call is suboptimal, and it might be a good idea to
>reexamine it. However, I will also point out that most newbie
>programmers probably won't use the @_ behavior, and likely won't be
>using slurpy parameters either, while more experienced programmers
>will know better.
>
>
Except for the absolutely massive amount of Perl5 code out there that
will be hastily translated by people who don't trust the Perl6 <-> Ponie
interplay, and the flocks of Perl5 programmers coming over without
learning all the new features of their upgraded language, you're
probably right.
-- Rod Adams
>Rod Adams <r...@rodadams.net> wrote:
>
>
>> if $x == 3|4|5|6 {...}
>>
>>would thread over infix:<==> without any funkiness, since we'll assume operators are well written, and can take junctions as parameters, same as:
>>
>> if is_prime(3|4|5|6) {...}
>>
>>Would pass the junction to is_prime, to do with as it pleases, including throwing an exception. However,
>>
>> if is_prime(»3|4|5|6«) {...}
>>
>>would thread over is_prime, and collate the results outside of call.
>>
>>
>
>So basically you're proposing that, rather than do one implicit loop
>that'll probably do what you want, the default should be to do an
>unknown number of implicit loops in somebody else's code, and you have
>to ask explicitly for the more sensible behavior. Somehow this
>doesn't strike me as an improvement.
>
>
What I'm asking for is to be in control of how often I get the possible
side effects of calling a function. If I make a call to a DBI method to
go insert a record, I want exactly 0 (on error) or 1 (on success)
records inserted into my database, _unless_ I told it do otherwise.
Since a junction is a single item, it should produce a single effect
_unless_ I tell it to expand the values and act like several items at once.
Simply put, I want control over my side effects.
As for whether passing the junction as a junction or passing the
junction via autothreading should be the default:
Junctions exists. I want to say "stay a junction" or "explode into
pieces, go your separate ways, and then come back". In one case I'm
doing nothing to the junction, in the other case I'm doing quite a bit.
So it makes sense that the latter is something additional to the former.
Taking into account how common that particular action might be, I chose
to propose a rather short modification to it, that still clearly said
something special was going on here. One could say I'm just extending
the hyper operator from "do this operator several times" to "do this
expression several times".
"More sensible behavior" is entirely subject to the exact instance at
hand. In the case of let's say C< == >, I want to feed it the raw,
unexploded junction.
I well written C< == > should be able to apply all kinds of
optimizations to the task at hand, and provide much more sensible results.
I expect calls with operators to be considerably more common than
function calls w/ junctions.
So if I grant you your default of autothreading, we then get:
if $x == »3|4|5« {...}
given $x {
when »3|4|5« {...}
}
Which seemed far more suboptimal than the other way around, IMO.
>>These semantics also give us the ability to easily mix and match what we send to a function, so we can say:
>>
>> if funky_test(all(@A), »any(@B)«) {...}
>>
>>
>
>sub funky_test ( Junction|Any @a, @b ) { ... }
>
>
Unless you have a junction, and you wanted it to autothread on the first
parameter, rather than be passed in, in order to get some perceived
desirable side effect out of funky_test that passing it a Junction would
only give a subset of.
Suppose funky_test is a derivative of C< printf >. Only this printf
let's you feed it a junction for the format, and it will sort through
them and see which one matches best depending on actual number of
parameters, parameter types, etc. Ordinarily, this would be fairly cool
stuff.
But let's say you instead wanted it to print the same data with all the
different formats. With my calling, you could make the distinction
rather easily. With yours, you have to set up the looping yourself.
>>Basically I'm putting all the power of threading into the hands of the caller.
>>
>>
>
>The caller is not in a position to know if the callee is internally
>structured in such a way that passing in a raw junction makes sense.
>
>
Sure they are. It's called reading the documentation. If it doesn't say
it can handle junctions, it probably can't.
>The right place to say "I can handle a junction, don't autothread" is
>in the callee; that's the behavior @Larry is proposing.
>
>
I'm sending the same message. Only my message goes to the programmer,
not the runtime.
I'm also making it where the decision to insert autothreading code or
not is made at compile time, not runtime.
>>As for C< .. >, I'd say that it should handle junctions being fed into it by throwing an exception.
>>
>>
>
>Why is this more sensible than returning a list of junctions in list
>context and a junction of arrayrefs in scalar context? (I believe
>infix:<..> will return an arrayref in scalar context, though I could
>be wrong.)
>
>(The array of junctions is negotiable, by the way; whatever it is,
>though, it should probably be the same as the default meaning for list
>returns from an autothreaded function.)
>
>
About the only thing is could return would be a lazy list of junctions.
And about the only places C< .. > gets used is inside C< for > and
inside array slice indices. Lists of junctions certainly make no sense
in a for loop... the loop cannot simultaneously exit and not exit at the
same time. Feeding a list of junctions into an array slice index is
asking for what? Another list of junctions out? I just don't see either
of those making sense. But if you really want it to do that, I can live
with it.
-- Rod Adams
> All I want now is for autothreading to be explicit.
It already *is*.
The only way that:
is_prime($x)
can ever autothread is if $x holds a junction. But that can now only happen if
there's an explicit C<use junctions> in scope where $x was assigned to (or the
explicit use of some other module that also activates C<use junctions>). So $x
having a junction must be a known possibility at that point.
Of course, literal junctions *will* autothread in all circumstances:
is_prime(any(6,7,8))
is_prime(6|7|8)
But they're both explicit too: you're explicitly using junction constructors,
so the autothreading can hardly come as a surprise.
And if:
is_prime($x)
does happen to autothread when you weren't expecting it to, then one of two
things will happen. Either the subroutine will be 'pure' in which case there's
no problem in autothreading it; or else the subroutine will have side effects,
in which case you'll get an explicit warning when the autothreading occurs.
Junctions are just another type of scalar value. All the arguments I hear
against that seem to mirror the arguments we always hear against Perl scalars
being dynamically typed at all: but how will you know whether $x has a number
or a string or a reference in it??? And of course the answer is: most people
know most of the time, simply by paying attention to the data flow of their
program. And when they occasionally mess up and accidentally give a function
or subroutine the wrong kind of value, Perl usually warns them about it.
Personally, I think it's completely fascist to require a C<use junctions>
pragma in order for junctions to even be stored in variables. It's as bizarre
as requiring C<use strings> or C<use references> or C<use undef> or C<use
infinities> would be. Yes, it *is* possible to get unexpected behaviour from
passing a junction where it isn't expected, but it's already possible to get
unexpected behaviour by passing a string or an undef or a reference or an
object where it isn't expected. Junctions are nothing new in that respect.
I think junctions ought to be first class scalar data types and not have to
ask permission before they can even be assigned. If you want to be sure that a
particular variable doesn't have a junction in it, you should have to specify
that, in exactly the same way you have to be explicit in order to prevent a
variable storing a string or a reference or an object...by giving it an
explicit type:
my Num $x; # Can't store a string or reference
my Ref $y; # Can't store a number or string
my Nonjunctive $x; # Can't store junctions
And where did C<Nonjunctive> come from? I created it. Ironically, by using the
Awesome Power of Junctions:
type Nonjunctive ::= none(Junction);
And if typing a variable to prevent it storing a junction is too onerous, it
would still be perfectly sufficient to simply provide a C<no junctions>
pragma, under whose geas no junction shall be suffered to live.
Look, I do understand the arguments in the other direction. I've understood
them for the past five years that I've been developing the concept of
superpositional data-types. I've thought them through numerous times myself,
and in the end: I just don't buy them.
The whole point of junctions is to make the threading of operations on
datasets both automatic and implicit; to make it Do The Right Thing without
the hassles of explicit threading. If you don't want that, that's fine: just
don't use junctions. Use arrays and hyperoperators instead. And we'll happily
give you a C<no junctions> pragma so you can be emphatic about not wanting them.
But bowdlerizing the concept of junctions isn't the answer.
Damian
I would argue that this function is badly designed.
Junctions are intended to ultimately be used in boolean tests. That's
why the values of the junction have an any/all/one/none relationship.
The proper data structure here is an array. (Actually, ironically
enough, it's probably a set, not an array.)
IMHO, there are three cases where it's legitimate to specifically ask
for junctions to be passed in:
1. When it makes absolutely no sense to use a junction in a particular
function, especially one with side effects.[1]
2. When doing some sort of junction-aware serialization, a la Storable
or Data::Dumper.
3. When the author of the function can optimize the way junctions are
handled without changing their basic semantics.
Outside of these cases, it's probably wrong.
A junction is *not* a meaningless data structure to be slung around at
will; it is a construct that implies a certain type of behavior when
used.
> > The caller is not in a position to know if the callee is internally
> > structured in such a way that passing in a raw junction makes sense.
>
> Sure
> they are. It's called reading the documentation. If it doesn't say it can
> handle junctions, it probably can't.
I don't want to have to stop in the middle of a hundred-line function
to think, "Does Store::Market.get act sanely when I give it a
junction? Do I need to explode it manually, or will it handle the
junction nicely on its own?" Nor do I want to work in a language
where most functions don't handle a basic construct elegantly, even
when they can.
> About the only thing is could return
> would be a lazy list of junctions. And about the only places C< .. > gets
> used is inside C< for > and inside array slice indices. Lists of junctions
> certainly make no sense in a for loop... the loop cannot simultaneously exit
> and not exit at the same time.
You don't think this makes sense? (C<for> itself wouldn't autothread;
the calls and operators inside the loop body would.)
for 1|2, 3|4, 5|6 { ... }
(Actually, Synopsis 3 talks about what happens when you give C<for> a
single junction--essentially, it loops over the contents of the
junction in whatever order it feels is most optimal. But in this case
we're giving it an array of junctions, which presumably would act
differently)
> Feeding a list of junctions into an array
> slice index is asking for what? Another list of junctions out?
Synopsis 3:
print if @foo[any(1,2,3)]
[1] Note, however, that this needs to be done carefully. For example,
while it doesn't really make sense for the string arguments of a call
to C<print> to be junctions, the object argument is another matter
entirely:
#!/usr/bin/perl6
# naive tee
$OUT=$OUT & open("> $_") for @ARGS;
print or die "Can't write to $!.filename: $!" for *$IN; # Or
however it's done this week
That was certainly not my intent and I apologize if that's how it appeared. I
genuinely respect the contributions of every person on this list, and even
when (as now) I strenuously disagree with the ideas expressed, I know that
those contributions are sincere and offered with the best interests of Perl at
heart.
I still stand by every point I made in that last message, but I'm sorry that I
let my frustrations leak into the discussion.
Damian
It's one set for any()/all()/one() and two sets for none(). Of course,
if something (eg. functions) cannot be tested for equality, then we'll
have to assume them to be unique from each other anyway, in which case
junctions do act as arrays.
> [1] Note, however, that this needs to be done carefully. For example,
> while it doesn't really make sense for the string arguments of a call
> to C<print> to be junctions, the object argument is another matter
> entirely:
> #!/usr/bin/perl6
> # naive tee
> $OUT=$OUT & open("> $_") for @ARGS;
> print or die "Can't write to $!.filename: $!" for *$IN; # Or
> however it's done this week
I think it's an unary "=" this week, according to S04:
print for =*$IN;
Thanks,
/Autrijus/
>Rod Adams <r...@rodadams.net> wrote:
>
>
>>>The caller is not in a position to know if the callee is internally
>>>structured in such a way that passing in a raw junction makes sense.
>>>
>>>
>>Sure
>>they are. It's called reading the documentation. If it doesn't say it can
>>handle junctions, it probably can't.
>>
>>
>
>I don't want to have to stop in the middle of a hundred-line function
>to think, "Does Store::Market.get act sanely when I give it a
>junction? Do I need to explode it manually, or will it handle the
>junction nicely on its own?"
>
You call functions where you don't know what data types they are
expecting? That's... surprising.
Even in a loosely typed world like Perl, knowing what a sub or method is
expecting to be fed seems like a good idea to me. I see checking for
accepting junctions as input as being on the same level as "Does it want
a list or an arrayref here?".
When I'm writing my own insanely large functions, I'm constantly hitting
the docs to see the nuances about the method calls I'm not already
intimately familiar with. And if I'm going to attempt to use a function
in a new way (like feeding it a Junction), I recheck the docs to make
sure I'm not setting myself up for trouble down the road.
Your mileage may vary.
-- Rod Adams.
(PS - This should not be construed to be an attack on you or your
programming style. It is not. Though I've never seen you in action, I
have every reason to believe you are a fully competent developer. It is
simply a response to your statement above, explaining why I thought the
attitude expressed there represented a weak argument.)
> Rod Adams wrote:
>
>> All I want now is for autothreading to be explicit.
>
>
> It already *is*.
>
> The only way that:
>
> is_prime($x)
>
> can ever autothread is if $x holds a junction. But that can now only
> happen if there's an explicit C<use junctions> in scope where $x was
> assigned to (or the explicit use of some other module that also
> activates C<use junctions>). So $x having a junction must be a known
> possibility at that point.
>
> Of course, literal junctions *will* autothread in all circumstances:
>
> is_prime(any(6,7,8))
>
> is_prime(6|7|8)
I had not caught the difference between:
use junctions;
$x = 6|7|8;
if is_prime($x) {...}
and
if is_prime(6|7|8) {...}
before. Is this new, or yet another important detail I missed along the
way? Or is this a side effect of not being able to store a Junction, and
can go away if C< use Junctions > is turned on?
>
> But they're both explicit too: you're explicitly using junction
> constructors, so the autothreading can hardly come as a surprise.
*If* we are guaranteed than an explicitly created junctions will always
autothread, I'll agree with this.
I will, however, question if this is optimal. Compare two simple cases:
C< $x == any(4,5,6) > and C< $x < all(4,5,6) >. Both of them are prime
candidates to some optimizations, but the optimizations are likely
rather different. If we pass the junction into the operator, then it can
perform some custom tailored code, to make things much more efficient,
instead of relying on a more generalized junction optimizer to handle
things.
What this also means is that if you wish to pass an anonymous junction,
you can't. You have to do something like:
{
use junctions;
some_func(my $x = any(4|5|6));
}
Which just seems silly.
>
> And if:
>
> is_prime($x)
>
> does happen to autothread when you weren't expecting it to, then one
> of two things will happen. Either the subroutine will be 'pure' in
> which case there's no problem in autothreading it; or else the
> subroutine will have side effects, in which case you'll get an
> explicit warning when the autothreading occurs.
I addressed earlier concept of how does perl know when there are side
effects, particularly with the execution path can weave to parts written
in pure-parrot. In particular, if the Patrick responded by implying
that there was no such side effect protection. see:
http://www.nntp.perl.org/group/perl.perl6.language/19210 (my post)
http://www.nntp.perl.org/group/perl.perl6.language/19212 (Patrick's
response)
I see your statements on the subject, and Patrick's to be at odds. But
then again, it might be that I've misread something again, though I'm
doing my best to avoid it now.
I request some clarification on this. If nothing else, to make sure you
and Patrick have the same understanding of what's happening.
This problem goes away completely with explicit autothreading. perl
would no longer be making assumptions about what to autothread, and what
to carp over.
> Personally, I think it's completely fascist to require a C<use
> junctions> pragma in order for junctions to even be stored in
> variables. It's as bizarre as requiring C<use strings> or C<use
> references> or C<use undef> or C<use infinities> would be. Yes, it
> *is* possible to get unexpected behaviour from passing a junction
> where it isn't expected, but it's already possible to get unexpected
> behaviour by passing a string or an undef or a reference or an object
> where it isn't expected. Junctions are nothing new in that respect.
I had it in my head that if I were to get my »Junction« explicit
threading idea, I was going to follow up by saying the block against
storing junctions was a case of diminishing returns at that point, and
should probably go away. I appreciate the stop-gap measure that it was,
but I'd prefer to solve the real problem at hand.
>
> Ironically, by using the Awesome Power of Junctions:
I hope I never gave the impression that I felt Junctions were not
powerful... That was not the case. If anything, I was arguing that they
were *too* powerful... But in the end, I realized it's just the implicit
autothreading I didn't like.
>
> Look, I do understand the arguments in the other direction. I've
> understood them for the past five years that I've been developing the
> concept of superpositional data-types. I've thought them through
> numerous times myself, and in the end: I just don't buy them.
>
> The whole point of junctions is to make the threading of operations on
> datasets both automatic and implicit; to make it Do The Right Thing
> without the hassles of explicit threading. If you don't want that,
> that's fine: just don't use junctions. Use arrays and hyperoperators
> instead. And we'll happily give you a C<no junctions> pragma so you
> can be emphatic about not wanting them.
I can certainly understand the "hassles of explicit threading" if one is
thinking:
$y = func(any(3|4|5));
has to be explicitly written as:
$y = any(any(3|4|5).values().map(func($_)));
or some such, but is it really a hassle to say:
$y = func(»any(3|4|5)«);
instead? I'm proffering up some pretty heavy syntactic sugar here to
make explicit threading really easy.
As you stated earlier, one typically knows what they've got as they're
writing code. I'll proffer that in addition, people generally know what
a function can take, or not take (or they can read the docs to find
out), when they call it.
Coders also know what kind of side effects a given call is expected to
generate. Those side effects are probably why they're calling it in the
first place. They also know if they are expecting the language to thread
over the function, or pass the junction along. All of this knowledge,
which comes almost innately during the process of writing code, can
become almost Halting Problem hard for the compiler or runtime to figure
out.
What I'm offering is a way for the programmer to impart this innate
knowledge onto the language, in what I consider a fairly non-obstructive
fashion. In doing so, I believe I've resolved all the concerns over
"magical side effects" and "unexpected results". If the programmer is in
that innate knowledge, there are almost definitely other errors in their
code, that no amount of "Do the Right Thing" or "Do What I Mean" will
resolve.
>
> But bowdlerizing the concept of junctions isn't the answer.
I'm no longer doing that.
Or at least, I'm no doing anything anywhere close to as extreme as some
of my other ideas over the last week or two. Which I can see perfectly
well, in retrospect, how you felt I was gutting the power away from
junctions, and how frustrating that must have been for you.
In dissecting junctions left and right, trying to get rid of them, I did
come to a pretty good appreciation of what they can do. And overall, I
like them now. They are very weird, and likely to be misunderstood more
often than not, but overall they add some interesting properties to the
language. (I still argue that they are not supported by linguistics,
it's Sets that are supported that way, but _that's_ a different topic
than what form junctions should take, if any)
But I still don't like implicit autothreading, and likely never will. I
don't know how to explain it, but it just feels very wrong. It's down
there with using typeglobs to pass filehandles, which is thankfully
history.
-- Rod Adams
Instead of primary sigils, what about secondary sigils on an array to
mark it as an unordered set?
@|foo = any
@&foo = all
@^foo = one # can arrays be curried arguments? hmm
@!foo = none
After all, why should scalars get all the good secondary sigils? :)
Ashley Winters
Well, I for one, never took any offense to any of the responses sent my
way. And I appreciate the patience it's likely taken to not just
completely Warnock me.
However, I also realize that I might have stepped on some toes of the
course of this long discussion. Which was never my intention, and I'll
apologize to any who feel I've slighted them in the process.
I do believe everyone on this list shares the same goals of making Perl
6 the possible language that it can be. However, opinions will vary as
to what that actually means. Being a group of people that can by and
large be described as having a fairly large egos, these differences of
opinion can become rather passionate. And passion leads to some pretty
extreme responses.
It certainly hasn't helped matters that the exact nature of my proposal
has changed in some fairly drastic ways on a regular basis, as I came to
a better understanding of what Junctions were, and how they were being
implemented. I apologize for any confusion this may have caused, but I
do think the resulting discussions have shed some new insights on
Junctions, Sets, what it means to have a sigil, why junctions can't just
be another class, and several other topics. Poor Mr. Fowles is likely
having nightmares figuring out how to summarize all of this.
Positions I still stand by:
- Sets belong in the language, and need more support. This can likely be
done at the module level, but I'd like them better incorporated,
preferably with their own sigil. However, I believe they can peacefully
coexist with Junctions, and one concept does not need to crowd out the
other.
- Implicit autothreading is a Bad Thing, and should not happen. This is
almost entirely due to the side effects such behavior can generate. To
keep the power of junctions viable, explicit threading should be
trivially easy, but it should be explicit, none the less.
-- Rod Adams
> I had not caught the difference between:
>
> use junctions;
> $x = 6|7|8;
> if is_prime($x) {...}
>
> and
>
> if is_prime(6|7|8) {...}
There isn't one.
> Is this new, or yet another important detail I missed along the way?
> Or is this a side effect of not being able to store a Junction, and can go
away
> if C< use Junctions > is turned on?
Yes, it's a side-effect of the new default prohibition on junction assignments
(though I'm still working hard to convince everyone that that prohibition
cripples junctions and that having to <use junctions> before you can assign a
basic Perl 6 scalar datatype to a variable is an abomination).
> I will, however, question if this is optimal. Compare two simple cases:
> C< $x == any(4,5,6) > and C< $x < all(4,5,6) >. Both of them are prime
> candidates to some optimizations, but the optimizations are likely rather
different.
> If we pass the junction into the operator, then it can perform some custom
tailored code,
> to make things much more efficient, instead of relying on a more
generalized junction
> optimizer to handle things.
Sure. That's why we have the ability to specify subroutines where junctive
args are *not* autothreaded, by typing the corresponding parameter as taking a
junction:
multi sub infix:«==» (YourType $x, Junction $y) is symmetrical {...}
multi sub infix:«<» (YourType $x, Junction $y) is symmetrical {...}
These two multisubs can optimize for junctive arguments to their hearts' content.
> I addressed earlier concept of how does perl know when there are side effects,
> particularly with the execution path can weave to parts written in
pure-parrot.
> In particular, if the Patrick responded by implying that there was no such
side
> effect protection. see:
>
> http://www.nntp.perl.org/group/perl.perl6.language/19210 (my post)
> http://www.nntp.perl.org/group/perl.perl6.language/19212 (Patrick's response)
I don't see that Patrick's response implies that at all. In fact, I think his
statement that:
>> Well, the ultimate answer is that both Dan and Patrick (and others)
>> will negotiate the exact interface when we get to that point, and
>> that we don't seem to be too concerned about it at the moment.
>> (It could just be that we're both burying our heads in the sand
>> hoping it'll be magically "solved" by the other. :-)
>>
>> However, in working out these examples I'm fairly comfortable that
>> it can be made to work at the Perl 6 compiler level if need be,
>> although it will probably be a lot more efficient if we can find a
>> way to do it within Parrot.
seems to confirm that detecting and reporting autothreaded side-effects is
entirely possible.
> If nothing else, to make sure you and Patrick have the same understanding of what's happening.
I'm sure we will.
> This problem goes away completely with explicit autothreading.
> perl would no longer be making assumptions about what to autothread, and
what to carp over.
But that's the whole point of junctions! Namely that perl works it out for
you. If you don't want that to happen then don't use junctions (and don't
allow them to be used, by specifying C<no junctions>).
If you want explicit threading (I refuse to call it "autothreading"; if it has
to be manually specified, it certainly isn't "auto") then use arrays instead,
and thread your subroutines and operators over them using the explicit
hyperoperator notation (see example below).
>> But bowdlerizing the concept of junctions isn't the answer.
>
> I'm no longer doing that.
>
> Or at least, I'm no doing anything anywhere close to as extreme as some
> of my other ideas over the last week or two. Which I can see perfectly
> well, in retrospect, how you felt I was gutting the power away from
> junctions, and how frustrating that must have been for you.
Thank-you for understanding that.
> But I still don't like implicit autothreading, and likely never will. I
> don't know how to explain it, but it just feels very wrong. It's down
> there with using typeglobs to pass filehandles, which is thankfully
> history.
I understand your qualms, even if you can't nail down the exactly reasons for
them.
However, I still disagree with them. I truly believe that junctions (including
their autothreading behaviour) ought to be core to Perl 6...and not ham-strung
in any way.
I appreciate that some people will not like the potential autothreading of:
if is_prime($x) {...} # Might possibly autothread
but I think it's sufficient to give those people:
# At the top of the program...
no junctions;
# and then...
if is_prime($x) {...} # Can't possibly autothread
As for explicit threading: hey, you've already got it. Just use an array
instead of a junction, and only allow explicit junctives. Either:
# At the top of the program...
no junctions 'assignment'; # Junctive constants okay
# and then...
if is_prime(any(@x)) {...} # Explicitly threaded
or, even more thread-explicitly:
# At the top of the program...
no junctions 'assignment'; # Junctive constants okay
# and then...
if any is_prime«(@x) {...} # Explicitly threaded
Damian
It's a side effect of not being able to store a junction.
> I will, however, question if this is optimal. Compare two simple cases:
> C< $x == any(4,5,6) > and C< $x < all(4,5,6) >. Both of them are prime
> candidates to some optimizations, but the optimizations are likely
> rather different. If we pass the junction into the operator, then it can
> perform some custom tailored code, to make things much more efficient,
> instead of relying on a more generalized junction optimizer to handle
> things.
There's nothing to *prevent* us from passing a junction into a routine
that perform custom tailored code for the junction--it's just not
the default. If &infix:«<» wants to optimize for junctions, we define
it with something like
multi sub *infix:«<»(Junction $x, Junction $y) returns bit { ... }
and it can then optimize appropriately. Autothreading routines
by default occurs only when the junction arguments are bound to scalar
parameters that are inconsistent with the Junction type (S09).
> >And if:
> > is_prime($x)
> >does happen to autothread when you weren't expecting it to, then one
> >of two things will happen. Either the subroutine will be 'pure' in
> >which case there's no problem in autothreading it; or else the
> >subroutine will have side effects, in which case you'll get an
> >explicit warning when the autothreading occurs.
>
> I addressed earlier concept of how does perl know when there are side
> effects, particularly with the execution path can weave to parts written
> in pure-parrot. In particular, if the Patrick responded by implying
> that there was no such side effect protection. see:
>
> http://www.nntp.perl.org/group/perl.perl6.language/19210 (my post)
> http://www.nntp.perl.org/group/perl.perl6.language/19212 (Patrick's
> response)
>
> I see your statements on the subject, and Patrick's to be at odds. But
> then again, it might be that I've misread something again, though I'm
> doing my best to avoid it now.
AFAICT there aren't any conflicts between my and Damian's statements.
The example given in the 19210 post supposed that a Junction would
get passed to a "pure Parrot" function, but didn't actually provide
code to demonstrate that. Any "pure Parrot" routine declared as
accepting scalars inconsistent with Junctions would be autothreaded
the same as any other routine.
Assuming that a Junction *is* passed to a "pure Parrot" routine
(e.g., via a list or other data structure), and assuming that
Parrot is not itself supporting autothreading, then it's fairly
straightforward to define the Junction type such that requests
to stringify, numify, or otherwise access/evaluate the Junction
as a non-Junctional value will throw an exception.
> All of this knowledge, which comes almost innately during the
> process of writing code, can become almost Halting Problem hard
> for the compiler or runtime to figure out.
So far I haven't seen anything about Junctions that requires an
awful lot of knowledge, guessing, or exhaustive evaluation on
the part of the compiler/runtime system. They will likely need
to do a bit of checking of the arguments to decide when to
autothread a call, but they're going to be doing other sorts
of argument checking anyway, and the additional checking to be
performed is fairly shallow -- just testing each argument
for isa(Junction) or its equivalent. (We don't have to perform
deep inspection of arrays or the like.)
Pm
I should've included this in my previous post...and write it now
simply to promote clarity/completeness:
My response in #19212 was intended to show that the side-effect
problems being described don't exist, and therefore there isn't
any need for "side-effect protections" such as the ones being
proposed. I didn't mean to imply that they would be needed but
not available.
Pm
Personally, I've found the thread to be incredibly useful in
(1) understanding Junctions, and (2) getting a handle on the
various ways in which they can be implemented. So it's been
worthwhile for me. And no offenses have been intended or taken.
Pm
On Sat, 19 Feb 2005 15:20:59 -0600, Rod Adams <r...@rodadams.net> wrote:
> Poor Mr. Fowles is likely having nightmares figuring out how to summarize all of this.
Not really nightmares, more sleepless nights twitching in fear... But
I did skip writing it this week as this thread is the only one of
great length and I was hoping to wait for it to resolve... Which it
sounds like it is doing :-)
Matt
--
"Computer Science is merely the post-Turing Decline of Formal Systems Theory."
-???
> Rod Adams wrote:
>
>> Is this new, or yet another important detail I missed along the way?
>> Or is this a side effect of not being able to store a Junction, and
>> can go away if C< use Junctions > is turned on?
>
>
> Yes, it's a side-effect of the new default prohibition on junction
> assignments (though I'm still working hard to convince everyone that
> that prohibition cripples junctions and that having to <use junctions>
> before you can assign a basic Perl 6 scalar datatype to a variable is
> an abomination).
FWIW, I agree with you that having something half enabled by default
make little sense. But I'll accept it as a stop gap until I get my
explicit threading.
And I really don't like the implications of how turning on "use
junctions" can also suddenly change the threading semantics of
junctions. Talk about subtleties in action...
I imagine some intermediate level programmer decides they are ready to
start playing with stored junctions. He adds a "use junctions;" at the
top of his file, thus turning it on for all functions written in that
package. Suddenly, some of his function calls with stop implicitly
threading, and instead start carping in some truly spectacular ways.
Said programmer quickly takes away the "use junctions;", and never
considers using stored junctions again. It's just not worth the trouble,
in his mind.
If, however, he was conditioned to put in some »«'s whenever he wanted
his junctions to thread, he would not encounter this problem.
>
>> I will, however, question if this is optimal. Compare two simple cases:
>
> > C< $x == any(4,5,6) > and C< $x < all(4,5,6) >. Both of them are prime
> > candidates to some optimizations, but the optimizations are likely
> rather different.
> > If we pass the junction into the operator, then it can perform some
> custom tailored code,
> > to make things much more efficient, instead of relying on a more
> generalized junction
>
>> optimizer to handle things.
>
>
> Sure. That's why we have the ability to specify subroutines where
> junctive args are *not* autothreaded, by typing the corresponding
> parameter as taking a junction:
>
> multi sub infix:«==» (YourType $x, Junction $y) is symmetrical {...}
> multi sub infix:«<» (YourType $x, Junction $y) is symmetrical {...}
>
> These two multisubs can optimize for junctive arguments to their
> hearts' content.
I guess I misunderstood your statement of "literal junctions *will*
autothread in all circumstances" to mean _all_ circumstances, not just
ones where the callee didn't explicitly say they could accept them, or
if the current "use junctions;" status is in alignment with the moon, or
other such things.
I will allow you to alter that statement if you now find it
insufficiently qualified.
>
> I don't see that Patrick's response implies that at all. In fact, I
> think his statement that:
>
> >> Well, the ultimate answer is that both Dan and Patrick (and others)
> >> will negotiate the exact interface when we get to that point, and
> >> that we don't seem to be too concerned about it at the moment.
> >> (It could just be that we're both burying our heads in the sand
> >> hoping it'll be magically "solved" by the other. :-)
> >>
> >> However, in working out these examples I'm fairly comfortable that
> >> it can be made to work at the Perl 6 compiler level if need be,
> >> although it will probably be a lot more efficient if we can find a
> >> way to do it within Parrot.
>
> seems to confirm that detecting and reporting autothreaded
> side-effects is entirely possible.
Let me attempt to clarify the discrepancy as I see it.
I'll take Damian's recent comment of: " And if C< is_prime($x) > does
happen to autothread when you weren't expecting it to, then one of two
things will happen. Either the subroutine will be 'pure' in which case
there's no problem in autothreading it; or else the subroutine will have
side effects, in which case you'll get an explicit warning when the
autothreading occurs. "
and my 19210 code of:
$x = 3|4;
$y = fun($x);
sub fun ($x) {
warn "enter: fun($x)\n" if $DEBUG;
$x + 1;
}
I hope it's clear that whether or not fun() has side effects is a
runtime decision, and can't realistically be determined at function call
time.
Now, let's assume that this falls under Damian's heading of "happens to
autothread when I wasn't expecting it to."
If $DEBUG is off, no worries.
If $DEBUG is on, I expect my explicit warning, as promised.
What I am questioning is how perl decides when to give my warning. Does
perl inspect fun() to see if there are any side effect causing calls in
it? Or does it keep an internal note to itself saying "I'm
autothreading. This class of functions is Bad.", and then when it
encounters one of those functions, carp?
Now throw in a quote from Patrick (19212): "As you've written things
above, C<fun> is autothreaded (your option #3), and we'll see two
C<warn> output lines if $DEBUG is set."
Somehow, that's not the kind of "explicit warning" I was expecting from
Damian's comment.
As for the references to pure-Parrot issue, no I did not previously
supply any supporting code, but it was an extension of the issue above.
$x = 3|4;
$y = fun($x);
sub fun ($x) {
some_parrot_func($x);
$x + 1;
}
I can't write the supporting Parrot code, but suffice it to say that at
this point in time, we do not know if some_parrot_func() has side
effects or not, so we don't know if we should continue autothreading, or
carp.
This is at least the second time Damian has implied that autothreading
bombs out with output or other side effects (xref 19179). But the way I
see it, determining if a given call has side effects or not is on par
with the Halting Problem. You don't know until you try it. And with
Parrot in the mix, it becomes even harder, since I doubt perl will be
able to monitor whether or not there are any side effects of merit
inside a given Python routine. But if Patrick, Dan, and their merry pack
of helpers tell me they are confident in their ability to solve this
problem, I'll trust them. I just want agreement that we are looking at
the same problem before I accept it.
The programmer making the call, however, generally has a very good idea
about the possible side effects, and can make a much better decision
about it what is needed, and can express it with the simple presence or
absence of »«.
>
>> This problem goes away completely with explicit autothreading. perl
>> would no longer be making assumptions about what to autothread, and
>> what to carp over.
>
>
> But that's the whole point of junctions! Namely that perl works it out
> for you. If you don't want that to happen then don't use junctions
> (and don't allow them to be used, by specifying C<no junctions>).
I was thinking that the whole point of junctions was to superimpose
several values onto a single variable. Without threading, junctions are
meaningless, I'll give you that.
But I fail to see how trivially declared explicit threading vs implicit
threading is found to be crippling to the concept. I invite you to
convince me otherwise.
I see that adding the »« is my telling perl to "go work this junction
out for me", not putting it in is telling perl "hold off on working this
junction out for now... I want you to thread it later."
>
>> But I still don't like implicit autothreading, and likely never will.
>> I don't know how to explain it, but it just feels very wrong. It's
>> down there with using typeglobs to pass filehandles, which is
>> thankfully history.
>
>
> I understand your qualms, even if you can't nail down the exactly
> reasons for them.
>
> However, I still disagree with them. I truly believe that junctions
> (including their autothreading behaviour) ought to be core to Perl
> 6...and not ham-strung in any way.
I concur that junctions in all their glory should be core to Perl 6,
except implicit threading.
And it's not that I don't want junctions. I do want them. I just don't
want implicit threading, and have yet to be convinced that it is
essential to the concept.
>
> if any is_prime«(@x) {...} # Explicitly threaded
Very cool, except it doesn't appear to cover the case of:
if is_sqrt_of(»any(@x)«,@y) {...}
Or did Perl sneak currying in when I wasn't looking?
if any (is_sqrt_of«(@x))(@y) {...}
I don't recall seeing anything like that, and I don't think it would
work very well if it was.
Simply put,
I want my junctions.
I want my hyper operator superstrength arrays.
I want them both at the same time.
I never want to see implicit threading. Ever.
> Simply put,
>
> I want my junctions.
Standard in Perl 6.
> I want my hyper operator superstrength arrays.
Standard in Perl 6.
> I want them both at the same time.
Standard in Perl 6.
> I never want to see implicit threading. Ever.
If this is the only stumbling block, then it's easily solved.
Instead of ruining junctions by imposing all kinds of complex and annoying
hoops and hurdles (i.e. C<use junctions> and C<no junctions>), we can just
offer a C<no autothreading> pragma that prevents any non-explicit junction
from autothreading any operator or subroutine. Put it at the top of your code
and you'll never get any implicit threading.
Damian
Point of consideration: is accidentally autothreading over a junction
any more dangerous than accidentally looping forever over an infinite
lazy list?
> Rod Adams wrote:
>
>> I never want to see implicit threading. Ever.
>
>
> If this is the only stumbling block, then it's easily solved.
>
> Instead of ruining junctions by imposing all kinds of complex and
> annoying hoops and hurdles (i.e. C<use junctions> and C<no
> junctions>), we can just offer a C<no autothreading> pragma that
> prevents any non-explicit junction from autothreading any operator or
> subroutine. Put it at the top of your code and you'll never get any
> implicit threading.
Well, allow me to explain what I want in a little more detail, so I can
be sure I'm getting it.
I want to be able to feed a junction to functions that handle it, but I
also want to thread over ones that do not. But I do not want the
decision of which way to go to be made for me.
So, I want to be able to pass a raw junction into C< == >, because it
understands junctions, and will take care of the threading for me,
likely in a way that's much more efficient than my generalized »«
threading ever could. I do not consider this implicit threading. It's
passing the task of threading off to C< == >, which itself performs some
form of explicit threading.
But when I'm faced with some function that does not directly support
junctions, or one which does, but not in a way that I like, I want to be
able to thread my junction over it.
I do not think that what you said above is enough to accomplish this. I
believe what I need to separate your threading desires from mine is two
fold:
1) I need my »« modifier which forces explicit threading, which will not
normally be needed under "use autothreading;" conditions.
2) I need "no autothreading;" to alter the calling syntax to require »«
when threading is desired (or you can do a .values() and .junctiontype()
and roll your own if you really want to). But I can still pass junctions
around at will, withstanding normal type check requirements.
Then the only argument left is whether "use autothreading" or "no
autothreading" should be default. I would, of course, say "no
autothreading;", and then turn back on the ability to store junctions.
IMO, "no autothreading" would provide enough cover for the unsuspecting,
removing the Bad Side Effects problems that spawned Larry's no junction
storage default. At the same time, junctions still have enormous power
via the »«. If people don't want to have to bother figuring out when to
thread for themselves, they can then turn on "use autothreading", and
let perl attempt to figure it out for them.
Also, if "no autothreading" is default, the person new to Perl6 will
always have something to present to investigate to figure out what is
going on. In my code, it'll be the funny looking C< »$junction« >
things. In your code, it'll be the C< use autothreading; > at the top of
the page.
The only implementation problem I see is a potential for the »« to be
mis-parsed, since » and « seem to be serving several different roles
these days, but I don't think any of them conflict with this meaning. If
you want to rename »« to something else, I'm open to suggestions. Just
leave it fairly simple.
As for why implementation should be easy (or at least the delta between
your way and my way is easy):
- The functionality of »$junction« has to be defined anyways if
autothreading happens. It's just calling an already existent chunk of code.
- The logic of when to thread becomes:
given $situation {
when marked_with_»«()
{ thread }
when use_autothreading() & damians_ouija_board()
{ thread }
default
{ don't thread }
}
And you've already defined how the ouija board works, everything else
should be boilerplate for the compiler/runtime to handle.
This sound reasonable enough?
-- Rod Adams
> This sound reasonable enough?
Frankly, no. ;-)
Sorry, but your latest proposal sounds complex, multiply special-cased, and
way too much of an imposition on the programmer (which is specifically what
junctions are supposed to avoid).
I'm going to continue to strongly recommend that we stick with junctions that
are simply another (fully assignable) kind of scalar value, and which always
autothread in any scalar context that isn't explicitly typed C<Junction>.
To protect the innocent (or the timorous) I'm also going to recommend a C<no
autothreading> pragma, which will prevent non-explicit junctions from
autothreading.
And that's all I'm going to recommend.
Then, if you don't want implicit autothreading, you can turn it off
completely: C<no autothreading>.
And if you want explicit (non-auto)threading, you can use an explicit
junction, even under C<no autothreading>:
is_prime( any($x) )
And everywhere else, junctions will just Do The Right Thing, even to the point
of ensuring that any side-effects on your unordered data are appropriately
non-orderly. ;-)
I truly appreciate the thought and effort that all of you--especially Rod and
Patrick--have put into this discussion, but I'm afraid I have to bow out of it
now.
Damian
PS: FWIW, I'd have absolutely no objection to us adding a fifth core
junctive type (perhaps C<Adjunction>, perhaps just C<Set> ;-) that
has *no* associated predicate, but which implements full set semantics:
my $x = set(1..3);
my $y = set(1,3,5,7,9);
my $n = 2;
$x | $y # set(1,2,3,5,7,9)
$x & $y # set(1,3)
$x - $y # set(2)
!$x # set(none(2)) ;-)
$x < $y # $x is a proper subset of $y
$x <= $y # $x is a subset of $y
$x == $y # $x is the set $y
$x ~~ $y # $x is the set $y
$n < $y # $n is an element of $y
$n ~~ $y # $n is an element of $y
set() # empty set
$x.values # list of elements of set $x
$x.powerset # power set of $x
$x x $y # cross product of $x and $y
# etc., etc.
> the Awesome Power of Junctions:
As I tried to express elsehwere, this what I'm looking for.
Instinctively, and for a long time since I first came across Q::S, I thought
that the "killer app" of Junctions is there somewhere, I'm just not seeing it
yet.
I'd really like to see some practical demonstrations of the Awesome Power.
Something that goes beyond producing a boolean result from a set of values that
could equally be done using hyperoperators?
Njs.
>On Sat, 19 Feb 2005 18:42:36 +1100, dam...@conway.org (Damian Conway) wrote:
>
>>The Awesome Power of Junctions:
>>
>>
>
>As I tried to express elsehwere, this what I'm looking for.
>
>Instinctively, and for a long time since I first came across Q::S, I thought
>that the "killer app" of Junctions is there somewhere, I'm just not seeing it
>yet.
>
>I'd really like to see some practical demonstrations of the Awesome Power.
>
>Something that goes beyond producing a boolean result from a set of values that
>could equally be done using hyperoperators?
>
>
Well, that was one of my stumbling blocks early on. They don't really
give you the power to do anything you couldn't previously do. They just
make certain things a lot easier to do.
But I'll take a whack at giving you something non-trivial to do without
junctions:
$re1 = /^ <-[x]>* x <-[x]>* $/; # match a string with exactly one
'x' in it.
$re2 = /^ <-[y]>* y <-[y]>* $/; # ditto 'y'
$re3 = /^ <-[z]>* z <-[z]>* $/; # ditto 'z'
$re4 = all($re1, $re2, $re3); # matches if x,y, & z all appear
exactly once, in any order.
$re5 = one($re1, $re2, $re3); # matches if there is exactly one x,
y, or z
$re6 = any($re1, $re2, $re3); # matches if there is at least one of
x,y,z that appears exactly once.
$re7 = none($re1, $re2, $re3); # matches if there are 0 or 2+ of
each of x,y,z.
And all seven of these can be used as any stored RE can:
if $x ~~ $re6 {...};
given $x {
when $re5 {...}
when 'santa' {...}
when ($re1 | $re3) & $re3 {...}
}
Looking at the junctioned RE's:
#6 would is straight forward to do as a single RE in this case, and in
the general case.
#5 would be fairly trivial to in this case, but not in the general case.
#4 is possible with a lot of zero-length look aheads and look-behinds,
but very ugly.
#7 I have no idea how to attack as a single RE in anything close to
approaching elegance.
So what have we gained here? The ability to join several RE's together,
into something that still acts like a single RE. Show me the equivalent
code without junctions, and then we'll compare the power of junctions.
btw, the examples above assume the ability to store a junction. So you
either have to 'use junctions;', or convince Larry to rescind that
restriction.
HTH.
-- Rod Adams
> Rod Adams asked:
>
> > This sound reasonable enough?
>
> Frankly, no. ;-)
>
> Sorry, but your latest proposal sounds complex, multiply
> special-cased, and way too much of an imposition on the programmer
> (which is specifically what junctions are supposed to avoid).
Funny. I thought it was simple and elegant. My explanation might have
been complex, as well as the motivations, but I truly considered it a
straight forward and simple solution.
As for "too much of an imposition on the programmer", that's essentially
what I was asking for all along. To be imposed upon to decide when
threading occurs. I won't apologize for that, or change my position on
it. But I was making the imposition optional.
And, actually, now that I better understand your latest proposal, I
realize how amazing close we are.
>
> Then, if you don't want implicit autothreading, you can turn it off
> completely: C<no autothreading>.
>
> And if you want explicit (non-auto)threading, you can use an explicit
> junction, even under C<no autothreading>:
>
> is_prime( any($x) )
Change that C< any($x) > to C< »$x« >, and the important differences
between our positions shrink dramatically.
Reason I don't like any() here is that $x already has a junctive
condition on it. While any() with a single value does not change in the
least how this will be evaluated in the long run, it could be confusing
to wrap an all() junction inside an any() call. Doubling up the type
works for all the types except none(). Use »« for an explicit call
designator, and you revert to the type already in the junction.
Give me my »« as a secondary way of always explicitly threading, and
I'll go away happy. You can keep the rest of your proposal the same.
I'll just start using them wherever I think threading should happen,
even if I never needed to use them in most of the places I will. It'll
be a good visual to myself of what's going on.
Don't give them to me, and I'll frown for a while, but then likely get
over it.
>
> I truly appreciate the thought and effort that all of you--especially
> Rod and Patrick--have put into this discussion, but I'm afraid I have
> to bow out of it now.
I understand bowing out of this perfectly. There other tasks whose time
I've raided for this discussion, and the backlog is starting to look
pretty nasty.
But it has been a good and healthy discussion. Many things have come of
it. Thanks to all the participants.
>
>
> PS: FWIW, I'd have absolutely no objection to us adding a fifth core
> junctive type (perhaps C<Adjunction>, perhaps just C<Set> ;-) that
> has *no* associated predicate, but which implements full set
> semantics:
>
> my $x = set(1..3);
> my $y = set(1,3,5,7,9);
> my $n = 2;
>
> $x | $y # set(1,2,3,5,7,9)
> $x & $y # set(1,3)
> $x - $y # set(2)
> !$x # set(none(2)) ;-)
> $x < $y # $x is a proper subset of $y
> $x <= $y # $x is a subset of $y
> $x == $y # $x is the set $y
> $x ~~ $y # $x is the set $y
> $n < $y # $n is an element of $y
> $n ~~ $y # $n is an element of $y
> set() # empty set
> $x.values # list of elements of set $x
> $x.powerset # power set of $x
> $x x $y # cross product of $x and $y
>
> # etc., etc.
>
Now THAT is an even better idea than the separate Set class I was going
to champion whenever I next had time for such a thing!
I support this concept fully!
-- Rod Adams
PS - Looks like Matt gets his wish. This juggernaut of a thread is
coming to a close. I'd be happy to proof his summary of it if he gives
me an advanced copy.
Just noting that secondary sigils aren't limited to scalars:
@*biglist = 1... ; # global @::*::biglist
has %.dictionary; # public attribute
has @:children; # private attribute
say @?BLOCK; # which blocks am I in?
{ sort @^list; } # placeholder array
%=POD{'DATA'} # filehandle for =begin DATA stream
Pm
By the power of a single good example, you can now count me amongst the
convinced.
>
> btw, the examples above assume the ability to store a junction. So you
> either have to 'use junctions;', or convince Larry to rescind that
> restriction.
>
Patricks's arguement that there is no real difference between a scalar that
contains a Junction and a scalar that contains a coderef or any type of ref when
it comes to requiring the programmer to know (or test for) what the scalar
holds, was enough to convince me that no additional pragmas are required for
junctions. Though I wouldn't object to Damian's c<no autothreading>.
My only problem was that I couldn't see the larger benefit of them. Your example
above is sufficient to convince that being able to encapsulate not just a set of
data, but /required result/ of *any* operations performed using that set data
did the trick for me.
I now view the junction
my $x = any( @values );
as roughly equal to:
my $junction = bless [ @values ], 'any';
and similarly for the other three. Simplistic, but enough for my mind to make
the transition.
Using the hyper operators, you would have to re-state the desired result for
each operation, but with a junction you've encapsulated it in.
>
> HTH.
>
> -- Rod Adams
>
Thanks for taking the time to produce your good example and so carrying me
along.
njs.
>
>
> --------------020209010404060902000407--
Examine what is said, not who speaks.
> $re1 = /^ <-[x]>* x <-[x]>* $/; # match a string with exactly one
> 'x' in it.
> $re2 = /^ <-[y]>* y <-[y]>* $/; # ditto 'y'
> $re3 = /^ <-[z]>* z <-[z]>* $/; # ditto 'z'
> $re7 = none($re1, $re2, $re3); # matches if there are 0 or 2+ of
> each of x,y,z.
> #7 I have no idea how to attack as a single RE in anything close to
> approaching elegance.
Depending on your idea of elegance ... anchored zero-width negative
lookahead:
$re7 = qr/^ (?!= $re1 | $re2 | $re3 ) /x;
Oh right. Perl6. Well, if I understand "<$re1>" correctly, I think
this is what it will look like:
$re7 = /^ <!before <$re1> | <$re2> | <$re3> > /;
I still want junctions, but I also still am not quite sure how they
will behave. For instance, I wonder how this would autothread or not:
my sub f (Int $x) {
if $x {
return 0, 1, $x;
}
else {
return 0, 1;
}
}
my $j = 0 | 7;
my @a = (1, f($j), 0);
- How many elements are there in @a? 3? 5? 4|5?
- What is @a[-1]? 0? any(0)? 0|undef?
- What is @a[4]? undef? 0|undef? 0?
Naïvely, I would expect that setting of @a be equivalent to this:
my @a = (1, ([0,1]|[0,1,7]), 0);
And so from the callers perspective, &f, which usually returns two
or three values, suddenly returns a single (junctive) value. New
semantics for &f, courtesy of autothreading. I expect this is too
naïve. But what am I missing?
Eirik
--
All bridge hands are equally likely, but some are more equally likely
than others.
-- Alan Truscott
Given this:
> my $x = set(1..3);
> my $y = set(1,3,5,7,9);
> my $n = 2;
>
> $x | $y # set(1,2,3,5,7,9)
> $x & $y # set(1,3)
> $x - $y # set(2)
> !$x # set(none(2)) ;-)
I don't understand this last line, even given the context of the preceding
three. Why is it none of 2, rather than none of something else?
Nicholas Clark
>Rod Adams <r...@rodadams.net> writes:
>
>
>
>> $re1 = /^ <-[x]>* x <-[x]>* $/; # match a string with exactly one
>>'x' in it.
>> $re2 = /^ <-[y]>* y <-[y]>* $/; # ditto 'y'
>> $re3 = /^ <-[z]>* z <-[z]>* $/; # ditto 'z'
>>
>>
>> $re7 = none($re1, $re2, $re3); # matches if there are 0 or 2+ of
>>each of x,y,z.
>>
>>
>>#7 I have no idea how to attack as a single RE in anything close to
>>approaching elegance.
>>
>>
>
> Depending on your idea of elegance ... anchored zero-width negative
>lookahead:
>
>$re7 = qr/^ (?!= $re1 | $re2 | $re3 ) /x;
>
> Oh right. Perl6. Well, if I understand "<$re1>" correctly, I think
>this is what it will look like:
>
>$re7 = /^ <!before <$re1> | <$re2> | <$re3> > /;
>
>
That doesn't quite do it, but something along those lines is possible.
You'd have to make sure the look ahead/behinds match to the end of the
string, not just some substring. And then you'd have to put in something
to actually match the string. So possible, but not straightforward in
the least.
>
> I still want junctions, but I also still am not quite sure how they
>will behave. For instance, I wonder how this would autothread or not:
>
>my sub f (Int $x) {
> if $x {
> return 0, 1, $x;
> }
> else {
> return 0, 1;
> }
>}
>
>my $j = 0 | 7;
>my @a = (1, f($j), 0);
>
> - How many elements are there in @a? 3? 5? 4|5?
>
> - What is @a[-1]? 0? any(0)? 0|undef?
>
> - What is @a[4]? undef? 0|undef? 0?
>
> Naïvely, I would expect that setting of @a be equivalent to this:
>
>my @a = (1, ([0,1]|[0,1,7]), 0);
>
> And so from the callers perspective, &f, which usually returns two
>or three values, suddenly returns a single (junctive) value. New
>semantics for &f, courtesy of autothreading. I expect this is too
>naïve. But what am I missing?
>
>
I believe you are correct in your analysis.
It's also something that I will cover as soon as I have time to write
down the revelation I had about junctions laying in bed last night.
(Don't cringe everyone. I actually figured out why Damian insists on
implicit threading. And I agree with him. Now I want to clamp a
completely different kind of restriction on Junctions.)
I might be able to find time for it late tonight. Might take a few days.
-- Rod Adams
NC> On Sun, Feb 20, 2005 at 07:41:16PM +1100, Damian Conway wrote:
NC> Given this:
>> my $x = set(1..3);
>> my $y = set(1,3,5,7,9);
>> my $n = 2;
>>
>> $x | $y # set(1,2,3,5,7,9)
>> $x & $y # set(1,3)
>> $x - $y # set(2)
>> !$x # set(none(2)) ;-)
NC> I don't understand this last line, even given the context of the
NC> preceding three. Why is it none of 2, rather than none of
NC> something else?
my guess is a typo and $x should be $n.
uri
--
Uri Guttman ------ u...@stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
ESTUPIDDAMIAN.
Should, of course be:
!$x # set(none(1..3)) ;-)
Damian
PS: This is also a demonstration of the awesome power of junctions: that we
can specify the complement of a set without knowing its universal set!
> Rod Adams <r...@rodadams.net> writes:
>
>> Eirik Berg Hanssen wrote:
>>
>>>Rod Adams <r...@rodadams.net> writes:
>>>> $re1 = /^ <-[x]>* x <-[x]>* $/; # match a string with exactly one
>>>>'x' in it.
>>>> $re2 = /^ <-[y]>* y <-[y]>* $/; # ditto 'y'
>>>> $re3 = /^ <-[z]>* z <-[z]>* $/; # ditto 'z'
>>>> $re7 = none($re1, $re2, $re3); # matches if there are 0 or 2+ of
>>>>each of x,y,z.
>>>$re7 = /^ <!before <$re1> | <$re2> | <$re3> > /;
>> That doesn't quite do it, but something along those lines is
>> possible. You'd have to make sure the look ahead/behinds match to the
>> end of the string, not just some substring. And then you'd have to put
>> in something to actually match the string. So possible, but not
>> straightforward in the least.
>
> $re1, $re2, and $re3 are already anchored: Covered.
>
> And it will match the empty string at pos()==0, given that the
> assertion is satisfied. This of course differs from a junction of
> three match objects, each matching the whole string at pos()=0,
Whoops again: That would be a junction of three match objects,
_none_ of which are successful. When the assertion is not satisfied,
however, you will be able to inspect the match object junction's
states to see how many of the $re\d matched, and in the general case,
how they matched. But on the whole, I now get the impression that
these match object junctions (or at least match object injunctions)
would rarely be used outside boolean context.
Eirik
--
For every complex problem, there is a solution that is simple, neat, and wrong.
-- H. L. Mencken
A good plan today is better than a perfect plan tomorrow.
-- Patton
> Eirik Berg Hanssen wrote:
>
>>Rod Adams <r...@rodadams.net> writes:
>>
>>
>>
>>> $re1 = /^ <-[x]>* x <-[x]>* $/; # match a string with exactly one
>>>'x' in it.
>>> $re2 = /^ <-[y]>* y <-[y]>* $/; # ditto 'y'
>>> $re3 = /^ <-[z]>* z <-[z]>* $/; # ditto 'z'
>>>
>>>
>>> $re7 = none($re1, $re2, $re3); # matches if there are 0 or 2+ of
>>>each of x,y,z.
>>>
>>>
>>>#7 I have no idea how to attack as a single RE in anything close to
>>>approaching elegance.
>>>
>>>
>>
>> Depending on your idea of elegance ... anchored zero-width negative
>>lookahead:
>>
>>$re7 = qr/^ (?!= $re1 | $re2 | $re3 ) /x;
Whoops. For "?!=", read "?!".
Now imagine the quality of my Perl6 code ...
>> Oh right. Perl6. Well, if I understand "<$re1>" correctly, I think
>>this is what it will look like:
>>
>>$re7 = /^ <!before <$re1> | <$re2> | <$re3> > /;
>>
>>
> That doesn't quite do it, but something along those lines is
> possible. You'd have to make sure the look ahead/behinds match to the
> end of the string, not just some substring. And then you'd have to put
> in something to actually match the string. So possible, but not
> straightforward in the least.
$re1, $re2, and $re3 are already anchored: Covered.
And it will match the empty string at pos()==0, given that the
assertion is satisfied. This of course differs from a junction of
three match objects, each matching the whole string at pos()=0, but
that difference is in part to be expected (the point of the exercise
was to avoid a junction), and in part trivial to fix: just add ".*".
In the general case, it will be more complex, yes, but so will the
match object returned by the junctive rule.
Well, enough pattern matching. Junctions on the agenda, now! ;-)
Eirik
--
"So this is the Sword of Immortality? Huh?
What's it doin' in a CRYPT?!"
--- John S. Novak, III, quoting an unnamed player
I think it may be even simpler than the above, no lookaheads required:
$re7 = / <!$re1> & <!$re2> & <!$re3> /;
Pm
Or one more thing to drive the mathematicians into a rage...