Implicit block parameter?

D

Doug H

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]

1.
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/
 
A

Andrew McGuinness

gabriele said:
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) )
 
D

Dominik Bathon

Hi --

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

(I've not tested that btw but you get the idea). However I guess with = =20
that it's also potentially confusing when using the implicit idea, =20
since it has the same magic variable meaning two different things in =20
the same line. I guess in this case I'd probably declare the argument = =20
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 =20
"it", because it's a global function and not a variable and it always =20
returns the first argument of the current block (if none was given it =20
returns nil).

And for the other point: if it gets to complicated, just don't use "it" =20
and declare the argument(s).

Dominik
 
R

Ross Bamford

Hi --

[[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.

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 Bathon 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,
 
R

Ross Bamford

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 )

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 :)
 
R

Ross Bamford

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 ) )

:D

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]
 
J

Jacob Fugal

class String
def to_proc
lambda { eval self }
end
end

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

gives:

1
2
3

=3D> [1, 2, 3]

Except, of course, for the exception:

NameError: undefined local variable or method `it'

;)

Jacob Fugal
 
R

Ross Bamford

class String
def to_proc
lambda { eval self }
end
end

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

gives:

1
2
3

=> [1, 2, 3]

Except, of course, for the exception:

NameError: undefined local variable or method `it'

;)

Jacob Fugal

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

Matthias Georgi

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.
 

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,780
Messages
2,569,614
Members
45,289
Latest member
stfp04

Latest Threads

Top