Project

General

Profile

Feature #16667

Allow parameters to Symbol#to_proc and Method#to_proc

Added by jgomo3 (Jesús Gómez) 8 months ago. Updated 8 months ago.

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

Description

Allow parameters to Symbol#to_proc and Method#to_proc

So we can say:

ary.map(:dig.to_proc(:id))

Instead of

ary.map { |e| e.dig(:id) }

Oppening the posibilities to refine the & operator in the future, for something like:

ary.map(&(:dig, :id))

Related:

Updated by sawa (Tsuyoshi Sawada) 8 months ago

I am not sure how allowing parameters to Symbol#to_proc and Method#to_proc would make:

ary.map(:dig.to_proc(:id))

possible as Array#map does not take an argument. What do you want it to return?

Needless to say, it is entirely unclear how that, if possible, would replace:

ary.map { |e| e.dig(:id) }

or how it would be superior to writing like:

ary.map { _1.dig(:id) }

Updated by shevegen (Robert A. Heiler) 8 months ago

I think this or something similar was suggested in the past, even aside from
the two linked in proposals. So this may even be older than 5 years. :)

I do not remember what the conclusion was in all these proposals (and perhaps
I misremember, but I think it was suggested even before those ~5 years, perhaps
in another context).

As for the particular last syntax, I think this is quite inelegant:

ary.map(&(:dig, :id))

Ruby users may feel that both :dig and :id have the same "importance",
since they are symbols, but if I understood it correctly then your
example would imply that dig refers to a method-name, and id refers
to an argument; and to me, if this is the case, this looks ... strange.
But I understand that this was just an example.

I also admit that I often prefer oldschool ruby, so I am biased. :)

Edit: Actually zverok suggested something similar. I believe we should be very
cautious about whether it is needed/necessary. When a feature is added, obviously
people will use it, so all trade-offs should be considered (pro or con).

sawa wrote:

or how it would be superior to writing like:

 ary.map { _1.dig(:id) }

Yes, this may be an additional question. But I think _1 _2 and so forth are
more useful when there are more parameters. I found that I actually only
use _1 _2 for quick debugging/writing, and replace it with the "real", longer
name lateron anyway.

Updated by jgomo3 (Jesús Gómez) 8 months ago

I am not sure how allowing parameters to Symbol#to_proc and Method#to_proc would make:

ary.map(:dig.to_proc(:id))

possible as Array#map does not take an argument. What do you want it to return?

Currently, to_proc on symbols creates a Proc that receives an object and calls on it the method with name equal to the symbol.

The idea is to extend to_proc so it creates a Proc that calls the method on the given object using as arguments the parameters set in to_proc call.

So,

:dig.to_proc(:id)[ary] would be the same as ary.dig(:id).

Currently the Symbol to Proc feature is limited to methods that doesn't receive arguments... But I think that allowing this will open the doors to many possibilities.

The use case for the map was one example only.

Updated by sawa (Tsuyoshi Sawada) 8 months ago

jgomo3 (Jesús Gómez) wrote in #note-3:

The idea is to extend to_proc so it creates a Proc that calls the method on the given object using as arguments the parameters set in to_proc call.

So,

:dig.to_proc(:id)[ary] would be the same as ary.dig(:id).

I already know that. That does not make ary.map(:dig.to_proc(:id)) work. You seemed to have missed my point.

Updated by Hanmac (Hans Mackowiak) 8 months ago

i tried ary.map.with_object(:id,&:dig) but it just returned :id

but this one would work: ary.each_with_object(:id).map(&:dig)

Updated by jgomo3 (Jesús Gómez) 8 months ago

sawa (Tsuyoshi Sawada) wrote in #note-4:

I already know that. That does not make ary.map(:dig.to_proc(:id)) work. You seemed to have missed my point.

Ok, it would be actually ary.map(&:dig.to_proc(:id)).

But that is not the important thing.

The core is just the ability to make "to_proc" useful for symbols of methods that require an argument.

Currently, there is no use of dig.to_proc, but if we allow the parameter, then all those symbols will benefit better from to_proc.

Here is a proof of concept:

class Symbol
  def to_proc(*extra)
    ->(obj) { obj.public_send(self, *extra) }
  end
end

dig_id = :dig.to_proc(:id)

h = {id: 1, v: 10}
dig_id[h] # 1

ary = [{id: 1, v: 10}, {id: 2, v:34}]
ary.map(&dig_id) # [1, 2]

Updated by jgomo3 (Jesús Gómez) 8 months ago

Hanmac (Hans Mackowiak) wrote in #note-5:

i tried ary.map.with_object(:id,&:dig) but it just returned :id

but this one would work: ary.each_with_object(:id).map(&:dig)

Amazing this approach.

Sadly, it will work only for Arity-1 functions.

If you want to dig more, how to do it? e.g, supposing to_proc supports this idea:

ary = [{thing: {id: 1, v:3}, meta: {}}, {thing: {id: 2, v: 4}, meta:{}}]
ary.map(&:dig.to_proc(:thing, :id)) # [1, 2]

Updated by Hanmac (Hans Mackowiak) 8 months ago

jgomo3 (Jesús Gómez) i found my old Symbol#call code


class Symbol
  class SymbolHelper
    def initialize(obj,methId,*args)
      @obj= obj
      @args=args
      @methId=methId
    end
    def method_missing(methId,*args)
      return SymbolHelper.new(self,methId,*args)
    end

    def to_proc
      proc {|obj| (@obj.nil? ? obj : @obj.to_proc.(obj)).public_send(@methId,*@args) }
    end
  end

  def call(*args)
    return SymbolHelper.new(nil,self,*args)
  end
end

you can chain it like that

[1,2,3,4].map(&:to_s.(2)) #=> ["1", "10", "11", "100"]
[1,2,3,4].map(&:to_s.(2).length) #=> [1, 2, 2, 3]

might be updated for keyword arguments

Updated by jgomo3 (Jesús Gómez) 8 months ago

Hanmac (Hans Mackowiak) wrote in #note-8:

jgomo3 (Jesús Gómez) i found my old Symbol#call code

Great helper.

Well, that is another way.

So yes, the whole point of this ticket is to propose to allow that kind of expressions.

In other words, to enable us programmers to prepare, to "partially" call, or to "curry" better the thing that would be a method call based on a symbol for any purpose.

So e.g, using the extension designed by Hanmac (Hans Mackowiak), my example use case would be:

ary.map(&:dig.(:thing, :id)) # [1, 2]

And for getting the partial proc:

dig_thing_and_id = :dig.(:thing, :id).to_proc

And for comparison, the idea as I explained:

ary.map(&:dig.to_proc(:thing, :id)) # [1, 2]
dig_thing_and_id = :dig.to_proc(:thing, :id)

Also available in: Atom PDF