multi scoping
Here's a good Perl 6 final exam question: Spot the mistake (hint: it's not in the math): module Complex; sub i() is export { Complex.new(0,1) } multi sub infix:<+> (Complex $left, Complex $right) is export { Complex.new($left.real + $right.real, $left.imag + $right.imag); } multi sub infix:<*> (Complex $left, Complex $right) is export { Complex.new( $left.real * $right.real - $left.imag * $right.imag, $left.real * $right.imag + $right.real * $left.imag) } # ... Give up? When you add two complex numbers, you get into an infinite loop. That's because infix:<+> adds things using the plus operator, which is, you guessed it, infix:<+>. Now you'd think that multimethods would handle that, but they don't because by defining "multi sub infix:<+>" you are defining a *package* operator which *masks* the global operator! So this turns into infinite recursion. By the way, this was done in Set.pm. Pugs's scoping rules were recently fixed, so suddenly the subtraction operator became an infinite loop. I see two solutions: 1) Disallow a "multi" to introduce a symbol if it masks another symbol of the same name. You must explicitly ask for masking by predeclaring the symbol without "multi" somewhere before you define the multi. This still doesn't solve anything when you accidentally put "is export" on an operator (as was done in Set.pm). 2) Make multi automatically find the symbol that you would have referred to if the definition had not been there, and add a multi case to that symbol. So in the example above, the innermost infix:<+> that existed before you said "multi" was *infix:<+>, so the multi definition would basically infer that you meant to say multi *infix:<+> and do the right thing. There could also be some combination of the two. Maybe something else entirely. Ideas? Luke
Re: multi scoping
On Sun, Sep 04, 2005 at 07:55:21 +, Luke Palmer wrote: > Here's a good Perl 6 final exam question: > > Spot the mistake (hint: it's not in the math): > > module Complex; > > sub i() is export { > Complex.new(0,1) > } > multi sub infix:<+> (Complex $left, Complex $right) is export { > Complex.new($left.real + $right.real, $left.imag + $right.imag); > } > multi sub infix:<*> (Complex $left, Complex $right) is export { > Complex.new( > $left.real * $right.real - $left.imag * $right.imag, > $left.real * $right.imag + $right.real * $left.imag) > } > # ... > > Give up? > > When you add two complex numbers, you get into an infinite loop. > That's because infix:<+> adds things using the plus operator, which > is, you guessed it, infix:<+>. Now you'd think that multimethods > would handle that, but they don't because by defining "multi sub > infix:<+>" you are defining a *package* operator which *masks* the > global operator! So this turns into infinite recursion. I always saw scoping of multis as something that applies to the variants... multi sub foo { } { my multi sub foo { } # the second variant is just for this scope, but neither masks # the other } > You must explicitly ask for masking I think this should be an option... You can either mask off a single variant by declaring one that overrides it in a tighter scope, with yadda yadda as the body, or you could ask a whole scope to be omitted from the possible variants. > 2) Make multi automatically find the symbol that you would have > referred to if the definition had not been there, and add a multi case > to that symbol. So in the example above, the innermost infix:<+> that > existed before you said "multi" was *infix:<+>, so the multi > definition would basically infer that you meant to say multi > *infix:<+> and do the right thing. I don't agree with this... It takes the lexical scoping semantics out of things. If multi's would cascade WRT to the scope of their definition, but not mask, things should DWIM most of the time. Please remember that when you declare a new multi you are usually ammending to a commonly used operator, in order to extend it's behavior in the scope that there is new data which much behave accordingly. There is one more problem though: class Complex { multi sub &infix:<*> { ... } } package Moose; use Complex; use SomeMathLib ; ... function($some_complex_number); # if function calls infix:<*> on # it's operand, somehow... What happens? -- () Yuval Kogman <[EMAIL PROTECTED]> 0xEBD27418 perl hacker & /\ kung foo master: /me spreads pj3Ar using 0wnage: neeyah!!! pgpTDDK5o2dCa.pgp Description: PGP signature
Re: multi scoping
On 9/4/05, Yuval Kogman <[EMAIL PROTECTED]> wrote: > I always saw scoping of multis as something that applies to the > variants... > > multi sub foo { > > } > > { > my multi sub foo { > > } > > # the second variant is just for this scope, but > neither masks > # the other > } Reading over A12 again (in the section "Multiple Dispatch"), it appears that you are right. So let's figure out if that's actually the right way or the wrong way. I don't understand the second-to-last paragraph of that section, which apparently explains why single variants mask. > > You must explicitly ask for masking > > I think this should be an option... You can either mask off a single > variant by declaring one that overrides it in a tighter scope, with > yadda yadda as the body, or you could ask a whole scope to be > omitted from the possible variants. Just to clarify, is this what you are suggesting? multi foo (Int $x) { $x + 1 } multi bar (Int $x) { $x + 1 } { my sub foo ($x) {...} my multi foo (Str $x) { "x" ~ $x } my multi bar (Str $x) { "x" ~ $x } bar("hi"); # xhi bar(1);# 2 foo("hi"); # xhi foo(1);# error, no compatible sub found } Though if that's the case, then it almost feels to me like the "multi" should go before the "my". But that would be screwing with the consistency of the grammar a little too much, I think. > > 2) Make multi automatically find the symbol that you would have > > referred to if the definition had not been there, and add a multi case > > to that symbol. So in the example above, the innermost infix:<+> that > > existed before you said "multi" was *infix:<+>, so the multi > > definition would basically infer that you meant to say multi > > *infix:<+> and do the right thing. > > I don't agree with this... It takes the lexical scoping semantics > out of things. Oh, by the way, I was suggesting that a *bare* "multi" would do this. If you said "our" or "my", you are stating exactly what you mean. Well, almost (what does "my multi foo" mean if there is an outer lexical multi foo -- mask or append?). > There is one more problem though: > > class Complex { > multi sub &infix:<*> { ... } > } > > package Moose; > use Complex; > use SomeMathLib ; > > ... > > function($some_complex_number); # if function calls infix:<*> on > # it's operand, somehow... What happens? This is actually my *whole* problem, and the reason that I don't think that variants should mask, but entire symbols should mask. It seems like you'd use a lexically scoped multi variant about as much as you'd use a lexically scoped method on a class. Can you think of a use for that? Isn't the point of lexical scoping so that you don't have to worry whether somebody else called something the same thing you did? I can picture this: multi combine (Any $x, Any $y) { ZCombinator.new($x, $y) } multi combine (@x, @y) { ZList.new([ @x, @y ]) } # concat multi combine (Str $x, Str $y) { ZStr.new($x ~ $y) } # ... many lines pass ... sub process($x, $y) { my multi combine (Any $a, Any $b) { die "Cannot combine" } my multi combine (Int $a, Int $b) { $a + $b } return combine($x, $y); } process("Foo", "Bar"); # Gets back a... what? ZStr? What the heck is that Clearly the author of process intended that two integers get added and anything else dies. He was not aware of the combine defined many many lines above. But he was smart and made his multis lexically scoped so he didn't have to worry. Really, he should have written process like so: sub process($x, $y) { my sub combine ($a, $b) {...} my multi combine (Any $a, Any $b) { die "Cannot combine" } my multi combine (Int $a, Int $b) { $a + $b } return combine($x, $y); } That case, to me, seems a lot more common than lexically overriding a multi variant. But I am open to counterexamples. Assuming we have no information about the frequency, there is another question to ask whether it makes sense to override or extend: Is it natural to do it both ways? That is, does it feel right? (Please, get your mind out of the gutter and pay attention! :-) It seems fairly natural when we go with the extension mechanism that A12 seems to propose. "multis extend, subs mask", so if you want to mask, you just define it as a sub first. It's a little awkward, but it'll do. The other way, however, is not nearly as natural: # let's say that he wanted process() to do what I said he didn't intend sub process($x, $y) { my multi combine (Any $x, Any $y) { OUTER::combine($x, $y) } my multi combine (Int $x, Int $y)
FYI: Lambda Calculus on Perl 6
Folks, I recently needed to write a series of codes on lambda calculus in perl. As MJD has shown Perl 5 can handle lambda calculus but I am beginning to get tired of whole bunch of 'my $x = shift' needed. our $ZERO = sub { my $f = shift; sub { my $x = shift; $x }}; our $SUCC = sub { my $n = shift; sub { my $f = shift; sub { my $x = shift; $f->($n->($f)($x)) }}}; our $ADD = sub{ my $m = shift; sub { my $n = shift; sub { my $f = shift; sub { my $x = shift; $m->($f)($n->($f)($x)) ; our $MULT = sub { my $m = shift; sub { my $n = shift; sub { my $f = shift; $m->($n->($f)) }}}; our $POW = sub { my $m = shift; sub { my $n = shift; $n->($m) }}; And I found that these can be made much, much simpler and more intuitive with Perl 6, even more so than scheme! our $ZERO = sub($f){ sub($x){ $x }}; our $SUCC = sub($n){ sub($f){ sub($x){ $f.($n.($f)($x)) }}}; our $ADD = sub($m){ sub($n){ sub($f){ sub($x){ $m.($f)($n.($f) ($x)) ; our $MULT = sub($m){ sub($n){ sub($f){ $m.($n.($f)) }}}; our $POW = sub($m){ sub($n){ $n.($m) }}; You can even make it simpler by removing dots but I leave it that way because it looks more like the original notation that way (i.e. zero := λf.λx.x). Runs perfectly fine on Pugs 6.2.8. Add the code below and see it for yourself. my $one = $SUCC.($ZERO); my $two = $SUCC.($one); my $four= $ADD.($two)($two); my $eight = $MULT.($two)($four); my $sixteen = $POW.($four)($two); for($one, $two, $four, $eight, $sixteen) -> $n { $n.(sub($i){ 1 + $i})(0).say }; Maybe we can use this for advocacy. Dan the Perl 6 User Now P.S. I am surprised to find Pugs does not include this kind of sample scripts.
Re: FYI: Lambda Calculus on Perl 6
On Mon, Sep 05, 2005 at 12:35:36PM +0900, Dan Kogai wrote: > And I found that these can be made much, much simpler and more > intuitive with Perl 6, even more so than scheme! > > our $ZERO = sub($f){ sub($x){ $x }}; > our $SUCC = sub($n){ sub($f){ sub($x){ $f.($n.($f)($x)) }}}; > our $ADD = sub($m){ sub($n){ sub($f){ sub($x){ $m.($f)($n.($f) > ($x)) ; > our $MULT = sub($m){ sub($n){ sub($f){ $m.($n.($f)) }}}; > our $POW = sub($m){ sub($n){ $n.($m) }}; Nice! Also the pointy notation may or may not be clearer: -> $f { -> $x { $x } } Also, you can use the & sigil: our &SUCC := sub(&n){ sub(&f){ sub(&x){ f(n(&f)(&x)) }}}; which may or may not be clearer... probably not, come to think about it. > P.S. I am surprised to find Pugs does not include this kind of > sample scripts. I'm surprised to find dan-san is not a Pugs committer. I've remedied this situation by sending you an invitation. The commit URL is the same as the anonymous checkout svn URL. Please commit them into the suitable examples/ subdirectory, probably "algorithms". Also please add yourself to AUTHORS. Welcome aboard! Thanks, /Autrijus/ pgpERLqoLpAGo.pgp Description: PGP signature