Feature #4102
closedProposal for 'let'. A new approach using block-defaults in 1.9
Description
=begin
This is a very simple function, it would be implemented as follows:
module Kernel
private
def let() yield end
end
First of all, do not dismiss this functionality out of hand because of
its simplicity.
Even though it is just a 'yield', when it is combined with Ruby 1.9's
block defaults and new block-variable scoping rules it is actually quite
powerful and it behaves exactly like a let* in lisp.
Some advantages of this functionality are:
(1) Gives you precise control over the scope of your variables.
I note that after the publication of "Metaprogramming in Ruby" by Paolo
Perrotta the following idiom has started to appear:
proc do
..my code..
end.call
It is used exactly as the proposed 'let' would be used, but is
syntactically much uglier.
Yes, i know an alternative is to just make shorter and smaller methods.
But is the ability to control and restrict scope ever a bad thing?
(2) Testing and teaching about blocks.
As the proposed 'let' simply yields to a block it can be used to
illustrate block behaviour and block concepts to a new Ruby programmer.
It also may be useful to an experienced programmer when trying out new
ideas.
Here are some example uses of the proposed 'let':
Example 1: Carve out a temporary scope, make 'x' local to that scope
x = :outer
let { |x| x = :inner } #=> :inner
x #=> :outer
Example 2: Here we use Ruby 1.9's block-defaults to make 'y' block-local
and give it a value:
let { |y=10| y } #=> 10
Example 3: Make 'x' and 'y' block-local and have 'y' value depend on 'x'
(equivalent to let* in lisp)
let { |x=10, y=(2*x)| [x, y] } #=> [10, 20]
In summary, I think this proposal should succeed for the following
reasons:
(1) It is an exceptionally simple implementation.
(2) More control over scope is never a bad thing.
(3) I have seen people re-implementing this functionality themselves
using: proc { ..code.. }.call
(4) It is very useful for teaching and testing block behaviour.
Thanks,
John
=end
Updated by now (Nikolai Weibull) about 14 years ago
=begin
On Tue, Nov 30, 2010 at 04:47, john mair redmine@ruby-lang.org wrote:
I note that after the publication of "Metaprogramming in Ruby" by Paolo
Perrotta the following idiom has started to appear:proc do
..my code..
end.call
Seriously? Where?
Yes, i know an alternative is to just make shorter and smaller methods.
And it’s a wonderful solution at that.
=end
Updated by zenspider (Ryan Davis) about 14 years ago
=begin
On Nov 29, 2010, at 19:47 , john mair wrote:
This is a very simple function, it would be implemented as follows:
module Kernel
private
def let() yield end
endFirst of all, do not dismiss this functionality out of hand because of
its simplicity.
Personally, I like it... I think it is a very illustrative tool with a lot of utility (mostly for teaching, but that's still very useful).
But I think you should release it as a gem first and let it make the rounds.
And hey... if you write it, rails will find a way to use it! DBT :D
That's how the tap method came to be in core...
=end
Updated by Eregon (Benoit Daloze) about 14 years ago
=begin
On 30 November 2010 04:47, john mair redmine@ruby-lang.org wrote:
Here are some example uses of the proposed 'let':
None of this example does actually "speak" to me.
Could you maybe elaborate the usefulness and show a "real" example
(i.e. how you would use it) ?
=end
Updated by wardrop (Tom Wardrop) over 13 years ago
=begin
Here's an example I just encountered where #let (a self executing proc) would have been useful. Here's a method I've just defined in a project I'm working on, including how that same method would look with #let.
The difference is small (does away with the #call at the end), but it results in cleaner and clearer code. It can be easy to miss the #call method at the end of the proc, as it's just not something you're use to seeing.
I find the main use case for #let is where you want to do something mid-expression. In this example, I want to define a method on a new object as part of an ||= assignment.
=end
Updated by judofyr (Magnus Holm) over 13 years ago
=begin
You can use begin/end-blocks or #tap:
# Begin
def fnames
@fnames ||= begin
hash = Hash.new { |h,k| k = k.to_s; h[k] = generate_fname(k) }
def hash.[](key)
super(key.to_s)
end
def hash.[]=(key, value)
super(key.to_s, value)
end
hash
end
end
# Tap
def fnames
@fnames ||= Hash.new { |h,k| k = k.to_s; h[k] = generate_fname(k)
}.tap do |hash|
def hash.
super(key.to_s)
end
def hash.[]=(key, value)
super(key.to_s, value)
end
end
end
// Magnus Holm
On Sun, Apr 24, 2011 at 08:07, Tom Wardrop tom@tomwardrop.com wrote:
Issue #4102 has been updated by Tom Wardrop.
Here's an example I just encountered where #let (a self executing proc)
would have been useful. Here's a method I've just defined in a project I'm
working on, including how that same method would look with #let.The difference is small (does away with the #call at the end), but it
results in cleaner and clearer code. It can be easy to miss the #call method
at the end of the proc, as it's just not something you're use to seeing.I find the main use case for #let is where you want to do something
mid-expression. In this example, I want to define a method on a new object
as part of an ||= assignment.Feature #4102: Proposal for 'let'. A new approach using block-defaults in
1.9
http://redmine.ruby-lang.org/issues/4102Author: john mair
Status: Open
Priority: Normal
Assignee:
Category:
Target version:This is a very simple function, it would be implemented as follows:
module Kernel private def let() yield end end
First of all, do not dismiss this functionality out of hand because of
its simplicity.Even though it is just a 'yield', when it is combined with Ruby 1.9's
block defaults and new block-variable scoping rules it is actually quite
powerful and it behaves exactly like a let* in lisp.Some advantages of this functionality are:
(1) Gives you precise control over the scope of your variables.I note that after the publication of "Metaprogramming in Ruby" by Paolo
Perrotta the following idiom has started to appear:proc do ..my code.. end.call
It is used exactly as the proposed 'let' would be used, but is
syntactically much uglier.Yes, i know an alternative is to just make shorter and smaller methods.
But is the ability to control and restrict scope ever a bad thing?(2) Testing and teaching about blocks.
As the proposed 'let' simply yields to a block it can be used to
illustrate block behaviour and block concepts to a new Ruby programmer.
It also may be useful to an experienced programmer when trying out new
ideas.Here are some example uses of the proposed 'let':
Example 1: Carve out a temporary scope, make 'x' local to that scope
x = :outer let { |x| x = :inner } #=> :inner x #=> :outer
Example 2: Here we use Ruby 1.9's block-defaults to make 'y' block-local
and give it a value:let { |y=10| y } #=> 10
Example 3: Make 'x' and 'y' block-local and have 'y' value depend on 'x'
(equivalent to let* in lisp)let { |x=10, y=(2*x)| [x, y] } #=> [10, 20]
In summary, I think this proposal should succeed for the following
reasons:
(1) It is an exceptionally simple implementation.
(2) More control over scope is never a bad thing.
(3) I have seen people re-implementing this functionality themselves
using: proc { ..code.. }.call
(4) It is very useful for teaching and testing block behaviour.Thanks,
John
=end
Updated by wardrop (Tom Wardrop) over 13 years ago
Thanks for the tips Magnus, they're very handy. I forget that begin ... end can be used just about anywhere to encapsulate multiple expressions that lead to a single result. The #tap method I simply had no idea about.
Cheers
Updated by nahi (Hiroshi Nakamura) almost 13 years ago
- Description updated (diff)
- Category set to core
- Assignee set to matz (Yukihiro Matsumoto)
Updated by shyouhei (Shyouhei Urabe) almost 13 years ago
- Status changed from Open to Assigned
Updated by trans (Thomas Sawyer) almost 13 years ago
It would conflict with RSpec's definition of #let.
Other then as a teaching tool for block scope what are the use cases? This seems to have very limited use. I find defining a new method is better way to get clean scope.
Updated by matz (Yukihiro Matsumoto) almost 13 years ago
- Status changed from Assigned to Rejected
The definition of let would be: "def let() yield; end"; I don't think it's worth to add.
Matz.