Implicit block parameter?

Hi –

On Wed, 4 Jan 2006, Ross B. wrote:

[[1,2,3],[2,3,4],[3,4,5]].each { it.select { it % 2 == 0 } }

(I’ve not tested that btw but you get the idea). However I guess with that
it’s also potentially confusing when using the implicit idea, since it has
the same magic variable meaning two different things in the same line. I
guess in this case I’d probably declare the argument in the select block
anyway (as ‘i’ or something).

That’s actually another good argument against “it”: you’d be trampling
the outer it with the inner it. And having to remember to declare the
inner argument to protect it from this really puts the whole thing in
the “added to the language as an afterthought” category.

David


David A. Black
[email protected]

“Ruby for Rails”, from Manning Publications, coming April 2006!

For what it’s worth, some of us proposed the same thing for the boo
language [1]. Also groovy has this feature [2]. (might be worth
putting in proposal that two other ruby & python inspired languages use
or want to use this, as well as AliceML)
Pnuts also has a related feature [3]:

print myitems[it % 2 == 0]

Essentially you can pass a boolean expression or a closure to an
indexer. Above would return all even items. I’m not proposing it for
ruby, just mentioning. “it” is very powerful.
Comega (or c-omega from microsoft) also has this feature [4]

http://groups.google.com/group/boolang/browse_frm/thread/52d619066407bfe/082fc0286faec24c
2. http://groovy.codehaus.org/Closures
3. http://pnuts.org/snapshot/latest/doc/lang.html
4. http://research.microsoft.com/Comega/

gabriele renzi wrote:

True, but considering how often people write
each {|x| stuff(x)}
I don’t think it would be a great less in readability.
Not really advocating “it”, but neither against it. No pun intended.

Another way of dealing with it would be if it were easier to get from a
method to a Proc object, so instead of, say

heads = queues.map { |x| x.first }

You could have something more like:

heads = queues.map( &:first )

As with the “it” thing, you can do it for a specific method -

module Enumerable
def map_method( method, *args )
map { |x| x.send( method, *args ) }
end
end

heads = queues.map_method( :first )

For things.each { |x| stuff(x) } it’s possible in principle:

things.each( &method(:stuff).to_proc )

But that’s not exactly a simplification - I’m hoping for something more
like lisp, where:

(setq heads (mapcar (lambda (x) (first x)) queues))

can be replaced exactly by:

(setq heads (mapcar #'first queues))

I don’t know if there’s a good way of doing what I want - Ruby is
fundamentally unlike lisp in that objects are more fundamental than
functions. I think it would be possible to allow Symbol and Method as
alternatives to Proc in a “block” parameter, but I’m not sure it would
be a good idea.

heads = queues.map( &:first )

things.each( &method(:stuff) )

On Tue, 03 Jan 2006 19:39:48 +0100, [email protected] wrote:

in the select block anyway (as ‘i’ or something).

That’s actually another good argument against “it”: you’d be trampling
the outer it with the inner it. And having to remember to declare the
inner argument to protect it from this really puts the whole thing in
the “added to the language as an afterthought” category.

With the patch I sent the outer “it” is not “overwritten” by the inner
“it”, because it’s a global function and not a variable and it always
returns the first argument of the current block (if none was given it
returns nil).

And for the other point: if it gets to complicated, just don’t use “it”
and declare the argument(s).

Dominik

On 1/3/06, Andrew McGuinness [email protected] wrote:

Another way of dealing with it would be if it were easier to get from a
method to a Proc object, so instead of, say

heads = queues.map { |x| x.first }

You could have something more like:

heads = queues.map( &:first )

Take a look at this:

http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Tech/Ruby/ToProc.rdoc

Jacob F.

On Tue, 03 Jan 2006 20:55:02 -0000, Andrew McGuinness
[email protected] wrote:

heads = queues.map { |x| x.first }

You could have something more like:

heads = queues.map( &:first )

Hmm, check out http://extensions.rubyforge.org/rdoc/classes/Symbol.html
.
I’d not thought about it until now but it’s actually a pretty good way
to
cut that verbosity in the simple cases…:

require 'extensions/symbol'
['one','two','three'].map &:upcase		# => ['ONE','TWO','THREE']

Nice one :slight_smile:

Jacob F. wrote:

Take a look at this:

http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Tech/Ruby/ToProc.rdoc

That’s good - if I’m mad, at least I’m not alone.

And I didn’t realise that thing.each( &method(:stuff) ) works already
(the same as thing.each( &method(:stuff).to_proc ) )

On Tue, 03 Jan 2006 18:39:48 -0000, [email protected] wrote:

in the select block anyway (as ‘i’ or something).

That’s actually another good argument against “it”: you’d be trampling
the outer it with the inner it. And having to remember to declare the
inner argument to protect it from this really puts the whole thing in
the “added to the language as an afterthought” category.

Hmm, I know. As I was writing it I started thinking about that and all
the
other little subtleties it could create. Generally I’m never happy with
a
solution that requires me to do more thinking :D. Based on the C-side
patch that was posted by Dominik B. the inner/outer it thing does
actually work, but isn’t ideal from a readability perspective I guess.

(As an aside, with that patch the approach I suggested doesn’t work,
since it doesn’t check whether ‘it’ is already declared so tramples the
outer it even if you declare a block argument on the inner).

I’m not sure I understand the afterthought thing, though - wouldn’t any
suggested enhancement that happened to be implemented now be an
afterthought? But anyhow this has snowballed a bit - originally I really
wanted a way to do it from the Ruby side, and more for fun than profit.
I’ve not been here long enough to start suggesting changes in Ruby
itself
but I just wondered if it’d been considered before…

Cheers,

On 1/3/06, Ross B. [email protected] wrote:

    1
    2
    3

    => [1, 2, 3]

Except, of course, for the exception:

NameError: undefined local variable or method `it’

:wink:

Jacob F.

On Tue, 03 Jan 2006 22:01:02 -0000, Andrew McGuinness
[email protected] wrote:

heads = queues.map( &:first )
Take a look at this:
http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Tech/Ruby/ToProc.rdoc

That’s good - if I’m mad, at least I’m not alone.

And I didn’t realise that thing.each( &method(:stuff) ) works already
(the same as thing.each( &method(:stuff).to_proc ) )

:smiley:

Yeah, AUIU ‘&’ works a bit like splat but for procs (with to_proc). For
a
silly example:

class String
  def to_proc
    lambda { eval self }
  end
end

[1,2,3].each &"puts it"

gives:

1
2
3

=> [1, 2, 3]

On Tue, 03 Jan 2006 23:20:01 -0000, Jacob F. [email protected]
wrote:

:wink:

Jacob F.

Oops, yes I’m working with the Ruby I patched this morning… Sorry for
the confusion.

It is also possible to instance_eval a passed block, like:

module Enumerable
def map_i(&block)
map { |i| i.instance_eval(&block) }
end
end

heads = queues.map_i { first }

Now the block is evaled in the scope of each item, which is a little
bit confusing, as you can’t call methods of the surrounding scope.

I don’t like the implicit ‘it’, because there are already enough things
to remember in ruby’s syntax.

On 03/01/06, Doug H [email protected] wrote:

For what it’s worth, some of us proposed the same thing for the boo
language [1]. Also groovy has this feature [2]. (might be worth
putting in proposal that two other ruby & python inspired languages use
or want to use this, as well as AliceML)
Pnuts also has a related feature [3]:

None of which I think is actually a reason for Ruby to get it.

I am in the “opposed” camp on this, because I think that it will
result in less-readable code overall.

-austin