Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

fallback semantics of list methods

1 view
Skip to first unread message

Moritz Lenz

unread,
Jun 14, 2008, 7:46:10 AM6/14/08
to perl6-c...@perl.org, Perl6
In the test suite and on IRC there was quite some confusion about how
list builtins are specced, and how they should behave in corner cases.

One is join():
our Str multi method join ( @values: Str $separator = ' ' )
our Str multi join ( Str $separator = ' ', *@values )

It is quite clear that the perl 5-style
join 'sep', $value, $value2;
invocation remains valid, and
@list.join($sep)
is the new method form.

The confusion arises what to do with
'str'.join('other_str')
should be.

Fallback semantics in S12 suggest that since no matching multi method is
found, subs are tried - that is, the expression is interpreted as
join('str', 'other_str')
yielding 'other_str'. t/spec/S29-list/join.t disagrees, and wants the
result to be 'str'.

Daniel Ruoso argued in favour of the tested behaviour, suggesting that
perhaps the specs should be updated accordingly, mostly because
('str').join('other_str')
would be confusing otherwise.

Patrick Michaud argued in favour of the specced behaviour, and I agree.
Mostly because nobody sane will write things like
'str'.join('other_str') with a literal string as the invocant in first
place. And if it's not a literal, and you want it to behave as list, the
invocant is either something that returns a list, or a variable with the
'@' sigil. We just need to be careful that everything that should return
really does that, even if it contains a single list.

Currently both pugs and rakudo make <a b> return a list (pugs actually
an array, but that's a different problem), but <a> is a Str.
If we'd just be more consistent and always return a list, I don't see a
problem with the spec. If not, it would be rather weird to have <a
b>.join('c') return 'abc', but <a>.join('c') return 'c'.

Consider
my $x = A.new();
$x.y = <a>;
say $x.y.join('b')
The output is different for the two possible declarations of class A:
class A { has $y is rw } # -> b
class A { has @y is rw } # -> a
not pretty IMHO.

So I see the following options:
1) be more consistent with what returns a list
2) Add a method with invocant Any for each list builtin
3) drop method fallback, thus disallowing Str.join(Str) outright
4) be lisp, make everything a list *g* (not serious)

I haven't thought a lot about the third option, and what it would mean
to the language as a whole, so I have no idea if it's a viable alternative.

Any thoughts on the topic are welcome. I hope I didn't confuse too much
here ;-)

Cheers,
Moritz

--
Moritz Lenz
http://moritz.faui2k3.org/ | http://perl-6.de/

Larry Wall

unread,
Jun 14, 2008, 12:20:09 PM6/14/08
to perl...@perl.org, perl6-c...@perl.org, Perl6
On Sat, Jun 14, 2008 at 01:46:10PM +0200, Moritz Lenz wrote:
: In the test suite and on IRC there was quite some confusion about how

: list builtins are specced, and how they should behave in corner cases.
:
: One is join():
: our Str multi method join ( @values: Str $separator = ' ' )
: our Str multi join ( Str $separator = ' ', *@values )
:
: It is quite clear that the perl 5-style
: join 'sep', $value, $value2;
: invocation remains valid, and
: @list.join($sep)
: is the new method form.
:
: The confusion arises what to do with
: 'str'.join('other_str')
: should be.
:
: Fallback semantics in S12 suggest that since no matching multi method is
: found, subs are tried - that is, the expression is interpreted as
: join('str', 'other_str')
: yielding 'other_str'. t/spec/S29-list/join.t disagrees, and wants the
: result to be 'str'.

I want the result to be 'str'.

: Daniel Ruoso argued in favour of the tested behaviour, suggesting that
: perhaps the specs should be updated accordingly, mostly because
: ('str').join('other_str')
: would be confusing otherwise.

More to the point,

$unknown.join('other_str')

needs to work well, I think. And that means even if $unknown doesn't
know whether it's singular or plural.

: Patrick Michaud argued in favour of the specced behaviour, and I agree.


: Mostly because nobody sane will write things like
: 'str'.join('other_str') with a literal string as the invocant in first
: place. And if it's not a literal, and you want it to behave as list, the
: invocant is either something that returns a list, or a variable with the
: '@' sigil. We just need to be careful that everything that should return
: really does that, even if it contains a single list.

Patrick and I discussed it on the phone the other day and came to an
understanding, I think.

: Currently both pugs and rakudo make <a b> return a list (pugs actually


: an array, but that's a different problem), but <a> is a Str.
: If we'd just be more consistent and always return a list, I don't see a
: problem with the spec. If not, it would be rather weird to have <a
: b>.join('c') return 'abc', but <a>.join('c') return 'c'.

<a>.join('c') should return 'a'.

: Consider
: my $x = A.new();
: $x.y = <a>;
: say $x.y.join('b')
: The output is different for the two possible declarations of class A:
: class A { has $y is rw } # -> b
: class A { has @y is rw } # -> a
: not pretty IMHO.

Indeed.

: So I see the following options:


: 1) be more consistent with what returns a list
: 2) Add a method with invocant Any for each list builtin
: 3) drop method fallback, thus disallowing Str.join(Str) outright
: 4) be lisp, make everything a list *g* (not serious)
:
: I haven't thought a lot about the third option, and what it would mean
: to the language as a whole, so I have no idea if it's a viable alternative.

I have been advocating for 2, and am considering doing 3 as well.
There's more than one way to do 2--we could, for instance, have some
magical syntax for saying that a class's invocant may be converted
from Any. But what Patrick and I decided on Wednesday was that, for
now, we'd go with the simplest approach, which is not to invent any new
syntax or semantics, but simply have the Prelude install Any methods
as defaults for universal methods such as .join that want to convert
the invocant to a particular type across the language as a whole.
No fallback is then necessary, and the fact that the Prelude is
defining language-wide default semantics is not a problem at all,
because that's precisely what the Prelude is for. And I don't see
any problem with the Any class claiming to own .join since subclasses
can still override if the like.

Larry

0 new messages