whats this lambda code doing?

H

hemant

I came across following code in typo's application.rb and I can't
understand the last part,


def with_blog_scoped_classes(klasses=[Content, Article, Comment,
Page, Trackback], &block)
default_id = this_blog.id
scope_hash = { :find => { :conditions => "blog_id = #{default_id}"},
:create => { :blog_id => default_id } }
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call
end

Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?
 
E

Ezra Zygmuntowicz

I came across following code in typo's application.rb and I can't
understand the last part,


def with_blog_scoped_classes(klasses=[Content, Article, Comment,
Page, Trackback], &block)
default_id = this_blog.id
scope_hash = { :find => { :conditions => "blog_id = #
{default_id}"},
:create => { :blog_id => default_id } }
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call
end

Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?


klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call

That outer block that ends on end returns the lambda made inside of
that block. So end.call ends up being lambda { klass.with_scope
(scope_hash, &blk) }.call




-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- (e-mail address removed)
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)
 
D

David Vallner

--------------enig783383C0715435A317AC8FB3
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
I came across following code in typo's application.rb and I can't
understand the last part,
=20
=20
def with_blog_scoped_classes(klasses=3D[Content, Article, Comment,
Page, Trackback], &block)
default_id =3D this_blog.id
scope_hash =3D { :find =3D> { :conditions =3D> "blog_id =3D #{defaul= t_id}"},
:create =3D> { :blog_id =3D> default_id } }
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call
end
=20
Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?
=20
=20

The call to klasses.inject returns a block (a Proc). The .call evaluates
the Proc.

The code is a bit too clever for its own good. Personally I could go
around and slap everyone that chains a method after a code block /
closure block, or tags a statement modifier conditional / loop after it,
most of the time it's just golfing to hide an unsightly level of control
flow nesting.

David Vallner


--------------enig783383C0715435A317AC8FB3
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFNs0wy6MhrS8astoRAoiFAJ4wrd3lgcPAxMzWaMhN2PDmFOIQKACffzTK
xx68vIKlZFUFivXxaqFeK4c=
=MIOp
-----END PGP SIGNATURE-----

--------------enig783383C0715435A317AC8FB3--
 
K

Ken Bloom

I came across following code in typo's application.rb and I can't
understand the last part,


def with_blog_scoped_classes(klasses=[Content, Article, Comment,
Page, Trackback], &block)
default_id = this_blog.id
scope_hash = { :find => { :conditions => "blog_id = #{default_id}"},
:create => { :blog_id => default_id } }
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call
end

Method will be called with a block and will probably add some scope to
the respective methods of ActiveRecord classes. But whats the last
part?
I mean, end.call?

The whole do...end thing (and its binding to the inject call) has higher
precedence than the method call that follows it. It's equivalent to

x=klasses.inject(block) do
...
end
x.call
 
D

David Vallner

--------------enigAB7DF8666B9EAF2F862CA995
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Ezra said:
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call
=20
That outer block that ends on end returns the lambda made inside of tha= t
block. So end.call ends up being lambda { klass.with_scope(scope_hash,=
&blk) }.call
=20

Hmm. Unless I'm very mistaken, it would also only call the lambda for
the last object in klasses, generating several garbage (and relatively
expensive) lambdas. Boggle. Am I missing something?

David Vallner


--------------enigAB7DF8666B9EAF2F862CA995
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFNs6Sy6MhrS8astoRApXlAJ0SdVtCZKSa01ConRk/rDekzmktLwCeOFnJ
DwIa0kDSm7HK8l5lxI64jNo=
=ik4D
-----END PGP SIGNATURE-----

--------------enigAB7DF8666B9EAF2F862CA995--
 
E

Ezra Zygmuntowicz

Hmm. Unless I'm very mistaken, it would also only call the lambda for
the last object in klasses, generating several garbage (and relatively
expensive) lambdas. Boggle. Am I missing something?

David Vallner


It looks like it would only call the last lambda but it does call
all of them. Here is a simplification of whats happening.

irb(main):065:0> def scoper(klasses=[:foo, :bar, :baz], &block)
irb(main):066:1> klasses.inject(block) do |blk, klass|
irb(main):067:2* lambda { puts klass; blk.call}
irb(main):068:2> end.call
irb(main):069:1> end
=> nil
irb(main):070:0* scoper { puts '&block called' }
baz
bar
foo
&block called
=> nil
irb(main):071:0>



-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- (e-mail address removed)
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)
 
E

Ezra Zygmuntowicz

This some wierd kind of lambda chaining. You're confusing "block" with
"blk", so you're thinking that each version of this code is using the
original block, but this is not so. Each lambda is chained to call the
previous lambda. I haven't got a clue what with_scope does, but
this would
all be a lot easier to understand if it was possible to eliminate the
lambda, and deal with each class separately.


with_scope is an ActiveRecord thing for scoping your where clauses.
It works like this:

Post.with_scope:)find => {:conditions => ["customer_id = ?",
@customer.id]}) do
@post = Post.find:)all)
end

And you can nest these with_scope blocks inside each other so the
innermost #find method gets the scoped :conditions merged with its
own conditions. So the original posters code is taking a list of
Model classes and scoping the find and create calls on them to a
specific blog.id .

But I have to agree that code is too clever for its own good.
Twisting it up like that may make a golfer happy, the next guy to
come across it will be confused when it could have been stated clearer.

Cheers.



-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- (e-mail address removed)
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)
 
M

Morton Goldberg

Hmm. Unless I'm very mistaken, it would also only call the lambda for
the last object in klasses, generating several garbage (and
relatively
expensive) lambdas. Boggle. Am I missing something?

David Vallner

It looks like it would only call the last lambda but it does call
all of them. Here is a simplification of whats happening.

irb(main):065:0> def scoper(klasses=[:foo, :bar, :baz], &block)
irb(main):066:1> klasses.inject(block) do |blk, klass|
irb(main):067:2* lambda { puts klass; blk.call}
irb(main):068:2> end.call
irb(main):069:1> end
=> nil
irb(main):070:0* scoper { puts '&block called' }
baz
bar
foo
&block called
=> nil
irb(main):071:0>

I think I'm still confused by this. I infer from your post that the
lambdas get nested by the inject (at the position of 'blk') with the
first one innermost, which is why they get called in reverse order
(and 'block' last of all). Is that right?

Regards, Morton
 
E

Ezra Zygmuntowicz

Ezra Zygmuntowicz wrote:
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call

That outer block that ends on end returns the lambda made inside
of that
block. So end.call ends up being lambda { klass.with_scope
(scope_hash,
&blk) }.call


Hmm. Unless I'm very mistaken, it would also only call the lambda
for
the last object in klasses, generating several garbage (and
relatively
expensive) lambdas. Boggle. Am I missing something?

David Vallner

It looks like it would only call the last lambda but it does call
all of them. Here is a simplification of whats happening.

irb(main):065:0> def scoper(klasses=[:foo, :bar, :baz], &block)
irb(main):066:1> klasses.inject(block) do |blk, klass|
irb(main):067:2* lambda { puts klass; blk.call}
irb(main):068:2> end.call
irb(main):069:1> end
=> nil
irb(main):070:0* scoper { puts '&block called' }
baz
bar
foo
&block called
=> nil
irb(main):071:0>

I think I'm still confused by this. I infer from your post that the
lambdas get nested by the inject (at the position of 'blk') with
the first one innermost, which is why they get called in reverse
order (and 'block' last of all). Is that right?

Regards, Morton

Yes thats essentially it. Maybe this helps clear it up a bit. Lets
forget about the with_scope AR stuff for a minute and just mock this
out:

class Scoper
def self.with_block(hsh={}, &block)
p hsh.merge( {:klass => name})
block.call
end
end

class Foo < Scoper
end

class Bar < Scoper
end

class Baz < Scoper
end

def set_scopers(klasses=[Foo, Bar, Baz], &block)
hash = {:test => 'test'}
klasses.inject(block) do |blk, klass|
lambda { klass.with_block hash, &blk }
end.call
end

puts set_scopers { puts 'done'}

# outputs
{:test=>"test", :klass=>"Baz"}
{:test=>"test", :klass=>"Bar"}
{:test=>"test", :klass=>"Foo"}
done



Now if you comment out the block.call line in the Scoper.with_block
method:

class Scoper
def self.with_block(hsh={}, &block)
p hsh.merge( {:klass => name})
#block.call
end
end

<snip>

puts set_scopers { puts 'done' }
#ouput
{:test=>"test", :klass=>"Baz"}
nil


and run the same code again it only calls the last lambda that gets
made. It is a twisty little mess of block and wrappers that is hard
to follow. But essentially inject is chaning those lambda's together
and the final .call calls the chain which calls the final &blk in the
end.
nil

Cheers-

-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- (e-mail address removed)
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)
 
R

Rick DeNatale

But I have to agree that code is too clever for its own good.
Twisting it up like that may make a golfer happy, the next guy to
come across it will be confused when it could have been stated clearer.

You know, I can understand, or maybe sympathize, with the appeal of
real golf, although I've yet to break 110, but I can't for the life of
me understand why anyone things code golfing is good for anyone.

But then again "Succintness is Power!"
 
H

hemant

Ezra Zygmuntowicz wrote:
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call

That outer block that ends on end returns the lambda made inside
of that
block. So end.call ends up being lambda { klass.with_scope
(scope_hash,
&blk) }.call


Hmm. Unless I'm very mistaken, it would also only call the lambda for
the last object in klasses, generating several garbage (and
relatively
expensive) lambdas. Boggle. Am I missing something?

David Vallner

It looks like it would only call the last lambda but it does call
all of them. Here is a simplification of whats happening.

irb(main):065:0> def scoper(klasses=[:foo, :bar, :baz], &block)
irb(main):066:1> klasses.inject(block) do |blk, klass|
irb(main):067:2* lambda { puts klass; blk.call}
irb(main):068:2> end.call
irb(main):069:1> end
=> nil
irb(main):070:0* scoper { puts '&block called' }
baz
bar
foo
&block called
=> nil
irb(main):071:0>

I think I'm still confused by this. I infer from your post that the
lambdas get nested by the inject (at the position of 'blk') with the
first one innermost, which is why they get called in reverse order
(and 'block' last of all). Is that right?

Regards, Morton

Hmm...i think i get the idea, but i do not understand how its .call is
working on all the lambdas?
 
H

hemant

On Oct 18, 2006, at 6:02 PM, David Vallner wrote:

Ezra Zygmuntowicz wrote:
klasses.inject(block) do |blk, klass|
lambda { klass.with_scope(scope_hash, &blk) }
end.call

That outer block that ends on end returns the lambda made inside
of that
block. So end.call ends up being lambda { klass.with_scope
(scope_hash,
&blk) }.call


Hmm. Unless I'm very mistaken, it would also only call the lambda for
the last object in klasses, generating several garbage (and
relatively
expensive) lambdas. Boggle. Am I missing something?

David Vallner

It looks like it would only call the last lambda but it does call
all of them. Here is a simplification of whats happening.

irb(main):065:0> def scoper(klasses=[:foo, :bar, :baz], &block)
irb(main):066:1> klasses.inject(block) do |blk, klass|
irb(main):067:2* lambda { puts klass; blk.call}
irb(main):068:2> end.call
irb(main):069:1> end
=> nil
irb(main):070:0* scoper { puts '&block called' }
baz
bar
foo
&block called
=> nil
irb(main):071:0>

I think I'm still confused by this. I infer from your post that the
lambdas get nested by the inject (at the position of 'blk') with the
first one innermost, which is why they get called in reverse order
(and 'block' last of all). Is that right?

Regards, Morton

Ok..i get it now..Thanks for the nice explanation Ezra.
 
E

Ezra Zygmuntowicz

Yes thats essentially it. Maybe this helps clear it up a bit. Lets
forget about the with_scope AR stuff for a minute and just mock
this out:

class Scoper
def self.with_block(hsh={}, &block)
p hsh.merge( {:klass => name})
block.call
end
end

class Foo < Scoper
end

class Bar < Scoper
end

class Baz < Scoper
end

def set_scopers(klasses=[Foo, Bar, Baz], &block)
hash = {:test => 'test'}
klasses.inject(block) do |blk, klass|
lambda { klass.with_block hash, &blk }
end.call
end

puts set_scopers { puts 'done'}

# outputs
{:test=>"test", :klass=>"Baz"}
{:test=>"test", :klass=>"Bar"}
{:test=>"test", :klass=>"Foo"}
done



Now if you comment out the block.call line in the Scoper.with_block
method:

class Scoper
def self.with_block(hsh={}, &block)
p hsh.merge( {:klass => name})
#block.call
end
end

<snip>

puts set_scopers { puts 'done' }
#ouput
{:test=>"test", :klass=>"Baz"}
nil


and run the same code again it only calls the last lambda that gets
made. It is a twisty little mess of block and wrappers that is hard
to follow. But essentially inject is chaning those lambda's
together and the final .call calls the chain which calls the final
&blk in the end.
nil


What it is doing with the inject is essentially building up a block
with multiple procs inside it and then calling that. So the block
that gets called looks something like this by the time it gets call'ed:

lambda {
lambda { Foo.with_block hash, &blk }
lambda { Bar.with_block hash, &blk }
lambda { Baz.with_block hash, &blk }
}.call


Make sense?

-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- (e-mail address removed)
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)
 
M

Morton Goldberg

What it is doing with the inject is essentially building up a
block with multiple procs inside it and then calling that. So the
block that gets called looks something like this by the time it
gets call'ed:

lambda {
lambda { Foo.with_block hash, &blk }
lambda { Bar.with_block hash, &blk }
lambda { Baz.with_block hash, &blk }
}.call

Maybe I'm being even more dense than usual, but I can't accept that.
It wouldn't produce the observed print-out order. I think it must be
more like:

lambda { Baz.with_block(hash,
lambda { Bar.with_block(hash,
lambda { Foo.with_block(hash, &blk) }) }) }.call

Regards, Morton
 
E

Ezra Zygmuntowicz

Maybe I'm being even more dense than usual, but I can't accept
that. It wouldn't produce the observed print-out order. I think it
must be more like:

lambda { Baz.with_block(hash,
lambda { Bar.with_block(hash,
lambda { Foo.with_block(hash, &blk) }) }) }.call

Regards, Morton

Ahh yes you are totally correct. See? It is tricky code ;)


-- Ezra Zygmuntowicz
-- Lead Rails Evangelist
-- (e-mail address removed)
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)
 
L

Louis J Scoras

It's really not that bad. Learn to love the lambda, I say =)

unwind = lambda {|x| puts x}
(1..9).inject(unwind) do |stack, i|
lambda {|x| puts x; stack}
end[10]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top