I know about Object#tap
, which takes a value and returns that value. But is there a method that takes a block and returns the value evaluated by the block?
To improve my code in this answer (which is more complicated than the snippet below), I'd like to change
deck.index("A").tap {|index|
STDERR.puts "Result of indexing for #{"A".inspect} is #{index.inspect}"
}
, which has "A"
repeated, into
def my_method(*args)
yield *args
end
deck = ['A', 'B', 'C']
my_method("A") {|value| deck.index(value).tap {|index|
STDERR.puts "Result of indexing for #{value.inspect} is #{index.inspect}"
} }
# Result of indexing for "A" is 0
# => 0
What you're looking for is essentially the equivalent of let
in Lisp or OCaml — something that allows you to temporarily bind a value to an identifier without introducing a new variable into the larger scope. There isn't anything that lets you do such a thing with that syntax in Ruby. The equivalent Ruby would be:
lambda {|value| deck.index(value).tap {|index|
STDERR.puts "Result of indexing for #{value.inspect} is #{index.inspect}"
} }.call 'A'
You could of course just write a method like:
def let(*values)
yield *values
end
I think you could solve it with fibers. Something like:
def myfiber
block = lambda{nil}
loop{ block = Fiber.yield(block.call) }
end
f = Fiber.new {myfiber }
f.resume
puts "result: #{f.resume(lambda{1})}"
puts "result: #{f.resume(lambda{5})}"
puts "result: #{f.resume(lambda{2})}"
will result in:
result: 1
result: 5
result: 2