Implicit block parameter?

R

Ross Bamford

Hi,

Probably this has been considered before, but I'll ask anyway.

Before I used Ruby, I used Groovy. In some ways they are (well, were
anyway) quite similar, and one feature that I found cool in Groovy that I
often miss in Ruby is the implicit (/default/whatever?) block parameter,
'it':

[1,2,3].each { puts it }

AFAIR this was only provided when no 'it' existed in scope, and the block
had a single argument passed when none were declared. Maybe this would
interfere with Ruby's warnings about block parameter mismatch, or maybe
the implementation doesn't allow for it, but I just wondered if it might
be possible, because I notice I do:

[1,2,3].each { |it| puts it }

and it bugs me a little bit :D

I did try hacking it from the Ruby side and came up with a halfway
solution using instance variables and the Binding extension from Rubyforge:

require 'rubygems'
require 'extensions/binding'

def itproc(&blk)
class << blk
def [](*args)
if args.length == 1
begin
old = binding[:mad:it]
binding[:mad:it] = args[0]
super
ensure
binding[:mad:it] = old
end
else
super
end
end

alias :call :[]
end
blk
end

But of course this doesn't work with regular procs (doing it on Proc
causes a segfault here, I guess because extensions uses procs itself to do
the binding stuff?) and of course it doesn't happen with yielded blocks,
even when passed from procs:

pr = itproc { puts "@it = #{@it.inspect}" }
pr2 = itproc { |one| puts "@it = #{@it.inspect}; one = #{one.inspect}" }
pr3 = itproc { |a, b| puts "@it = #{@it.inspect}; a = #{a.inspect}; b =
#{b}" }

# Works
puts "Call"
pr.call('Hello')
pr2.call('Hello')
pr3.call('He','llo')

# Works
puts "\n[]"
pr['Bye']
pr2['Bye']
pr3['Bye','Bye']

# Doesn't work through yield though :(
puts "\nYield"
[1,2,3].each &pr
[1,2,3].each &pr2
[1,2,3].each &pr3

Anyway, it's a bit of an abuse of instance vars I guess, and obviously
doesn't do the job properly - I wonder if anyone else has thought about
this, and whether it's something that doesn't already exist in Ruby itself
for a reason?

Cheers,
 
R

Ross Bamford

Before I used Ruby, I used Groovy. In some ways they are (well, were
anyway) quite similar, and one feature that I found cool in Groovy that
I often miss in Ruby is the implicit (/default/whatever?) block
parameter, 'it':

[1,2,3].each { puts it }

[...]

I did try hacking it from the Ruby side and came up with a halfway
solution using instance variables and the Binding extension from
Rubyforge:

I got a bit closer by defining an attr_reader 'it' on Kernel, but still it
doesn't work with all Procs or via Yield...
 
S

Simon Strandgaard

Before I used Ruby, I used Groovy. In some ways they are (well, were
anyway) quite similar, and one feature that I found cool in Groovy that
I often miss in Ruby is the implicit (/default/whatever?) block
parameter, 'it':

[1,2,3].each { puts it }

how about?

class Array
alias :each1 :each
def each(&b)
each1{|i| b.call($i =3D i) }
end
end
%w(a b c).each{p $i} #-> "a" "b" "c"
 
S

Simon Strandgaard

Before I used Ruby, I used Groovy. In some ways they are (well, were
anyway) quite similar, and one feature that I found cool in Groovy th= at
I often miss in Ruby is the implicit (/default/whatever?) block
parameter, 'it':

[1,2,3].each { puts it }
or how about?

module Kernel
def it
$i
end
end
class Array
alias :each1 :each
def each(&b)
each1{|i| b.call($i =3D i) }
end
end

[1, 2, 3].each{ puts it } # 1 2 3
 
D

Doug H

I think he wants this for any single parameter block though, not just
arrays.

What about automatically creating aliases, too? Like where you put
"alias :each1 :each", have it automatically create an alias
":eek:ld_methodname" whenever you overwrite an existing method (replace
"methodname" with actual name").
..
 
D

Dominik Bathon

Hi,

Probably this has been considered before, but I'll ask anyway.

I have also proposed this in October:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/162588

And it seems to have been proposed before.

I have actually implemented it for ruby 1.9 lately (it should work simila=
r =20
for 1.8). It's just a small patch for eval.c:

--- eval_org.c 2005-12-30 02:17:49.000000000 +0100
+++ eval.c 2006-01-02 23:25:46.000000000 +0100
@@ -4656,6 +4656,7 @@ rb_yield_0(VALUE val, VALUE self, VALUE
NODE *cnode =3D ruby_current_node;
int lambda =3D flags & YIELD_LAMBDA_CALL;
int state;
+ VALUE it_val =3D Qnil;

rb_need_block();

@@ -4675,8 +4676,10 @@ rb_yield_0(VALUE val, VALUE self, VALUE
scope_vmode =3D (flags & YIELD_PUBLIC_DEF) ? SCOPE_PUBLIC : =20
block->vmode;
ruby_block =3D block->prev;
if (block->flags & BLOCK_D_SCOPE) {
+ if (avalue && RARRAY(val)->len !=3D 0) it_val =3D RARRAY(val)->pt=
r[0];
+ if (!avalue && val !=3D Qundef) it_val =3D val;
/* put place holder for dynamic (in-block) local variables */
- ruby_dyna_vars =3D new_dvar(0, 0, block->dyna_vars);
+ ruby_dyna_vars =3D new_dvar(0, it_val, block->dyna_vars);
}
else {
/* FOR does not introduce new scope */
@@ -7596,6 +7599,15 @@ rb_exec_end_proc(void)
ruby_safe_level =3D safe;
}

+static VALUE
+rb_f_it(void)
+{
+ if (ruby_dyna_vars) {
+ if (ruby_dyna_vars->id =3D=3D 0) return ruby_dyna_vars->val;
+ }
+ return Qnil;
+}
+
void
Init_eval(void)
{
@@ -7650,6 +7662,8 @@ Init_eval(void)
rb_define_global_function("global_variables", rb_f_global_variables=
, =20
0); /* in variable.c */
rb_define_global_function("local_variables", rb_f_local_variables, =
0);

+ rb_define_global_function("it", rb_f_it, 0);
+
rb_define_method(rb_mKernel, "send", rb_f_send, -1);
rb_define_method(rb_mKernel, "__send__", rb_f_send, -1);
rb_define_method(rb_mKernel, "funcall", rb_f_funcall, -1);


This defines a global function #it that always returns the first block =20
argument, it is not assignable. #it is available whether other block =20
parameters are there or not. #it defaults to nil (if no arguments are =20
given or if not in a block context.

$ cat test_it.rb
p (1..5).to_a.map { -it }

%w[a b c].each { |x| p [x, it] }

p it
$ ./miniruby test_it.rb
[-1, -2, -3, -4, -5]
["a", "a"]
["b", "b"]
["c", "c"]
nil


Dominik
 
R

Ross Bamford

On Mon, 02 Jan 2006 20:20:30 -0000, I wrote:

Before I used Ruby, I used Groovy. In some ways they are (well, were
anyway) quite similar, and one feature that I found cool in Groovy that
I often miss in Ruby is the implicit (/default/whatever?) block
parameter, 'it':

[1,2,3].each { puts it }
or how about?

module Kernel
def it
$i
end
end
class Array
alias :each1 :each
def each(&b)
each1{|i| b.call($i = i) }
end
end

[1, 2, 3].each{ puts it } # 1 2 3

Cool, didn't consider doing it that way :) I was thinking in more general
terms, but this way does get it working with each. I did notice, though,
that it doesn't seem too happy with other Enumerable methods (select and
collect seem pretty odd)...

Cheers,
 
R

Ross Bamford

What about automatically creating aliases, too? Like where you put
"alias :each1 :each", have it automatically create an alias
":eek:ld_methodname" whenever you overwrite an existing method (replace
"methodname" with actual name").
.

That would be nice, but I wonder what'd happen if a method was aliased
more than once. The alias is another bit of noise, and doesn't protect you
from manually overwriting a previous alias, but at least you get the
chance to fix it.

Cheers,
 
D

dblack

Hi --

I think he wants this for any single parameter block though, not just
arrays.

What about automatically creating aliases, too? Like where you put
"alias :each1 :each", have it automatically create an alias
":eek:ld_methodname" whenever you overwrite an existing method (replace
"methodname" with actual name").

I've done that sometimes (alias old_meth meth), but I wouldn't want it
happening automatically. It would have the potential to clutter the
program space with methods I hadn't asked for and wouldn't need.


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 
U

Une bévue

Simon Strandgaard said:
class Array
alias :each1 :each
def each(&b)
each1{|i| b.call($i = i) }
end
end
%w(a b c).each{p $i} #-> "a" "b" "c"


could I use this mechanisms for String ?

actually i've a function call :

s="Happy new year!"
s_utf8=to_UTF8(s)
p s_utf8

with :

def to_UTF8(s)
return Iconv.new('UTF-8', 'MacRoman').iconv(s)
end

something like :

class String
def to_utf8(s)
return Iconv.new('UTF-8', 'MacRoman').iconv(s)
end
end

p "Happy new year!".to_UTF8

does that make sense ?
 
R

Ross Bamford

Hi,

Probably this has been considered before, but I'll ask anyway.

I have also proposed this in October:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/162588

And it seems to have been proposed before.

I have actually implemented it for ruby 1.9 lately (it should work
similar for 1.8). It's just a small patch for eval.c:

... [snipped patch] ...

Wicked :) I can confirm that seems to work in 1.8 (though I had to make
one change manually because the source is slightly different of course). I
guessed it'd be something that would be reasonably easy (when you know how
;)) from the C side.

FWIW +1 for inclusion in a future Ruby.

Cheers,
 
G

gabriele renzi

Ross Bamford ha scritto:
Hi,

Probably this has been considered before, but I'll ask anyway.

Before I used Ruby, I used Groovy. In some ways they are (well, were
anyway) quite similar, and one feature that I found cool in Groovy that
I often miss in Ruby is the implicit (/default/whatever?) block
parameter, 'it':

[1,2,3].each { puts it }

AFAIR this was only provided when no 'it' existed in scope, and the
block had a single argument passed when none were declared. Maybe this
would interfere with Ruby's warnings about block parameter mismatch, or
maybe the implementation doesn't allow for it, but I just wondered if
it might be possible, because I notice I do:

[1,2,3].each { |it| puts it }

and it bugs me a little bit :D

eh, I alwaya thought the same.
And I just discovered that "it" is used in at least another environment:
AliceML seem to use it to mean "the result of the last expression",
similarly to how we use "_" in irb.

Isn't it nice to write
x= 2+2
print it

;)
 
D

Devin Mullins

Hi --


You can submit it as an RCR if you wish, at http://www.rcrchive.net.
(I'll vote "Strongly opposed", but don't let that stop you :)

For what it's worth, I'll probably only vote "neutral" for the lack of
readability it encourages (not naming the things). It's not as if people
name their variables "it" a lot... :)

Devin
 
G

gabriele renzi

Devin Mullins ha scritto:

For what it's worth, I'll probably only vote "neutral" for the lack of
readability it encourages (not naming the things). It's not as if people
name their variables "it" a lot... :)

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

Ross Bamford

(e-mail address removed) wrote:

(David, can't see your message in the newsgroup, so I'll reply with this
one)

Hmm, I might put together a bit of a proposal and do that. I hope you'll
comment when you vote no, I'd be interested in your perspective on this :)

(Replying to Devin's message)
For what it's worth, I'll probably only vote "neutral" for the lack of
readability it encourages (not naming the things). It's not as if people
name their variables "it" a lot... :)

Yeah, readability is a downside but I think with a name like 'it' it would
mostly be pretty self evident, and of course it shouldn't preclude doing
it the current way (declaring the argument).

That was the reason I didn't mention another idea, about making $_ the
implicit block arg so you could do:

['one','two','three'].select { ~/t[a-z]*/ }

(I don't actually advocate that but it occured while I was playing with
the 'it' thing).
 
D

Daniel Berger

gabriele said:
Devin Mullins ha scritto:




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.

AGH! NOT THAT WORD!

- Knights of Ni
 
D

dblack

Hi --

(David, can't see your message in the newsgroup, so I'll reply with this one)


Hmm, I might put together a bit of a proposal and do that. I hope you'll
comment when you vote no, I'd be interested in your perspective on this :)

I don't like it. I've never felt there was a problem to solve in this
area, since it's so easy to specify an argument list. Having a magic
variable ("it" or $_ or whatever) seems like a step backwards, in
terms of clarity and consistency.

I can't come up with a technical argument against it, since obviously
it can be done (as witness Perl and other languages). It just strikes
me as needless, and very afterthought-like (in the sense that the fact
that blocks have named arguments always seemed to me to be what
happens *instead* of $_/it and @_ and so forth).


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 
R

Ross Bamford

I don't like it. I've never felt there was a problem to solve in this
area, since it's so easy to specify an argument list. Having a magic
variable ("it" or $_ or whatever) seems like a step backwards, in
terms of clarity and consistency.

I do agree that from a readability point of view it's potentially
confusing, and newcomers to Ruby could look at it and wonder where the
hell 'it' came from. I think though that $_ is a different argument
(again, no pun intended), since that introduces stuff like:

[1,2,3].each { print if ~/.../ }

which I couldn't convincingly argue for even were I paid to do so. This
kind of 'invisible data' confuses the hell out of me, and is IMHO one of
Perl's most evil features. On the other hand:

[1,2,3].each { puts it if it =~ /.../ }

(again ignoring the TMTOWTDI aspect for the sake of example) at least (to
me) implies some relationship between the message sent and the data - it
says to me "take each number from the array and output it if it matches
this". Granted, it's not vastly different from:

[1,2,3].each { |it| puts it if it =~ /.../ }

but the extra 'it' (or whatever) in there just grates (admittedly only
slightly) on my nerves. It also gets a bit worse when you have stuff like:

[[1,2,3],[2,3,4],[3,4,5]].each { |it| it.select { |it| it % 2 == 0 } }
[[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).
 
G

gabriele renzi

I don't like it. I've never felt there was a problem to solve in this
area, since it's so easy to specify an argument list. Having a magic
variable ("it" or $_ or whatever) seems like a step backwards, in
terms of clarity and consistency.

<ot>
eheh, I had this *exact* answer from a pythonista friend talking about
the mandatory naming of "self" in python methods :)
I guess it all comes down to taste.
</ot>
 
D

dblack

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.


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 

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

No members online now.

Forum statistics

Threads
473,775
Messages
2,569,601
Members
45,182
Latest member
alexanderrm

Latest Threads

Top