Exegesis 4 Available

pudge on 2002-04-03T04:50:36

Damian writes "Exegesis 4 is now on-line. There are some fundamental changes to Perl 6 since Apocalypse 4, so you might want to re-read that too -- to see what was Rule-2'd in the interim."


$^a and $^b # how assigned in example?

dcd on 2002-04-03T07:27:31

our %operator is private //= ( '*' => { $^a * $^b }, '/' => { $^a / $^b }, '~' => { ($^a + $^b) / 2 }, );

In the example above, and it's use below

push @stack, %operator{$token}(@args)

I don't see how $^a and $^b get assigned in pairs from @args?

I thought that the $^ magic variables had to be named.

Re:$^a and $^b # how assigned in example?

Damian on 2002-04-03T07:53:30


I don't see how $^a and $^b get assigned in pairs from @args ? I thought that the $^ magic variables had to be named.


A closure with "$^ magic variables" such as:


        { $^a * $^b }


is equivalent to an anonymous subroutine like this:


        sub ($^a, $^b) { $^a * $^b }


That sub takes two parameters, so when it's called with an array, the array is flattened to two values, which are then assigned to $^a and $^b in order. Just as with any other subroutine.

Magic $^ variables are only magic in that they allow a subroutine to be declared without an explicit sub keyword or an explicit parameter list. Instead, their parameter list is constructed implicitly: it's just the alphabetically sorted list of the names of all the magic $^ variables within the block.

And, because they're really just a normal subroutine (that is declared abnormally), such closures are certainly allowed to take named parameters. But they're not required to do so.

Re:$^a and $^b # how assigned in example?

Matts on 2002-04-03T10:29:42

Wow, that's going to make writing obfuscated code so much easier!

It's swings and roundabouts I guess...

pdcawley on 2002-04-03T18:45:59

Yes, you can use all the new expressiveness of Perl 6 in painfully obfuscatory ways, but it's also possible to use them to improve readability. For instance, on the perl6 language list we discussed how you'd declare an abstract method in Perl 6.

There's no formal way to do that in Perl5. You could use one of the various Class building helper modules on CPAN (Class::MethodMaker et al), or you could just do
sub an_abstract_method;
But how can a reader tell the difference between your abstract method and
sub a_forward_declaration;
say? Sure you could add a comment, but (speaking personally) any comment that isn't a 'class comment' at the top of the class declaration stating what the class is for generally starts me worrying that either the comment is unnecessary, or the code it's commenting on is too complex.

In Perl 6, the problem goes away:
method display_on ($a_medium) is abstract {...}
Simple, expressive, self documenting. And, the documentation stays in place through the vicissitudes of perl -MO=Deparse SomeFile.pm or whatever it's going to be called in Perl 6. Which is important when you're trying to write a refactoring browser.

Re:$^a and $^b # how assigned in example?

pdcawley on 2002-04-03T07:58:16

Well, @args is already a two item list, but I think that, as written, you'll see $^a becoming @args and $^b becoming undef.

The fix is simple though; just change the invocation to push @stack, %operator{$token}(*@args), unary '*' being the list flattening operator introduced in Apocalypse 3.

Re:$^a and $^b # how assigned in example?

Damian on 2002-04-03T08:48:12

Hmmmmm. Interesting point. I've asked Larry to clarify the semantics in such situations, and will post an update here when I get an answer.

Re:$^a and $^b # how assigned in example?

pdcawley on 2002-04-03T13:25:43

Even if *@args isn't required, I argue that it should be considered good style; you're making your assumptions explicit.

Re:$^a and $^b # how assigned in example?

Damian on 2002-04-03T21:33:27

Larry confirmed that it is required.
I'll be fixing it on-line ASAP.
Thanks for pointing out this bug in my wetware Perl 6 compiler! ;-)

Are these equal?

$Bob on 2002-04-03T19:08:01

Looking at An Aside: the "Smart Match" Operator on page 3, are these equal?

($elem =~ @array) == (@array =~ $elem)

($key =~ %hash) == (%hash =~ $key)

($value =~ (1..10)) == ((1..10) =~ $value)

($value =~ ('a', /\s/, 7)) == (('a', /\s/, 7) =~ $value)

Re:Are these equal?

TimToady on 2002-04-04T01:44:05

Yes.

Larry

Re:Are these equal?

hossman on 2002-04-06T20:34:33

Are you saying that the =~ is going to be defined such that ($paco =~ $yakko) == ($paco =~ $yakko) will allways be true? (or are you saying that just the 4 examples $Bob listed will allways be true)

What if $paco and $yakko are both sub refs? there's no garuntee that $yakko.($paco) will ever be equal to $paco.($yakko).

for that matter, what about the list behavior Damian describes...

if $value =~ ('a',/\s/,7) {...}
# true if $value is eq to 'a'
# or if $value contains whitespace
# or if $value is == to 7

That final example illustrates some of the extra intelligence that Perl 6's =~ has: When one of its arguments is a list (not an array), the "smart match" operator recursively "smart matches" each element and ORs the results together, short-circuiting if possible.

What happens in this scenerio...

if ($sub1 =~ ($sub2, $sub3)) { ... }

is it equivilent to ($sub1.($sub2) || $sub1.($sub3)) ? Or ($sub2.($sub1) || $sub3.($sub1)) ?

Re:Are these equal?

Damian on 2002-04-06T23:05:41

Are you saying that the =~ is going to be defined such that ($paco =~ $yakko) == ($paco =~ $yakko) will allways be true? (or are you saying that just the 4 examples $Bob listed will allways be true)
He's saying the latter. The matching rules are supposed to be "sensible" and "reasonable" (for sufficiently vague values of those two words ;-)

The table in A4 lists the behaviours of =~ under various combinations of arguments.

What if $paco and $yakko are both sub refs? there's no garuntee that $yakko.($paco) will ever be equal to $paco.($yakko).
That table doesn't mention the sub-ref/sub-ref case, but it would seem sensible and reasonable (to me, at least) that =~ would just try referential equality there. Of course, Larry's mileage may vary on that point.
for that matter, what about the list behavior Damian describes...
According to the table, it's reversible too.
What happens in this scenerio... if ($sub1 =~ ($sub2, $sub3)) { ... } is it equivilent to ($sub1.($sub2) || $sub1.($sub3)) ? Or ($sub2.($sub1) || $sub3.($sub1)) ?
Neither, I think. IMHO it should be distributed referential equality: $sub1 == $sub2 || $sub1 == $sub3

when %var

jaffray on 2002-04-07T23:57:14

In the example code, calc($$) stores a value "but true" in %var, which then allows get_data($) to use "given $data { when %var {...} }" and have the result be "key $data exists in %var" even though the usual hash/scalar match is "%var{$data} is true".

I suspect this would become an idiom with the given/when construct described in Apocalypse 4, and that worries me, since it looks like bad style - the meaning of the "when" condition is defined by an attribute attached to data elsewhere in the code. The condition behaves in an unexpected way if you look only at the local code, and the attribute could easily cause unexpected effects if the data gets used in other conditionals.

It's nice to be able to redefine truth, but to minimize confusion, it ought to be a relatively seldom occurrence...

It seems like the example of hash/scalar matching in the example code - if we have a result or a handler or whatever for the given value, then use it - is going to be really common. It also seems easier for hash/scalar matching to mean "scalar exists as a hash key", and write "when %var{$_}" when you want truth, than to have it mean "value associated with scalar is true", and write "when exists %var{$_}" when you want existence.

It's late in the game, and I'm sure this has already been debated half to death, but... I figured I'd toss in two cents anyway. :-)

Thanks for another great article.

Re:when %var

Damian on 2002-04-08T04:14:07

It also seems easier for hash/scalar matching to mean "scalar exists as a hash key", and write "when %var{$_}" when you want truth, than to have it mean "value associated with scalar is true", and write "when exists %var{$_}" when you want existence.

Personally, I would agree, especially from a "hash-keys-form-a-set" perspective. But we also have the case of sometimes wanting:

     when defined %var{$_} {...}
which might also be useful in the example.

So we have three viable alternatives and only one of them can be the DWIMish one.

When designing the original Switch.pm module (the prototype for given/whens) I chose the "is true" variant, rather than the "is defined" or "is existent" alternatives.

The main reason was because that it's still relatively easy to get the other behaviours:

    # Switch according to definedness...
    given { defined %data{^$key} } {
        when 'key1' {...}
        when 'key2' {...}
        ...
    }

    # Switch according to existence...
    given { exists %data{$^key} } {
        when 'key1' {...}
        when 'key2' {...}
        ...
    }

    # Switch according to either...
    sub def($key) { { defined %^hash{$key} } }
    sub ex($key)  { { exists  %^hash{$key} } }

    given %data {
        when def 'key1' {...}
        when ex  'key1' {...}
        when def 'key2' {...}
        when ex  'key2' {...}
        ...
    }

(By the way, Oh ye Gods of Use Perl, the lameness filter rejected the above post until I added enough pointless extra text here to satisfy it. That suggests to me that the lameness filter needs a couple of passes through itself. ;-)