Implicit block parameter?


#1

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 :smiley:

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[:@it]
          binding[:@it] = args[0]
          super
        ensure
          binding[:@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,


#2

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

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…


#3

On 1/2/06, Ross B. removed_email_address@domain.invalid wrote:

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 }

how about?

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”


#4

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
“:old_methodname” whenever you overwrite an existing method (replace
“methodname” with actual name").


#5

On 1/2/06, Simon S. removed_email_address@domain.invalid wrote:

On 1/2/06, Ross B. removed_email_address@domain.invalid wrote:

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


#6

On Mon, 02 Jan 2006 21:32:57 +0100, Ross B.
removed_email_address@domain.invalid wrote:

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:

— 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 = ruby_current_node;
int lambda = flags & YIELD_LAMBDA_CALL;
int state;

  • VALUE it_val = Qnil;

    rb_need_block();

@@ -4675,8 +4676,10 @@ rb_yield_0(VALUE val, VALUE self, VALUE
scope_vmode = (flags & YIELD_PUBLIC_DEF) ? SCOPE_PUBLIC :
block->vmode;
ruby_block = block->prev;
if (block->flags & BLOCK_D_SCOPE) {

  •   if (avalue && RARRAY(val)->len != 0) it_val = 
    

RARRAY(val)->ptr[0];

  •   if (!avalue && val != Qundef) it_val = val;
       /* put place holder for dynamic (in-block) local variables */
    
  •   ruby_dyna_vars = new_dvar(0, 0, block->dyna_vars);
    
  •   ruby_dyna_vars = 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 = safe;
}

+static VALUE
+rb_f_it(void)
+{

  • if (ruby_dyna_vars) {

  •   if (ruby_dyna_vars->id == 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,
    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
argument, it is not assignable. #it is available whether other block
parameters are there or not. #it defaults to nil (if no arguments are
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


#7

On Mon, 02 Jan 2006 22:06:49 -0000, Simon S.
removed_email_address@domain.invalid
wrote:

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

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 :slight_smile: 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,


#8

On Mon, 02 Jan 2006 22:26:46 -0000, Doug H removed_email_address@domain.invalid wrote:

What about automatically creating aliases, too? Like where you put
“alias :each1 :each”, have it automatically create an alias
“:old_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,


#9

Hi –

On Tue, 3 Jan 2006, Doug H wrote:

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
“:old_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
removed_email_address@domain.invalid

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


#10

On Mon, 02 Jan 2006 22:54:43 -0000, Dominik B. removed_email_address@domain.invalid
wrote:

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 :slight_smile: 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,


#11

Simon S. removed_email_address@domain.invalid wrote:

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 ?


#12

Hi –

On Tue, 3 Jan 2006, Ross B. wrote:

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.

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 :slight_smile:

David


David A. Black
removed_email_address@domain.invalid

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


#13

Ross B. ha scritto:

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 :smiley:

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

:wink:


#14

removed_email_address@domain.invalid wrote:

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 :slight_smile:

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… :slight_smile:

Devin


#15

gabriele renzi wrote:

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

#16

Devin M. 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… :slight_smile:

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.


#17

On Tue, 03 Jan 2006 13:52:03 -0000, Devin M. removed_email_address@domain.invalid
wrote:

removed_email_address@domain.invalid wrote:

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

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 :slight_smile:

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
:slight_smile:

(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… :slight_smile:

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


#18

On Tue, 03 Jan 2006 17:26:15 -0000, removed_email_address@domain.invalid wrote:

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 :slight_smile:

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


#19

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.

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.

#20

Hi –

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

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 :slight_smile:

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
removed_email_address@domain.invalid

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