Feature #16667
openAllow parameters to Symbol#to_proc and Method#to_proc
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) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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 asary.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) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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) over 4 years 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)