Re: Tweaking junctions
On 10/22/2010 06:16 AM, Damian Conway wrote: That is, a C$value is an eigenstate of a C$junction if-and-only-if: $value !~~ Junction$value ~~ $junction In general this definition makes it impossible to return a list of eigenstates from the junction. Just think of junctions containing Code objects. Or anything more complicated than the built-in value types. But the C!eigenstates method (as currently defined) does not return a list of such eigenstates. Instead it merely returns a partially-flattened list of the raw internal values of the junction...which is not (usually) the same thing at all. Right; but afaict it's the only thing that can actually be implemented. And because it doesn't make all too much sense, it's specced to be private.
Re: Tweaking junctions
Moritz wrote: $value !~~ Junction $value ~~ $junction In general this definition makes it impossible to return a list of eigenstates from the junction. Just think of junctions containing Code objects. Well, that's a deficiency in smartmatching: that Callable ~~ Code doesn't check for identity between the two objects. Likewise the Regex ~~ Regex doesn't check for identity. Likewise Range ~~ Range testing for identical endpoints. Etc. ;-) The definition of eigenvalues() is supposed to be abstractly descriptive, not specifically constructive. The idea is simply: any leaf state inside the junction to which the junction could collapse. Now the implementation I already provided does currently rely on smartmatching, but that will be fixed pronto, now that you've kindly pointed out that smartmatching is...well...broken on several interesting types of states. :-) Right; but afaict it's the only thing that can actually be implemented. And because it doesn't make all too much sense, it's specced to be private. Fine. But please change the name anyway. If we all agree it's not returning eigenstates, and some of us believe it *can't* every return eigenstates, then it certainly shouldn't be called C.eigenstates. Damian
Re: Tweaking junctions
On 11/01/2010 12:41 PM, Damian Conway wrote: Moritz wrote: $value !~~ Junction$value ~~ $junction In general this definition makes it impossible to return a list of eigenstates from the junction. Just think of junctions containing Code objects. Well, that's a deficiency in smartmatching: that Callable ~~ Code doesn't check for identity between the two objects. Likewise the Regex ~~ Regex doesn't check for identity. Likewise Range ~~ Range testing for identical endpoints. Etc. ;-) Sorry, I disagree. It's exactly what smartmatching was designed to be. If you don't want that kind of behaviour, use infix:eqv or infix:=== instead. The definition of eigenvalues() is supposed to be abstractly descriptive, not specifically constructive. The idea is simply: any leaf state inside the junction to which the junction could collapse. Junctions only collapse to Bool::True or Bool::False. So an attempt to fix your definition could be 'any leaf state inside the junction, which makes the junction collapse to True if compared to the junction as a whole' but begs for a more concrete comparison rule. For which, IMHO smartmatching would be nice, since I mostly use junctions for smartmatching. Which brings us into the dilemma I mentioned before. Right; but afaict it's the only thing that can actually be implemented. And because it doesn't make all too much sense, it's specced to be private. Fine. But please change the name anyway. If we all agree it's not returning eigenstates, and some of us believe it *can't* every return eigenstates, then it certainly shouldn't be called C.eigenstates. Agreed. Cheers, Moritz
Re: Tweaking junctions
Food for thought, a few non-junction solutions: On 10/22/2010 06:16 AM, Damian Conway wrote: # Find the list of common elements in two lists... sub intersection (@list1, @list2) { (any(@list1) any(@list2).eigenstates; } sub intersection(@list1, @list2) { uniq gather for @list1 X @list2 - $a, $b { take $a if $a eqv $b } } or sub interseection(@list1, @list2) { (@list1 X= @list2).grep({ .key eqv .value}).key.uniq } Admittedly it's not as declarative, but it's explicit about the comparison used (which is a plus, in my eyes). If you want to use eq or ===, hash based solutions come to mind too. # Find the factors of a number... sub factors (Num $n) { ( $n/any(1..$n) ).eigenstates.grep({ $^q.Int == $q }); } sub factors($n) { ($n X/ 1..$n).grep: { .Int == $_ } } or sub factors($n) { (1..$n).grep: { %n %% $_ } } # Check for an unacceptable password, and list all when warning... constant UNACCEPTABLE = any Godel Escher Bart etc... ; if $passwd ~~ UNACCEPTABLE { say Unacceptable password. Don't use any of these:; say UNACCEPTABLE.eigenstates¬».fmt(\t%s\n); } constant UNACCETABLE = Godel Escher Bach; if $passwd ~~ any UNCACCEPTABLE { say Unacceptable password. Don't use any of these:; say UNACCEPTABLE».fmt(\t%s\n); }
Re: Tweaking junctions
On Mon, Nov 1, 2010 at 7:24 AM, Moritz Lenz mor...@faui2k3.org wrote: On 10/22/2010 06:16 AM, Damian Conway wrote: That is, a C$value is an eigenstate of a C$junction if-and-only-if: $value !~~ Junction $value ~~ $junction In general this definition makes it impossible to return a list of eigenstates from the junction. Just think of junctions containing Code objects. Or anything more complicated than the built-in value types. [Originally sent to Moritz alone because of Reply not sending to the list] Is it too late in this discussion to point out that, in non-perl usage, eigenstates are associated with the operator, not with the value fed into the operator? [Added at Moritz request] In linear algebra, eigenvectors and eigenvalues are non-trivial solutions to the equation Ax=λx, where x is a vector in a vector space, A is a operator (a function from a vector space to itself) and λ is a member of the field the vector space is defined over. For a given operator A, only certain values of λ allow that equation to be solved, and those values are called the eigenvalues for A. Also, for a given operator A, only certain vectors x will solve the equation, and those vectors are called eigenvectors. It should also be clear that different values of λ work with different sets of vectors x (the solutions to Ax = ax and Ax=bx are different if a != b), so it's typical to talk about the eigenvectors of A associated with a given eigenvalue λ. Since A is linear, if Ax=λx and Ay=λy, then A(ax) = a(Ax) = a(λx) = λ(ax) and A(x+y)=Ax+Ay=λx+λy=λ(x+y), so fir a given eigenvalue λ, there are typically multitudes of eigenvectors which form a vector space of their own. Eigenvectors for different eigenvalues are orthogonal, and any eigenvector can be scaled to be a unit eigenvector. If an operator has a full set of eigenvalues, one can pick a set of unit eigenvectors to act as a natural orthonormal basis for the operator. If operator A has three eigenvalues a, b, c, and three unit eigenvectors x, y, z, such that Ax=ax, Ay=by, and Az=cz, then if w = dx+ey+fz, Aw = a(dx)+b(ey)+c(fz), which is really easy to compute. In quantum mechanics, especially the Heisenberg matrix formulation (but by analogy, also every other formulation, including wave mechanics), quantum states are represented by vectors in a complex vector space, and vectors which differ by a real-valued scaling factor are generally considered equivalent. Transformations (i.e., anything which modifies the quantum state of the system, including but not limited to the passage of time) are represented by (unitary) operators on the state space. (Unitary in this case means that the norm of Ax is the same as the norm of x, for all x.) The standard notation is a bit odd, where the 'ket' |x represents a system in state x (and therefore |x+y a state in a superposition of x and y), The 'bra' x| is the complement of the ket |x, and can be multiplied by a ket to get a braket x|y which represents the probability that a system in state y is also in state x. |x is, naturally, usually normalized such that x|x = 1. Operators act on kets and return kets, so A|x is the braket notation way of writing the linear algebra Ax. Naturally, that means that y|A|x is the probability that a system that starts in state x will be in state y after the transform A. Since A is a linear operator, it has eigenvalues and eigenvectors. In the quantum mechanical world, where vectors represent states, the eigenvectors are called eigenstates. Eigenstates |i, |j of an operator A have the property that i|A|i = j|A|j = 1, but i|A|j = 0 (informally, if you start in an eigenstate of A, then the transform leaves you unchanged). However, A|i+j = |ai+bj, so A can change the nature of a superposition of states. i|i+j = 1/2, j|i+j = 1/2, but i|A|i+j = a/(a+b), j|A|i+j = b/(a+b). Schrodinger's Wave Equation, in matrix notation, is of the form Hx=Ex, where H is the Hamiltonian operator of the system, and E is the energy of the system, so the only allowed solutions of the wave equations are for energy levels E which are eigenvalues of H,and for quantum states which are eigenstates of H. Similar equations exist for virtually every observable, so the only allowable momenta are the eigenvalues or eigenstates of the momentum operator, the only allowable positions are the eigenvalues or eigenstates of the position operator, etc. So asking for the eigenstates of a quantum superposition is asking the wrong object for the property.
Re: Tweaking junctions
Buddha Buck wrote: Is it too late in this discussion to point out that, in non-perl usage, eigenstates are associated with the operator, not with the value fed into the operator? [cut] So asking for the eigenstates of a quantum superposition is asking the wrong object for the property. Probably is, yes :(. I argued the same thing a couple of years back (http://dave.whipp.name/sw/perl6/perl6_xmas_2008.html -- wow, p6/rakudo has advanced a lot since then!) when I suggested bra-ket notation for extracting the values of a junction: $player_value = max | 4..21 |==| $player_value_junc | (in this case, the value of a blackjack hand). Since then, I simplified this idea to use a more perl6ish meta-operator: $player_value = max 4..21 G== $player_value_junc My current belief is that it is likely that there'll eventually be sufficient core functionality that I'll be able to implement something like this as a module. So I don't push it except when someone else starts a thread about the values of a junction :). Dave.
Re: Tweaking junctions
On Thu, 28 Oct 2010, Damian Conway wrote: The apparent paradox ... is due to the assumption (employed in the second interpretation) that is identical to !=. Certainly that is true for simple scalar numbers, but not always for vector types such as tuples, sets, bags, complex numbers...or junctions. That doesn't make either or != intrinsically invalid on vector types (though they obviously are inappropriate for *some* vector types); it just means you can't reasonably treat the two operators as universally interchangeable, just because they sometimes are. Well, I think returning or throwing an Unordered exception would be the appropriate way to handle those, both for complex numbers and for junctions. Anyone who thinks they're dealing with real numbers will reasonably expect and != to be the same, and a junction can masquerade as anything (within each thread), including a real number. And what about when that difference is wrapped up inside a function? In other words, what's wrong when I expect the following two snippets to work the same way? A: sub anything_is_broken($subject) { grep { ! .test() } $subject.parts() } if anything_is_broken($subject) { fail($subject) } else { pass($subject) } B: sub everything_is_working($subject) { ! grep { ! .test() } $subject.parts() } if everything_is_working($subject) { pass($subject) } else { fail($subject) } Why should it make a difference whether $subject is a junction? In summary, the problem here seems to be that, algebraically, junctions don't behave exactly like non-junctions. Which is true, but no more a problem than the fact that, algebraically, complex numbers don't behave exactly like non-complex numbers, or that sets don't behave exactly like non-sets, or that Rats don't behave exactly like Nums, which don't behave exactly like Ints, which don't behave exactly like ints either. When you're talking about built-in operators, that's plausible, because by looking for ! in the operator name there are ways to DWIM-or-throw-an-exception. That's not true for user-defined functions, so I think the real problem is that the parallelizing of the expression that contains a junction may not be obvious at the point where it happens. Hmmm maybe one way to improve that might be to say that you can't put a junction into an untyped scalar, or indeed in any variable that isn't explicitly marked as this thing might contain a junction. That would apply, as now, to function parameters and returns, but also to variables and aggregate members; indeed, *everywhere*. And, of course, that's why Perl 6 has strong typing. So that, when these differences in behaviour do matter, we can specify what kind(s) of data we want to allow in particular variables, parameters or return slots...and thereby prevent unexpected kinds of data from sneaking in and violating (un)reasonable expectations or inducing (apparent) paradoxes. :-) I don't think strong typing is enough, because we explicitly mask the application of type constraints by autothreading. Each thread sees a thing with the type it expects, but the problem comes when the threads are recombined; the result often won't be what's expected. Don't get me wrong, I think Junctions are a really clever way of writing concise conditional expressions, but I think algebraic consistency is more important than clever conciseness. -Martin
Re: Tweaking junctions
Martin D Kealey suggested: Well, I think returning or throwing an Unordered exception would be the appropriate way to handle those, both for complex numbers and for junctions. For complex numbers that might be true, because the order relationship between two complex numbers isn't expressible in-band. But for junctions, the relationship of sometimes , somtimes = is entirely expressible. It's just: any(True, False). And what about when that difference is wrapped up inside a function? In other words, what's wrong when I expect the following two snippets to work the same way? sub anything_is_broken($subject) { grep { ! .test() } $subject.parts() } sub everything_is_working($subject) { ! grep { ! .test() } $subject.parts() } Why should it make a difference whether $subject is a junction? Because, although the two subroutines seem like they're complementary, they actually only partition the universe when the universe is strictly one-dimensional. And sometimes not even then. For example, here's a *non-junctive* scalar subject for which the two don't provide consistent answers either: class Part { has $.value; method test { $.value != 0 } } class Subject { has Part @.parts; method parts { @.parts.pick(2) } } my Subject $subject .= new(0..9); say anything_is_broken($subject);# 0 (because .parts picked 3 and 7) say everything_is_working($subject); # 0 (because .parts picked 9 and 0) In other words, it isn't the junctive-ness that creates unexpected behaviour, it's the assumption that every scalar works the same way. That's not true for user-defined functions, so I think the real problem is that the parallelizing of the expression that contains a junction may not be obvious at the point where it happens. But that's not a unique property of junctions; that not obviousness is equally true of any scalar that, for example, simply overloads .Num or .Str or .Bool. For instance: if $result { say $result } can easily print 0, which is not obvious, but is still both correct and useful (when, for example, $result the result of a call to Csystem). Hmmm maybe one way to improve that might be to say that you can't put a junction into an untyped scalar, or indeed in any variable that isn't explicitly marked as this thing might contain a junction. That would apply, as now, to function parameters and returns, but also to variables and aggregate members; indeed, *everywhere*. But junctions are an intrinsic part of Perl 6. So it's unreasonable to *not* expect them. And if you want to not expect them, you can just mark your variables that way, with (ironically): my $subject where none(Junction); Besides, are you also going to extend this segregation of junctions to not allow C0 but true in untyped scalars either? Because how else will you avoid the non-obviousness of: if $result { say $result } ??? I don't think strong typing is enough, because we explicitly mask the application of type constraints by autothreading. Each thread sees a thing with the type it expects, but the problem comes when the threads are recombined; the result often won't be what's expected. Huh? If the variables are strongly typed as non-junctive, a junction will never be able sneak past into or out-of an autothreading. Don't get me wrong, I think Junctions are a really clever way of writing concise conditional expressions, but I think algebraic consistency is more important than clever conciseness. Aha. I see that we mean different things when we use the term algebraic consistency. You seem to want all algebras to be universally consistent; I want each algebra to be internally consistent. Or to put it another way, you appear to want: A given operator or function does one consistent thing (regardless of the specific types of its operands) whereas I want: A given operator or function does one thing (consistent with the specific types of its operands) In other words, you seem to be arguing for monomorphism, whereas I'm definitely arguing for polymorphism. Neither is inherently better, but one is inherently more powerful. While I deeply respect your position, I'm going to keep arguing for that more powerful alternative. Damian
Re: Tweaking junctions
Martin D Kealey asked: Or do we not invert junctions, and run the risk of unexpected action-at-a-distance instead? I think our current approach is correct. That is: we invert junctions on operators that are themselves intrinsically inverted (such as !=, !~~, !), but do not invert on those that are not (such as ==, ~~, =). Or rather, we *never* invert junctions at all, but merely honour the standard semantics of the prefix:! metaoperator: hoisting the negation outside the entire operation and applying it once the underlying operation is complete. The apparent paradox you demonstrated with the two interpretations of C$foo ($bar | $zot) is due to the assumption (employed in the second interpretation) that is identical to !=. Certainly that is true for simple scalar numbers, but not always for vector types such as tuples, sets, bags, complex numbers...or junctions. That doesn't make either or != intrinsically invalid on vector types (though they obviously are inappropriate for *some* vector types); it just means you can't reasonably treat the two operators as universally interchangeable, just because they sometimes are. In summary, the problem here seems to be that, algebraically, junctions don't behave exactly like non-junctions. Which is true, but no more a problem than the fact that, algebraically, complex numbers don't behave exactly like non-complex numbers, or that sets don't behave exactly like non-sets, or that Rats don't behave exactly like Nums, which don't behave exactly like Ints, which don't behave exactly like ints either. And, of course, that's why Perl 6 has strong typing. So that, when these differences in behaviour do matter, we can specify what kind(s) of data we want to allow in particular variables, parameters or return slots...and thereby prevent unexpected kinds of data from sneaking in and violating (un)reasonable expectations or inducing (apparent) paradoxes. :-) With regard to your other point: If it's about parallel data handling, then we have to be prepared to (notionally) fork the entire rest of the runtime, even as far as having a definition of what return value the parent process sees (from exit) when those threads are implicitly collapsed at termination. That's certainly true, although junctions are supposed to guarantee to coalesce all the threads they may generate back into a single superimposed result back in the originating thread. The problem only arises if an operation or subroutine that has been junctively threaded terminates without returning. But that's just having a side-effect, which we already know is inappropriate for junctions (and hyperoperators, and autothreaded loops, and pretty much any other kind of parallel construct). Damian
Re: Tweaking junctions
Damian Conway wrote: If it's about parallel data handling, then we have to be prepared to (notionally) fork the entire rest of the runtime, even as far as having a definition of what return value the parent process sees (from exit) when those threads are implicitly collapsed at termination. That's certainly true, although junctions are supposed to guarantee to coalesce all the threads they may generate back into a single superimposed result back in the originating thread. The problem only arises if an operation or subroutine that has been junctively threaded terminates without returning. But that's just having a side-effect, which we already know is inappropriate for junctions (and hyperoperators, and autothreaded loops, and pretty much any other kind of parallel construct). Could thread termination without a return reasonably be treated as another way of saying returns nothing or alternately returns an empty junction (a junction ranging over zero values)? Or would that instead better be treated as an error such that returning nothing should have been done explicitly? -- Darren Duncan
Re: Tweaking junctions
I have to admit to feeling uneasy about the whole action-at-a-distance effect that junctions are capable of producing. They sit around pretending to be a scalar, only to pop up and wreak havoc with ones expectations of linearity when you're not expecting it. That unexpected-action-at-a-distance is somewhat mitigated by autothreading functions where the junction is presented as a direct parameter, but being able to defer a junction by putting it inside an aggregate seems like asking for trouble unless we make sure that it behaves as much like an ordinary scalar as possible when it does finally get collapsed. I think we need to decide whether junctions are just about flow control, or are more generalized parallel data handling, typified as a way of handling sets. If it's about parallel data handling, then we have to be prepared to (notionally) fork the entire rest of the runtime, even as far as having a definition of what return value the parent process sees (from exit) when those threads are implicitly collapsed at termination. And deciding what happens when, in the process of autothreading junction A, you wind up collapsing junction B in different ways (including perhaps not collapsing it)? Right now I don't think we're up for all that. For the sake of flow control, do we have a formal algebra of how we apply distributive rules so that all the elements of the resulting junction are booleans and the result of the collapse is obvious? In the simple cases it's (supposedly) obvious: $foo == ($bar | $zot) === ($foo == $bar) | ($foo == $zot) But what about: $foo != ($bar | $zot) === not($foo == ($bar | $zot)) === not(($foo == $bar) | ($foo == $zot)) === (not($foo == $bar)) (not($foo == $zot)) === ($foo != $bar) ($foo != $zot) Moreover, what about: $foo ($bar | $zot)=== ($foo $bar) | ($foo $zot) versus: $foo ($bar | $zot)=== not($foo = ($bar | $zot)) === not(($foo = $bar) | ($foo = $zot)) === (not($foo = $bar)) (not($foot = $zot)) === ($foo $bar) ($foo $zot) In short, in order to avoid unexpected action at a distance, it's logical to invert the junction type, but in doing so you hit some very nasty corner cases. How do you know whether a function is saying X has property Y or X does not have property ~Y ? Which of Y or ~Y are we talking about? It seems fair enough to infer that equality comparison is Y rather than ~Y, but any other sort of comparison is fraught. This leads me to the conclusion that collapsing a junction over an ordered comparison should fail in the same way that an ordered comparison involving complex numbers or vectors should fail (or more generally, an ordered comparison of any aggregate should fail unless the aggregate type defines some sensible ordering; there should be no default). Indeed, the whole notion of autothreading a junction over an arbitrary function (to produce a boolean that can be used for flow control) starts to sound fishy: how do we decide whether a given function should be treated like ==, where the distributive rule produces the same junction type, or like !=, where the distributive rule produces the inverse junction type? Or do we not invert junctions, and run the risk of unexpected action-at-a-distance instead? -Martin
Re: Tweaking junctions
Jon Lang wrote: Personally, I don't think that it should be a public method: one thing about junctions is that you can use them interchangeably with ordinary scalars; giving them a public method breaks that. In particular, code that makes use of a Junction public method would break if you were to hand it a non-Junction. On the other hand, I would argue that, because non-junctive scalars are just an (uninteresting ;-) special degenerate case of junctions, they should have a public .eigenvalues() too! It would, of course, just return their uninteresting degenerate special-case scalar value. Alternatively, I would note that Perl 6 already provides a perfectly good way to handle this issue. If you're calling $someval.eigenvalues(), you're inherently assuming that you're calling it on a Junction. If there's any possibility that what you're calling it on *isn't* a junction, then you should either be unsurprised to get the exception that will be thrown, or else you should explicitly cater to the possibility with: my @values = $val.?eigenvalues; or perhaps: my @values = $val.?eigenvalues // $val; Although, if that second form is to become the standard safe approach, that argues to me that non-junctions ought to have a .eigenvalues() too, so that the first alternative above just DWIMs. BTW, given Larry's recent clarification about Sets producing a list of their keys in list context, I have absolutely no objection to .eigenvalues() returning a Set, rather than a List. Indeed, I think it would be even more valuable that way. Damian
Re: Tweaking junctions
On 2010-Oct-25, at 15:14, Damian Conway wrote: Yes, Ted Z. pointed out to me that, as the name of this construct, every has ambiguity and synonym issues. Other possibilities are: select(@values) one(3..7) those(@values) one(3..7) whichever(@values) one(3..7) itemize(@values) one(3..7) extract(@values) one(3..7) ...of which, only Cselect really seems a good alternative. Any other suggestions most welcome! Applying a slightly more stringent test example, the above terms, plus a few new suggestions, separates in to two groups these work for me @nv = those(@values) one(3..7) @nv = only(@values) one(3..7) @nv = solely(@values) one(3..7) @nv = exclusively(@values) one(3..7) @nv = whichever(@values) one(3..7) these do not work for me @nv = select(@values) one(3..7) @nv = extract(@values) one(3..7) @nv = isolate(@values) one(3..7) @nv = locate(@values) one(3..7) @nv = itemize(@values) one(3..7) It is tempting to use @nv = sift(@values) one(3..7) @nv = winnow(@values) one(3..7) however, like 'filter' it is confusing what they focus attention on. Often they focus on what is being discarded To winnow the chaff from the grain yet sometimes they focus on what is being retained it's difficult to winnow out the truth it's difficult to sift out the truth I wonder if some times we might desire what is to be discarded, that is the complement of the outcome this conversation started with. (@keep, @discard) = filter(@values) one(3..7) (@grain, @chaff) = winnow(@values) one(3..7) (@truth, @lies) = sift(@values) one(3..7) Regards, Todd
Re: Tweaking junctions
On Oct 22, 6:41 pm, dam...@conway.org (Damian Conway) wrote: Dave Whipp wrote: When this issue has been raised in the past, the response has been that junctions are not really intended to be useful outside of the narrow purpose for which they were introduced. Hmm. There are intentions, and then there are intentions. I know what I intended when I invented the original idea, and it wasn't just the narrow purpose for which they were added to Perl 6. :-) Problem 2 could be solved by defining a new (and public!) C.eigenstates method in the Junction class. [...] I think that you're proposed solution is a bit too specific: That's because I didn't explain Part B of my nefarious plan! namely that, if you'd only give me proper eigenstates, I'd give you an even nicer alternative. I actually think that the meta doesn't belong on the operator at all (though I have no problem with that idea in itself). Instead, I think the meta should be placed on the data (which, of course, is what any(), all(), one(), and none() already do). So I'm going to go on to propose that we create a fifth class of Junction: the transjunction, with corresponding keyword Cevery. [snip] I'm probably missing something, but wouldn't it have been easier to write that module by using eval STRING to create all of those infix operators? Start with a list of the names of the operators, generate a string containing all four argument variations for each operator, then eval it.
Re: Tweaking junctions
Ben Goldberg asked: I'm probably missing something, but wouldn't it have been easier to write that module by using eval STRING to create all of those infix operators? Sure. But the module is already slow to start up. I was concerned that it would get even slower with an embedded eval. But, in the name of maintainability, I probably should benchmark it both ways. Thanks for pointing that out, Ben. Damian
Re: Tweaking junctions
Damian Conway wrote: So I'm going to go on to propose that we create a fifth class of Junction: the transjunction, with corresponding keyword Cevery. [...] say (^10 G[] one(3,7)); 3 4 5 6 which could also be: say every(^10) one(3,7); # Every value up to 10 that's greater than 3 or 7 but not both [...] I just think those all read much better than the (otherwise excellent) meta-operator. In much the same way that the existing junctive types read better than their functional or operator-oriented alternatives. I think that the two proposals are equivalent, in the sense that either can be trivially implemented using the other. So it is indeed a question of which one reads better and fits in better. And I agree, your Cevery proposal does read better (at least to an English speaker). However, I am a little concerned that the transjunction magically changes an operator that returns a Boolean value into one that returns a list. If the usage of a transjuction is non-local to its creation then this could result in surprises, and hence frustrating debugging sessions. If I wanted to write intentionally confusing code (which sometimes happens due to carelessness) then I might take advantage of the fact that Cevery is, in English, a synonym for Call, not Cany: if every(@values) one(3..7) {...} I'd like these in the core language (for performance and universal accessibility), but if not, I already have a nearly-complete implementation of a module implementing them, which runs successfully on the current release of Rakudo*. I append said model for your amusement (and suggestions!). ++ Dave.
Re: Tweaking junctions
Dave Whipp noted: I think that the two proposals are equivalent, in the sense that either can be trivially implemented using the other. Agreed. However, I am a little concerned that the transjunction magically changes an operator that returns a Boolean value into one that returns a list. Technically, it turns the operator into one that returns a transjunction. The surprise occurs because tranjunctions self-eigenvalue in a list context. That's a huge convenience in direct usages, but not an essential component of the proposal if indeed it proves too surprising in indirect usages. However, if we did lose that feature then usages like: say eigenvalues every(@number) one(3,7); sacrifices more than a little of the construct's original appeal, I think. If I wanted to write intentionally confusing code (which sometimes happens due to carelessness) then I might take advantage of the fact that Cevery is, in English, a synonym for Call, not Cany: if every(@values) one(3..7) {...} Yes, Ted Z. pointed out to me that, as the name of this construct, every has ambiguity and synonym issues. Other possibilities are: select(@values) one(3..7) those(@values) one(3..7) whichever(@values) one(3..7) itemize(@values) one(3..7) extract(@values) one(3..7) ...of which, only Cselect really seems a good alternative. Any other suggestions most welcome! Damian
Re: Tweaking junctions
Damian Conway wrote: Yes, Ted Z. pointed out to me that, as the name of this construct, every has ambiguity and synonym issues. Other possibilities are: select(@values) one(3..7) those(@values) one(3..7) whichever(@values) one(3..7) itemize(@values) one(3..7) extract(@values) one(3..7) ...of which, only Cselect really seems a good alternative. Any other suggestions most welcome! My suggestion is, of course, to move it to the operator: @values G one(3..7) which has a pleasing (to my mind) symmetry with Perl6-isms such as @values X* 2 Failing that, perhaps Cfilter might work (though I always find myself wanting to qualify a filter as either filter-in or filter-out).
Re: Tweaking junctions
-BEGIN PGP SIGNED MESSAGE- Hash: SHA1 On 10/22/10 13:00 , Dave Whipp wrote: Damian Conway wrote: I've been thinking about junctions, and I believe we may need a small tweak to (at least) the jargon in one part of the specification. When this issue has been raised in the past, the response has been that junctions are not really intended to be useful outside of the narrow purpose for which they were introduced. It occurs to me: If their purpose is that narrow, why are they wasting conceptual space in the core language? - -- brandon s. allbery [linux,solaris,freebsd,perl] allb...@kf8nh.com system administrator [openafs,heimdal,too many hats] allb...@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH -BEGIN PGP SIGNATURE- Version: GnuPG v2.0.10 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkzC5gsACgkQIn7hlCsL25Wp8ACbBzcg2kACC/hmtTD1GUGe2lD2 2ucAmgNSPb5F6T3SqqHMvvG7UzFR9M3D =1x5F -END PGP SIGNATURE-
Re: Tweaking junctions
Brandon mused: It occurs to me: If their purpose is that narrow, why are they wasting conceptual space in the core language? Well, mainly because their purpose isn't narrow at all: it's parallelized data comparisons (all(@values) $threshold), and multiway comparisons (all(@values) ~~ any(@ranges)), and distributed sub calls on a set of arguments (foo(any @alternatives), and subroutine call parallelism (foo bar)($arg), and type unions (my Wax|Polish $shimmer), and type intersections (my $coefficient where Num 0..1), and parallel lookup of arrays and hashes ($name ~~ %known_aliases{any $suspect}), and multiway existence checks (if any %f...@args} :exists), and convenient multiple file tests ($fh ~~ :r :w :!x), and overlapping or exhaustive matches (my $results = m:ex/foo/), and complex matching logic expressed declaratively (my $is_valid = Num {$_0} | Str /zero|one/). And, of course, they're in the core because there's no point in having that much power and usefulness if it's not fast too. Damian
Re: Tweaking junctions
In general I like where this is going but need a little hand holding here- I'm not an expert on junctions or anything perl6- So I'm going to go on to propose that we create a fifth class of Junction: the transjunction, with corresponding keyword Cevery. It seems that by these definitions every isn't quite a junction- every(@list) comparision-op value to mean ... grep * comparision-op value, @list; You'll need to specify but not necessarily in the same order if you want junctive autothreading to work on every as it does with other junctions. In which case it should probably be returning a junction and not an ordered list. Which reminds me, .eigenvalues strictly speaking is a set and not a list. !eigenstates can be whatever the internal representation is... not that I've checked the synopsis on that... And while I like the cleanliness of the every expressions I'm still having trouble seeing why every should behave differently from any, all. Maybe every isn't a junction? Maybe junctions in general need to behave a little differently when being compared against then they do now, so the need for every goes away?
Re: Tweaking junctions
In general I like where this is going but need a little hand holding here- I'm not an expert on junctions or anything perl6- So I'm going to go on to propose that we create a fifth class of Junction: the transjunction, with corresponding keyword Cevery. It seems that by these definitions every isn't quite a junction- It's an unordered collection of values that can be used like a single value, is parallelizable, and has an intrinsic associated operation. That's pretty much a junction. The only difference is that the associated operation is (in a sense) Cgrep, rather than C, C||, C^^, or C! You'll need to specify but not necessarily in the same order if you want junctive autothreading to work on every as it does with other junctions. You're quite right. I should have been more specific about that. And, yes, I certainly do want to leave open the potential for parallel evaluation. In which case it should probably be returning a junction and not an ordered list. If you look at the implementation I appended, any comparison on a transjunction returns a transjunction. But it turns out to be much more useful if they also collapse to a list, which is why I implemented them as a role added to arrays/lists. Which reminds me, .eigenvalues strictly speaking is a set and not a list. Sure, but again, it's much more useful as a list. ;-) And while I like the cleanliness of the every expressions I'm still having trouble seeing why every should behave differently from any, all. Maybe every isn't a junction? Maybe not, in the strictest sense. But it has many of the same characteristics and is extremely useful in many of the same kinds of ways as a duck^Wjunction. Maybe junctions in general need to behave a little differently when being compared against then they do now, so the need for every goes away? Interestingly, that was how they were originally defined (back when they were called Quantum::Superpositions). Comparing a superposition produced a superposition of just those values for which the comparison was true. But I agree with Larry's decision to make junctions behave normally wrt comparisons. They're much easier to understand the way they are now. I am merely trying to reintroduce the useful extra concept of a collection of numbers that filters under comparisons. But if people aren't comfortable, I'm happy to leave it as a module (especially since it's already written). I would hate this (very worthwhile) discussion to distract from the key point of the original post: namely, that the private C!eigenstates is misnamed, and a public C.eigenvalues is missing. Damian
Re: Tweaking junctions
Damian Conway wrote: I've been thinking about junctions, and I believe we may need a small tweak to (at least) the jargon in one part of the specification. When this issue has been raised in the past, the response has been that junctions are not really intended to be useful outside of the narrow purpose for which they were introduced. Problem 2 could be solved by defining a new (and public!) C.eigenstates method in the Junction class. [...] I think that you're proposed solution is a bit too specific: it assumes that when you say eigenstates, what you really mean is the set of values that would compare equal to. I have no problem with that concept, expect that I think that it would be better realized as a meta-operator -- I previously proposed G[op] as that operator (G for grep) -- to reinforce the idea that you only get to peer into a junction if you state the operator. sub factors (Num $n) { ( $n/any(1..$n) ).eigenstates.grep({ $^q.Int == $q }); } could be: sub factors (num $n) { ^$n G[==] $n/any(1..$n) } if $passwd ~~ UNACCEPTABLE { say Unacceptable password. Don't use any of these:; say UNACCEPTABLE.eigenstates¬».fmt(\t%s\n); } Could be if $passwd ~~ UNACCEPTABLE { say Unacceptable password. Don't use any of these:; say (::Str G[eq] UNACCEPTABLE)».fmt(\t%s\n) } And, of course: method eigenstates( Junction $v : ) { ::Any G=== $v } Operations other than equality could be used: say (^10 G[] one(3,7)); 3 4 5 6 I suggest a meta-op instead of simply using the grep method to strongly imply that the implementation would need to be analytic in nature.
Re: Tweaking junctions
Dave Whipp wrote: When this issue has been raised in the past, the response has been that junctions are not really intended to be useful outside of the narrow purpose for which they were introduced. Hmm. There are intentions, and then there are intentions. I know what I intended when I invented the original idea, and it wasn't just the narrow purpose for which they were added to Perl 6. :-) Problem 2 could be solved by defining a new (and public!) C.eigenstates method in the Junction class. [...] I think that you're proposed solution is a bit too specific: That's because I didn't explain Part B of my nefarious plan! namely that, if you'd only give me proper eigenstates, I'd give you an even nicer alternative. I actually think that the meta doesn't belong on the operator at all (though I have no problem with that idea in itself). Instead, I think the meta should be placed on the data (which, of course, is what any(), all(), one(), and none() already do). So I'm going to go on to propose that we create a fifth class of Junction: the transjunction, with corresponding keyword Cevery. Then we define: every(@list) comparision-op value every($junction) comparision-op value to mean repectively: grep * comparision-op value, @list; grep * comparision-op value, $junction.eigenstates; as well as the reverses: value comparision-op every(@list) value comparision-op every($junction) to mean respectively: grep value comparision-op *, @list; grep value comparision-op *, $junction.eigenstates; So then we get: sub factors (Num $n) { ( $n/any(1..$n) ).eigenstates.grep({ $^q.Int == $q }); } [which] could be: sub factors (num $n) { ^$n G[==] $n/any(1..$n) } could instead be: sub factors (Num $n) { every( $n/any(1..$n) ) ~~ { $^q.Int == $q }); # Every quotient of N divided by any lesser integer, where the quotient is also an integer } and: say UNACCEPTABLE.eigenstates¬».fmt(\t%s\n); [which] could be say (::Str G[eq] UNACCEPTABLE)».fmt(\t%s\n) could also be: say (every(UNACCEPTABLE) ~~ ::Str)».fmt(\t%s\n) # Everything unacceptable that's a string, formatted as follows... Operations other than equality could be used: say (^10 G[] one(3,7)); 3 4 5 6 which could also be: say every(^10) one(3,7); # Every value up to 10 that's greater than 3 or 7 but not both And so on: say every(factor_of($x) factor_of($y)) 10; # Every common factor of both X and Y that's greater than 10 say every(@coefficient) ~~ 0..1; # Every coefficient in the range 0 to 1 say every(@string) ~~ /target/; # Every string that matches the target pattern I just think those all read much better than the (otherwise excellent) meta-operator. In much the same way that the existing junctive types read better than their functional or operator-oriented alternatives. I'd like these in the core language (for performance and universal accessibility), but if not, I already have a nearly-complete implementation of a module implementing them, which runs successfully on the current release of Rakudo*. I append said model for your amusement (and suggestions!). Damian -cut--cut--cut--cut--cut--cut--cut--cut- module Transjunctions; # Add missing public method (and equivalent function) to junctions # Call them .eigenvalues(), so as not to clash with existing misnamed !eigenstates()... use MONKEY_TYPING; augment class Junction { method eigenvalues (Junction $j:) { my @values = $j.eigenstates; @values .= map({ $^val.?eigenstates // $^val }) while @values.grep(Junction); return uniq(@values).grep($j) does ::Transjunction; } sub eigenvalues (Junction $j) is export { $j.eigenvalues } } # Transjunctions are just lists that autofilter when matched... role Transjunction { method ACCEPTS (Mu $filter) { self.grep($filter) } } # Convenient functions to build transjunctions... multi sub every (*...@list is copy) returns Transjunction is export { return @list does Transjunction; } multi sub every (Junction $j) is export { return eigenvalues($j) does Transjunction; } # Comparing against a transjunction filters it with the comparison # Rakudo star seems to require we provide two versions, though # I had expected the Mu version ought to handle both Junctions and Anys... multi sub infix:«» (Transjunction $list, Mu $n) is export { $list.grep( * $n ) does Transjunction } multi sub infix:«» (Transjunction $list,$n) is export { $list.grep( * $n ) does Transjunction } multi sub infix:«» (Transjunction $list, Mu $n) is export { $list.grep( * $n ) does Transjunction } multi sub infix:«» (Transjunction $list,$n) is export { $list.grep( * $n ) does Transjunction } multi sub infix:«=» (Transjunction $list, Mu $n) is export { $list.grep( * = $n ) does