-
Zack Hobson
OpenSourcery Alumnus
A common construct in functional programming languages is the let macro, used to define lexically-scoped names. The let macro exists in most (all?) Lisp dialects and spiritual descendants, including Clojure. Since it's possible to define lexically-scoped variables at any time in Ruby, there isn't much perceived need for a construct like let. However, Ruby's block syntax makes it particularly easy to experiment with functional programming techniques, and in fact there is already a method in Ruby's standard library that is maddeningly similar to let, the tap method.
The reason I assert that tap is "maddeningly similar" to let is that tap does not return the result of the block, rather, it always returns the receiver. This makes it possible to take a value or values and create lexically-scoped names referring to them in a block, but there is no way to access the results of that block unless you explicitly stash them in the receiver or (possibly) a global. Even still, tap has its uses, which is why it's been in the standard library since 1.9.
# output: up is down "down".tap { |up| puts "up is #{up}" }
A Ruby implementation of let would be similar to tap in that it would allow you to create a lexical block, but it would differ in that it evaluates to the block result:
# output: up is down puts "down".let { |up| "up is #{up}" }
In cases where you don't care about the result of the block you can use either one, but if you care about the block result instead of the receiver then you can use let instead of tap:
# output: UP IS DOWN puts "down".let { |up| "up is #{up}" }.upcase
I find myself wanting to use tap or let in cases when I need to use a calculated value multiple times. I could just create a temporary variable, of course:
c = some_complex_expression do_something if c other_method(c)
Instead I can use tap (or let if it's available) to achieve the same effect:
(some_complex_expression).tap do |c| do_something if c other_method(c) end
This is quite useful in ERB templates, where creating temporary variables feels especially wrong.
You can also exploit a feature of Ruby to get multiple lexical names per block. If the receiver is an Array and you provide multiple names in the block arguments, the contents will automatically be broken out. This works with either tap or let:
(1..3).to_a.tap do |one, two, three| puts "one is #{one}" puts "two is #{two}" puts "three is #{three}" end puts (1..3).to_a.let { |one, two, three| "one is #{one}", "two is #{two}", "three is #{three}" }.join("\n")
The implementation of both methods is trivial. It's not like any developer who wants to use let needs to wait for some Ruby core developer to code it up for him. Here's the complete implementation of Object#let:
class Object def let yield self end end
The tap method (which is already in the standard library) is about twice as large, weighing in at two trivial lines:
class Object def tap yield self self end end
To other developers that stumble across this, I pose this question: what techniques and/or idioms do you favor when a scoped temporary variable is called for?
Tagged as: Ruby
Very Informative Blog
Hi,
It is great to come to your about, i have been learning to build website by using Drupal, your blog posts are very informative for to keep on reading.
Thanks
Thanks
Thanks for the great info here!
==========================
Add your comment