From: Magnus Holm Date: 2011-04-25T03:30:58+09:00 Subject: [ruby-core:35868] Re: [Ruby 1.9 - Feature #4102] Proposal for 'let'. A new approach using block-defaults in 1.9 --0016e64ddf3eae2ddf04a1ae4a07 Content-Type: text/plain; charset=UTF-8 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.[](key) 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 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. > > http://pastie.org/1827489 > > 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/4102 > > Author: 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 > > > -- > http://redmine.ruby-lang.org > > --0016e64ddf3eae2ddf04a1ae4a07 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable You can use begin/end-blocks or #tap:

=C2=A0=C2=A0 = =C2=A0# Begin
=C2=A0=C2=A0 =C2=A0def fnames
=C2=A0=C2= =A0 =C2=A0 =C2=A0@fnames ||=3D begin
=C2=A0=C2=A0 =C2=A0 =C2=A0 = =C2=A0hash =3D Hash.new { |h,k| k =3D k.to_s; h[k] =3D generate_fname(k) }<= /div>
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0def hash.[](key)
=C2=A0=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0super(key.to_s)
=C2=A0=C2=A0 =C2=A0 = =C2=A0 =C2=A0end
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0def hash.[]=3D(= key, value)
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0super(key.to_= s, value)
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0end
=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0hash
=C2=A0=C2=A0 =C2=A0 =C2=A0end
=C2=A0=C2=A0 =C2=A0end
=C2=A0=C2=A0 =C2=A0# Tap
=C2=A0=C2=A0 =C2=A0def fnam= es
=C2=A0=C2=A0 =C2=A0 =C2=A0@fnames ||=3D Hash.new { |h,k| k =3D= k.to_s; h[k] =3D generate_fname(k) }.tap do |hash|
=C2=A0=C2=A0 = =C2=A0 =C2=A0 =C2=A0def hash.[](key)
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0super(key.to_s)
=C2= =A0=C2=A0 =C2=A0 =C2=A0 =C2=A0end
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2= =A0def hash.[]=3D(key, value)
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0super(key.to_s, value)
=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0end=
=C2=A0=C2=A0 =C2=A0 =C2=A0end
=C2=A0=C2=A0 =C2=A0end


// Magnus Holm


On Sun, Apr 24, 2011 at 08:07, Tom Wardr= op <tom@tomwardr= op.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 pro= ject I'm working on, including how that same method would look with #le= t.

http://pastie.org/1= 827489

The difference is small (does away with the #call at the end), but it resul= ts 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 seein= g.

I find the main use case for #let is where you want to do something mid-exp= ression. In this example, I want to define a method on a new object as part= of an ||=3D assignment.
----------------------------------------
Feature #4102: Proposal for 'let'. A new approach using block-defau= lts in 1.9
http= ://redmine.ruby-lang.org/issues/4102

Author: john mair
Status: Open
Priority: Normal
Assignee:
Category:
Target version:


=C2=A0This is a very simple function, it would be implemented as follows:
=C2=A0 =C2=A0 module Kernel
=C2=A0 =C2=A0 =C2=A0 private
=C2=A0 =C2=A0 =C2=A0 def let() yield end
=C2=A0 =C2=A0 end

=C2=A0First of all, do not dismiss this functionality out of hand because o= f
=C2=A0its simplicity.

=C2=A0Even though it is just a 'yield', when it is combined with Ru= by 1.9's
=C2=A0block defaults and new block-variable scoping rules it is actually qu= ite
=C2=A0powerful and it behaves exactly like a let* in lisp.

=C2=A0Some advantages of this functionality are:
=C2=A0(1) Gives you precise control over the scope of your variables.

=C2=A0I note that after the publication of "Metaprogramming in Ruby&qu= ot; by Paolo
=C2=A0Perrotta the following idiom has started to appear:

=C2=A0 =C2=A0 proc do
=C2=A0 =C2=A0 =C2=A0 ..my code..
=C2=A0 =C2=A0 end.call

=C2=A0It is used exactly as the proposed 'let' would be used, but i= s
=C2=A0syntactically much uglier.

=C2=A0Yes, i know an alternative is to just make shorter and smaller method= s.
=C2=A0But is the ability to control and restrict scope ever a bad thing?
=C2=A0(2) Testing and teaching about blocks.

=C2=A0As the proposed 'let' simply yields to a block it can be used= to
=C2=A0illustrate block behaviour and block concepts to a new Ruby programme= r.
=C2=A0It also may be useful to an experienced programmer when trying out ne= w
=C2=A0ideas.

=C2=A0Here are some example uses of the proposed 'let':

=C2=A0Example 1: Carve out a temporary scope, make 'x' local to tha= t scope

=C2=A0 =C2=A0 x =3D :outer
=C2=A0 =C2=A0 let { |x| x =3D :inner } #=3D> :inner
=C2=A0 =C2=A0 x #=3D> :outer

=C2=A0Example 2: Here we use Ruby 1.9's block-defaults to make 'y&#= 39; block-local
=C2=A0and give it a value:

=C2=A0 =C2=A0 let { |y=3D10| y } #=3D> 10

=C2=A0Example 3: Make 'x' and 'y' block-local and have '= ;y' value depend on 'x'
=C2=A0(equivalent to let* in lisp)

=C2=A0 =C2=A0 let { |x=3D10, y=3D(2*x)| [x, y] } #=3D> [10, 20]

=C2=A0In summary, I think this proposal should succeed for the following =C2=A0reasons:
=C2=A0(1) It is an exceptionally simple implementation.
=C2=A0(2) More control over scope is never a bad thing.
=C2=A0(3) I have seen people re-implementing this functionality themselves<= br> =C2=A0using: proc { ..code.. }.call
=C2=A0(4) It is very useful for teaching and testing block behaviour.

=C2=A0Thanks,

=C2=A0John


--
http://redmine.r= uby-lang.org


--0016e64ddf3eae2ddf04a1ae4a07--