Feature #19432
closedIntroduce a wrapping operator (&) to Proc
Description
I don't know if this concept exists under another name, or whether there’s a technical term for it. I often find myself wanting to wrap a proc in another proc.
Here's a snippet from a recent example where a visitor class renders a TipTap AST. Given a text
node, we want to output the text by calling the text
method with the value. But if the text has marks
, we want to iterate through each mark, wrapping the output for each mark. It's possible to do this using the >>=
operator if each proc explicitly returns another proc.
when "text"
result = -> { -> { text node["text"] } }
node["marks"]&.each do |mark|
case mark["type"]
when "bold"
result >>= -> (r) { -> { strong { r.call } } }
when "italic"
result >>= -> (r) { -> { em { r.call } } }
end
end
result.call.call
end
This is quite difficult to follow and the result.call.call
feels wrong. I think the concept of wrapping one proc in another proc would make for a great addition to Proc
itself. I prototyped this using the &
operator.
class Proc
def &(other)
-> { other.call(self) }
end
end
With this definition, we can call &
on the original proc with our other proc to return a new proc that calls the other proc with the original proc as an argument. It also works with &=
, so the above code can be refactored to this:
when "text"
result = -> { text node["text"] }
node["marks"]&.each do |mark|
case mark["type"]
when "bold"
result &= -> (r) { strong { r.call } }
when "italic"
result &= -> (r) { em { r.call } }
end
end
result.call
end