stevan / p5-mop-redux

A(nother) MOP for Perl 5
139 stars 36 forks source link

attribute defaults should have the invocant in $self, not $_ #116

Open doy opened 10 years ago

stevan commented 10 years ago

I dunno, I think $self outside of method bodies is really strange, I actually like $_ personally.

doy commented 10 years ago

It follows perl 6:

$ perl6 -e 'class Foo { has $.foo = self.build_foo; method build_foo { 1 } }; say Foo.new.foo'                                               
1
stevan commented 10 years ago

Yes and no, in Perl 6, $self is spelled self which gives it a very different feel

perlpilot commented 10 years ago

p5-mop could provide a self keyword for the invocant just like Perl 6 does.

An issue with this is how self would work with closures. p5-mop could also provide syntax to explicitly declare the invocant within a method (again, just like Perl 6) for those times when you need a var to close over: method foo ($self: @other_stuff) { ... } This also has the advantage (IMHO) that no specially-named scalars are magically appearing in scope; the programmer either declares a name or uses the self token.

doy commented 10 years ago

The more I think about it, the more I do kind of like self instead of $self. Having variables magically spring into existence without being declared is weird.

stevan commented 10 years ago

I am not against this

On Oct 16, 2013, at 6:55 PM, Jesse Luehrs notifications@github.com wrote:

The more I think about it, the more I do kind of like self instead of $self. Having variables magically spring into existence without being declared is weird.

— Reply to this email directly or view it on GitHub.

nicomen commented 10 years ago

+1 on self() ( and arguments() ) to match caller()...

stevan commented 10 years ago

arguments() gives me Javascript flashbacks, so I am not so sure about it. Also @_ is a core part of Perl anyway, whereas $self is not.

phaylon commented 10 years ago

My +1 would go to $self. It works inside closures (including Try::Tiny, Log::Contextual, and all other internal or external uses of the (&) prototype. It is always consistent (map { self->foo($_) } ... vs. mymap { $self->foo($_) ...). From a semantics and usage side, a lexical is the more simple, easy and understandable solution.

If the invocant variable has to be specified in every signature explicitly, then I have to add $self: to every signature if I want to be consistent across the project, because I usually use a couple closures. This takes a bit away of the visibility of having a changed invocant variable (method ($x) vs. method ($class: $x)).

If a self keyword is desired, how about it is provided along with a $self variable? Then you have the best of both worlds.

My main confusion however is this: What does the keyword variant buy you that a lexical doesn't, besides having to write one less character? Not to mention that I find it more odd to have a lower-case keyword to access a simple value that is only available in a specific scope. I'd expect something like that to look like like SUB.

stevan commented 10 years ago

@phaylon - the advantage of self is simply that it is not injecting a variable, to be honest, I prefer $self too, but I can see the logic in going with self.

doy commented 10 years ago

The main reasons are that nowhere else in the language does a lexical variable spring into existence without having been explicitly declared somewhere, and that perl 6 uses self by default (and we've been trying to follow perl 6 syntax when reasonable).

phaylon commented 10 years ago

But is it worth all of the above? In all my uses of syntax extensions on CPAN providing an implicit invocant lexical, I never longed for it to be a keyword instead. I would find it rather odd to refactor my signature and other invocant uses just because I want to use Try::Tiny. Also, other single scoped value access keywords in Perl 5 look like __SUB__, not like undef().

With regards to Perl 6, I'm trying to make the case that it might not be reasonable to complicate the semantics and make a fully consistent usage more wordy. How does Perl 6 deal with the issue of closures for example?

stevan commented 10 years ago

I think if self didn't work for thing as simple as Try::Tiny then it is a deal breaker and we stick with $self, but I am not sure that self won't work fine with Try::Tiny.

phaylon commented 10 years ago

I think it would be favorable to have it work in all closures. Meaning you can go from map { self->foo } ... to my_parallel_map { self->foo } ... to my_iterator_map { self->foo } ... (note that the last one might return something that closes over self, which might be invoked in the methods of different objects. It would basically have to turn self into a lexical-variable-masked-as-keyword, which we also don't have yet in Perl 5.

stevan commented 10 years ago

@doy, @phaylon makes some really good points here, just sayin'

doy commented 10 years ago

I imagine things like Try::Tiny would continue to work, because the closure is invoked immediately. The case that wouldn't work without marking something explicitly would be somewhere where you want to return something that closes over $self.

phaylon commented 10 years ago

There's also the case of closing over self in something you pass on:

self->_find_items(sub { self->_item_is_blue($_) })

Since that might or might not be executed by a method on the same object. It could also be delegated to a container object managing the items.

nicomen commented 10 years ago

I don't get it, if you want to close over, you close over it, you don't call a unclose-overable method/keyword from inside the block...

phaylon commented 10 years ago

The thing is, if you can't close over it at all, it severely limits it's use. If you can only close over it sometimes, it makes the matter more complicated.

doy commented 10 years ago

I'm still not entirely sure that "you can close over it when you declare it" is too complicated, although I could be wrong.

phaylon commented 10 years ago

My issue is, if I start out with using the keyword variant and suddenly have to close over it I have two choices: use both variants in a single method, or have a commit that changes large parts of the method for possibly just a single line. I'd just prefer to have a default, simple way to access the invocant without having to be explicit in every signature.

stevan commented 10 years ago

Gotta continue to agree with @phaylon, if you can't easily close over it, and have to do extra work in order to do that, thats a no-go for me.

doy commented 10 years ago

Another data point here is that we need to be able to close over attributes themselves, which effectively means implicitly closing over the invocant without the invocant necessarily explicitly existing (for instance, class Foo { has $!bar; method baz { return sub { $!bar } } }). So a lexical version of the invocant is going to need to exist regardless of what we decide otherwise.