Project

General

Profile

Feature #12125

Proposal: Shorthand operator for Object#method

Added by Papierkorb (Stefan Merettig) almost 2 years ago. Updated 3 months ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:74036]

Description

Hello,

The & operator lets one pass a #call-able object as block.

Really useful feature, but at the moment, if you want to pass a Method this way the syntax is not really concise:

Dir["*/*.c"].map(&File.method(:basename))

More often than not, at least I end up writing this instead .map{|a| File.basename a} which isn't that great either.

Thus, I want to propose adding a short-hand operator to the ruby language, which simply calls #method on an Object.

It could look like this: an_object->the_method which is 100% equivalent to doing an_object.method(:the_method)

I'm reusing the -> operator which is already used for the stabby lambda. But I think it makes sense: You have an object,
and from that object you point at a method to get it as Method.

With this, the example from above becomes: Dir["*/*.c"].map(&File->basename)

I attached a proof of concept patch. When you apply this to trunk, you can try the example above yourself.
Do note however that this PoC also breaks stabby lambda for the moment. I'll work on fixing that the following
days.

Thank you for reading,
Stefan.

method_shorthand.diff (740 Bytes) method_shorthand.diff Papierkorb (Stefan Merettig), 02/28/2016 09:36 PM
dot-symbol.patch (554 Bytes) dot-symbol.patch mame (Yusuke Endoh), 03/14/2016 04:43 PM

Related issues

Has duplicate CommonRuby - Feature #13581: Syntax sugar for method referenceOpen

History

#1 [ruby-core:74037] Updated by Papierkorb (Stefan Merettig) almost 2 years ago

The & operator lets one pass a #call-able object as block.

I meant #to_proc-able objects, sorry for the confusion.

#2 [ruby-core:74038] Updated by matz (Yukihiro Matsumoto) almost 2 years ago

I like the idea of short hand notation for Object#method(), but I don't think -> is a good idea.

Matz.

#3 [ruby-core:74050] Updated by zverok (Victor Shepelev) almost 2 years ago

For this kind of "conceptual" methods I sometimes define just one-letter shortcuts in core_ext.rb, like .map(&File.m(:basename)). I'm not sure, though, if any of existing/popular libraries will struggle from such kind of solution (or may be it would not play really well with local variables, which are frequently have one-letter names).

#4 [ruby-core:74053] Updated by Papierkorb (Stefan Merettig) almost 2 years ago

Yukihiro Matsumoto wrote:

but I don't think -> is a good idea.

Other options I can think of are:

  • Using .>: the_object.>a_method While its look is nearer to a normal method call (Which I think is a plus), I fear that the period would be hard to see in some fonts. Another plus is that this would be a entirely new operator, so no (unintentional?) breaking changes in the parser.
  • Using <..>: the_object<a_method> Inspired by the look of generics/templates in other programming languages. Should not clash with existing code and parsers and is well-readable in fonts I guess. Maybe it doesn't read as well anymore though, and the intention of <..> may not be that clear at first to someone who doesn't know the syntax (yet). No idea if that is a concern.
  • Using &>: the_object&>a_method Also readable in any font I can think of. It's a spin on &., reading like "and this"
  • Using |>: the_object|>a_method I think the Elixir language has this operator too (albeit with other semantics?). Its read like "and pipe it through this", so maybe it reads a bit like a shell script too?

Regards,
Stefan

#5 [ruby-core:74055] Updated by ksss (Yuki Kurihara) almost 2 years ago

How about this one?

class UnfoundMethod
  def initialize(receiver)
    @receiver = receiver
  end

  def method_missing(name, *args, &block)
    @receiver.method(name)
  end
end

module UnfoundMethodAttacher
  def method(name=nil)
    if name
      super
    else
      UnfoundMethod.new(self)
    end
  end
end

Object.prepend UnfoundMethodAttacher

File.method #=> #<UnfoundMethod receiver=File>
File.method.basename #=> #<Method: File.basename>
Dir["*/*.c"].map(&File.method.basename) #=> ["foo.c", "bar.c"]

#6 [ruby-core:74056] Updated by funny_falcon (Yura Sokolov) almost 2 years ago

Please don't do this!!! No need to make the language more complex just to solve such small issue!!!

If you want to do something pretty, then whole closure syntax should be simplified, not just call to '#method'.

Dir["*/*.c"].map{File.basename(_0)} # where `_0` is magic var

Then bytecode compiler may optimize it to .map(&File.method(:basename))

But it then allows to do more pretty things, for example:

# dumps key=>value pairs
myhash.each{|k,v| puts "#{k}=>#{v}"}
# do it in shorter way
myhash.each{puts "#{_0}=>#{_1}"}

#7 [ruby-core:74064] Updated by shevegen (Robert A. Heiler) almost 2 years ago

I think the &File->basename looks confusing since we also have
-> standalone now.

object->method reminds me a lot of php/perl and ruby uses the
prettier . instead, object.method.

I also think that :

  .map{File.basename(_0)} # where `_0` is magic var

Is not good either. I like _ as a variable name a lot but on
its own, not with extra. :)

Yuki Kurihara's proposal is somewhat better as he does not
have to use special constructs/tokens.

Dir["*/*.c"].map(&File.method.basename)

I believe that crystal allows some parameter for &; and I think
there have been earlier proposals in ruby too.

But I think it makes sense: You have an object, and from that
object you point at a method to get it as Method.

I do not think that this argument is a good one because the ->
is used in a dissimilar way, akin to Proc.new / lambda,
whereas your syntax proposal would be more similar to php
and perl syntax style, which I think will be confusing in
addition to -> already having another method. So this is
not good in my opinion.

#8 [ruby-core:74072] Updated by nobu (Nobuyoshi Nakada) almost 2 years ago

Stefan Merettig wrote:

  • Using .>: the_object.>a_method
  • Using <..>: the_object<a_method>

These two conflict with existing syntax and break compatibility.
Note that object.>(other) is a valid method call.

#9 [ruby-core:74073] Updated by nobu (Nobuyoshi Nakada) almost 2 years ago

  • Description updated (diff)

#10 [ruby-core:74293] Updated by jwmittag (Jörg W Mittag) almost 2 years ago

A proposal that has existed for years, if not decades, is to deprecate usage of the :: double colon binary infix namespace operator for message sends, and instead re-use it for method references:

Dir["*/*.c"].map(&File::basename)

This is also the syntax chosen by Java for method references.

There is one big problem, though: ambiguity with constant references for methods which start with an uppercase letter. Maybe, it would be possible to require parentheses in that case?

%w[1 2 3].map(&::Integer())

#11 [ruby-core:74294] Updated by jwmittag (Jörg W Mittag) almost 2 years ago

It would be nice if we could find symmetric syntax for getting an UnboundMethod from a module.

#12 [ruby-core:74299] Updated by mame (Yusuke Endoh) almost 2 years ago

+1 for this proposal. How about recv.:fname?

$ ./miniruby -e 'p Dir["*/*.c"].map(&File.:basename)'
["hypot.c", "memcmp.c", "erf.c", ...]

Fortunately, it brings no conflict.

A patch is attached. (dot-symbol.patch)

--
Yusuke Endoh mame@ruby-lang.org

#13 [ruby-core:74317] Updated by nobu (Nobuyoshi Nakada) almost 2 years ago

Yusuke Endoh wrote:

+1 for this proposal. How about recv.:fname?

dot_or_colon means that File:::basename also works?

#14 [ruby-core:74323] Updated by mame (Yusuke Endoh) almost 2 years ago

Nobuyoshi Nakada wrote:

Yusuke Endoh wrote:

+1 for this proposal. How about recv.:fname?

dot_or_colon means that File:::basename also works?

Yes it works. But I don't think it is important. Only .: is also okay to me.

--
Yusuke Endoh mame@ruby-lang.org

#15 [ruby-core:74352] Updated by Papierkorb (Stefan Merettig) almost 2 years ago

Yusuke Endoh wrote:

Nobuyoshi Nakada wrote:

Yusuke Endoh wrote:

+1 for this proposal. How about recv.:fname?

dot_or_colon means that File:::basename also works?

Yes it works. But I don't think it is important. Only .: is also okay to me.

The tetris-operator .: makes sense to me, but in respect to ::: I don't think we should allow "alternative" operators to do the same thing.

#16 [ruby-core:74353] Updated by funny_falcon (Yura Sokolov) almost 2 years ago

-1000

Please, don't!!!

I don't wonna Ruby to become Perl!!!

No more unnecessary syntax!!!

You all are not so weak! you are strong humans!!

You just can type a bit more characters!!!

#17 [ruby-core:74368] Updated by hanachin (Seiei Miyagi) over 1 year ago

How about File[.basename] ?

#18 [ruby-core:74396] Updated by shyouhei (Shyouhei Urabe) over 1 year ago

Just a status update: I heard from Matz in this month's developer meeting that however he wants this, all proposed syntax so far didn't charm him.

#19 [ruby-core:74402] Updated by nobu (Nobuyoshi Nakada) over 1 year ago

Seiei Miyagi wrote:

How about File[.basename] ?

It causes confusion if the receiver has #[] method.

#20 [ruby-core:74439] Updated by VeryBewitching (RP C) over 1 year ago

Stefan Merettig wrote:

Hello,

The & operator lets one pass a #call-able object as block.

Really useful feature, but at the moment, if you want to pass a Method this way the syntax is not really concise:

Dir["*/*.c"].map(&File.method(:basename))

More often than not, at least I end up writing this instead .map{|a| File.basename a} which isn't that great either.

Thus, I want to propose adding a short-hand operator to the ruby language, which simply calls #method on an Object.

It could look like this: an_object->the_method which is 100% equivalent to doing an_object.method(:the_method)

I'm reusing the -> operator which is already used for the stabby lambda. But I think it makes sense: You have an object,
and from that object you point at a method to get it as Method.

With this, the example from above becomes: Dir["*/*.c"].map(&File->basename)

I attached a proof of concept patch. When you apply this to trunk, you can try the example above yourself.
Do note however that this PoC also breaks stabby lambda for the moment. I'll work on fixing that the following
days.

Thank you for reading,
Stefan.

Dir["/.c"].map(File[&:basename])

From File, employ basename method. The referencing & should be applied to the method, not the class, as it is really the method you're concerned with. I'm not a language designer, but this is how I would expect this to work by looking at it.

#21 [ruby-core:74440] Updated by VeryBewitching (RP C) over 1 year ago

Another thought: Dir["/.c"].map(File.&basename)

#22 [ruby-core:83043] Updated by vassilevsky (Ilya Vassilevsky) 3 months ago

Is it possible to use a single colon for this?

object:name

File:basename

URI:parse

As far as I can see (not far, really, I don't even know C), it is currently not used for anything.

#23 [ruby-core:83044] Updated by nobu (Nobuyoshi Nakada) 3 months ago

A colon does too many things already, a ternary operator, a symbol literal, and a keyword argument.

#24 [ruby-core:83045] Updated by nobu (Nobuyoshi Nakada) 3 months ago

File[&:basename] and File.&basename are valid syntax already.

#25 Updated by k0kubun (Takashi Kokubun) 3 months ago

  • Has duplicate Feature #13581: Syntax sugar for method reference added

Also available in: Atom PDF