Issue #16264 has been updated by osyo (manga osyo).


Is `.:hoge` a `:hoge.to_proc` syntax sugar?
And, is `map(&self.:hoge)` and` map(&.:hoge)` different in meaning?
In Ruby, `self.hoge` and` hoge` without `self` often have the same meaning
But, it looks very strange that `map(&self.:hoge)` and` `map(&.:hoge)` have different meanings.


----------------------------------------
Feature #16264: Real "callable instance method" object.
https://bugs.ruby-lang.org/issues/16264#change-82278

* Author: zverok (Victor Shepelev)
* Status: Feedback
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
It is a part of thinking about the "argument-less call style" I already made several proposals about.

### Preface

***Argument-less call style*** is what I now call things like `map(&:foo)` and `each(&Notifications.:send)` approaches, and I believe  that naming the concept (even if my initial name is clamsy) will help to think about it. After using it a lot on a large production codebase (not only symbols, but method references too, which seem to be less widespread technique), I have a strong opinion that it not just "helps to save the keypresses" (which is less important), but also helps to clearer separate the concepts on a micro-level of the code. E.g. if you feel that `each(&Notifications.:send)` is "more right" than `select { |el| Notifications.send(el, something, something) }`, it makes you think about `Notifications.send` design in a way that allows to pass there _exactly_ that combination of arguments so it would be easily callable that way, clarifying modules responsibilities.

> (And I believe that "nameless block parameters", while helping to shorter the code, lack this important characteristic of clarification.)

### The problem

One of the problems of "argument-less calling" is passing additional arguments, things like those aren't easy to shorten:
```ruby
ary1.zip(ary2, ary3).map { |lines| lines.join("\n") }
#                                             ^^^^
construct_url.then(&HTTP.:get).body.then { |text| JSON.parse(text, symbolize_names: true) }
#                                                                  ^^^^^^^^^^^^^^^^^^^^^^
```

(BTW, here is [a blog post](https://zverok.github.io/blog/2019-10-18-each_with_object.html) where I show recently found technique for solving this, pretty nice and always existing in Ruby, if slightly esotheric.)

There's a lot of proposals for "partial applications" which would be more expressive than `.curry` ([guilty](https://bugs.ruby-lang.org/issues/16113) [myself](https://bugs.ruby-lang.org/issues/15301)), but the problematic part in all of this proposals is:

> **The most widespread "shortening" is `&:symbol`, and `Symbol` itself is NOT a functional object, and it is wrong to extend it with functional abilities.**

One of consequences of the above is, for example, that you can't use 2.6's proc combination with symbols, like `File.:read >> :strip >> :reverse`. You want, but you can't.

Here (while discussing aforementioned blog posts), I stumbled upon an idea of how to solve this dilemma.

### The proposal

I propose to have a syntax for creating a functional object that when being called, sends the specified method to its first argument. Basically, what `Symbol#to_proc` does, but without "hack" of "we allow our symbols to be convertible to functional objects". Proposed syntax:

```ruby
[1, 2, 3].map(&.:to_s)
```

Justification of the syntax:

* It is like `Foo.:method` (producing functional object that calls `method`)
* Orphan `.:method` isn't allowed currently (you need to say `self.:method` to refer to "current `self`s method"), and Matz's justification was "it would be too confusable with `:method`, small typo will change the result" -- which in PROPOSED case is not as bad, as `:foo` and `.:foo` both meaning the same thing;
* It looks kinda nice, similar to ([proposed and rejected](https://bugs.ruby-lang.org/issues/16120)) `map { .to_s }`  with my proposal, it is `map(&.:to_s)`, implying somehow applying `.to_s` to the previous values in the chain.

**The behavior:** `.:foo` produces object of class, say, `MethodOfArgument` (class name is subject to discuss)  which makes differences of "Proc created from Symbol" (existing internally, but almost invisible) obvious and hackable.

### Potential gains

* New object could be used in proc composition: `File.:read >> .:strip >> JSON.:parse >> .:compact`
* When both "method" and "method of argument" are proper functional objects, a new partial application syntax can be discussed, common for them both. For example (but **not necessary this method name!**)

  ```ruby
paragraph_hashes.map(&.:merge.with(author: current_author))
filenames.map(&File.:read.with(mode: 'rb'))
  ```
* (I believe at this point we'll be able to finally switch from discussing "show we extend Symbol with more callable-alike functionality" to just method's name and exact behavior)
* I am not an expert, but probably some optimizations could be applied, too
* Currently, `:sym.to_proc` is internally different from other proc, but this can't be introspected:

  ```ruby
:read.to_proc.inspect # => "#<Proc:0x0000556216192198(&:read)>"
                                                     # ^^^^^
  ```
*  Probably, exposure of this fact could lead to some new interesting metaprogrammin/optimization techniques.

### Transition

`:foo` and `.:foo` could work similarly for some upcoming versions (or indefinitely), with `.:foo` being more powerful alternative, allowing features like `groups_of_lines.map(&.:join.partial_apply(' '))` or something.

It would be like "real" and "imitated" keyword arguments. "Last hash without braces" was good at the beginning of the language lifecycle, but then it turned out that real ones provide a lot of benefits. Same thing here: `&:symbol` is super-nice, but, honestly, it is semantically questionable, so may be slow switch to a "real thing" would be gainful for everybody?..



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>