Introducing the "it" keyword

Nobuyoshi N. wrote:

p *my_strings.map(:pluralize).grep(/tests/)

p *my_strings.map(&:pluralize).grep(/tests/)

works better. =)


Ola B. (http://ola-bini.blogspot.com)
JvYAML, RbYAML, JRuby and Jatha contributor
System Developer, Karolinska Institutet (http://www.ki.se)
OLogix Consulting (http://www.ologix.com)

“Yields falsehood when quined” yields falsehood when quined.

def it
@it = block_given? ? yield : @it
end

p it if it{ ‘bar’ }

On 25.05.2007 01:00, Daniel DeLorme wrote:

puts it if it(v+1) < 10

Daniel

Not thread safe.

robert

This is obviously suboptimal code, as is, because it results in the
evaluation of m(x) twice. I propose a new keyword is added, “it”,
which may appear within the statement to the left of the decorator.
So, the previous statements become:

puts “User: #{it}” if opts[:user]

and

return it if |v + 1| < 10

IMO, this is a special case of the more general idea that within a
certain
piece of code, the value of an expression needs to be refered to more
offen.

Looking at how others do this, the functional programming / Scheme
people
usually handle it with a ‘let’ construct:

let v = opts[:user] in
puts “User: #{v}” if v

Of course this has the disadvantage that we have to invent a new name
(v).
But assuming that such a substition is only done locally, we could as
well
use placeholders. ‘it’ would be such a placeholder, kind of:

let opts[:user] in puts “User: #{it}” if it

but this would restrict us to have only one binding at any time. A more
general
solution could be like this:

let a+b+c in
let foo=de in
let x/y in
puts ($1+foo)
($1-$2)-$2/($1-foo)

Here, $1 would be bound to x/y (innermost unnamed let binding), $2 would
be bound
to a+b+c (second innermost unnamed let binding). This is similar to the
binding of
parameters in the DeBrujin Lambda calculus, if my rusty memory is right
(any FP
people here who can comment on this?).

Of course it does not mean that the notation $n is really good here (as
it is
already taken by a different usage), but one could equally well think of
other
ways to denote this, such as _1 or \1 or whatever it is seen practical.

Ronald

$_ releases after the method.
Ah. This works today:

return $_ if $_ = opts[:user]

But it’s still hard to read :slight_smile:

And don’t forget:

$_ = “foo”
puts $_ if ($_ = “bob” && chomp)

will print “foo”! (I feel like I’m back writing Perl again)

p *my_strings.map(:pluralize).grep(/tests/)
Ah, yes, my ruby-fu is lacking, but it was a contrived
example :slight_smile: Here’s another contrived one… the gist is
that you may need to use “it” more than just as a passthrough:

my_strings.map { it.substring(it.maxlen) }.filter { it.size > 5 }.each
{ p “#{it}: #{it.size}” }

def it
@it = block_given? ? yield : @it
end

p it if it{ ‘bar’ }

Neat. Instance variable could leak, but still neat!

And Re: Rspec, well, if the powers that be (not me) like ‘it’,
then I guess they’ll have to move out of the way :slight_smile:

And don’t tell me that this is too much typing for you.
Well, it is kinda, but that’s not as bad as the fact that
‘it’ will be un Gc-able till the end of the block unless
we do:

it = v+1
return it if it < 10
it = nil

It’s reminiscent of the all too common ruby memory ‘leak’:
(http://whytheluckystiff.net/articles/theFullyUpturnedBin.html)

x = [1, 2, 3]
loop do

add some crap to x and use it

end

slow_method_that_doesnt_use_x()

This problem is even trickier w/ closures grabbing x into their
binding too AFAIK. “it” needs to show "it"self to the door
when “it”'s no longer wanted. (It’s late)

On 25.05.2007 10:21, Greg F. wrote:

(http://whytheluckystiff.net/articles/theFullyUpturnedBin.html)
when “it”'s no longer wanted. (It’s late)
I see your point. I do wonder though how relevant this is in practice.
If you write 100+ lines methods then you have other problems than a
few objects that are kept around longer than needed. Even with short
methods that exhibit the behavior you described above it is only an
issue if the object kept around keeps a lot of memory from being
reclaimed (which is not the case for the example above) or if the method
is active multiple times. After all the cost of changing the language
should be smaller than the cost incurred by this missing feature.

Kind regards

robert

On Fri, May 25, 2007 at 07:42:09AM +0900, [email protected] wrote:

return v + 1 if its < 10

I’m not really clear on how that’s any better than this:

return v + 1 if v < 10

In fact, I think the “its” version is a bit more ambiguous, and thus
probably worse. Am I missing something?

On 24.05.2007 23:35, Greg F. wrote:

or

This keyword allows better DRY in ruby for this g(m(x)) op h(m(x))
pattern and also provides a nice optimization since your average lazy
programmer will usually evaluate m(x) twice instead of putting it into
temporary local storage themselves. By promoting it to a keyword, you
also prevent the problems seen here: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/166828

Thoughts?

I don’t understand why you want to change the language where you can
easily do

it = v+1
return it if it < 10

This is efficient and very readable. I consider variants like

return it if (it = v+1) < 10

as degenerate because the nested assignment does neither improve
readability nor efficiency nor is it semantically necessary. (The only
places where an assignment in a conditional can make code significantly
simpler and less redundant are loops.)

And don’t tell me that this is too much typing for you.

Regards

robert

Greg F. wrote:

puts “User: #{it}” if (it=opts[:user]).

AFAIK this doesn’t work because it will be assumed to
be a method call. That’s the crux of the linked post
above.

That’s what you get for not using a sigil. You never know what’s a
method call and what’s a local variable. Especially since you can’t
declare variables in Ruby.

Jenda

On Fri, May 25, 2007 at 06:40:08AM +0900, Greg F. wrote:

puts “User: #{opts[:user]}” if opts[:user]

I’m a little groggy, and about to go to bed, so I’m long on vague ideas
and short on specifics – but can’t that be done with something like
this bit of pseudocode?:

opts[:user].something do {|v| make stuff happen}

Greg F. wrote:

$_ releases after the method.
Ah. This works today:

return $_ if $_ = opts[:user]

But it’s still hard to read :slight_smile:

At least in one case I know what’s the variable here. For $_ that is,
opts might be anything.

And don’t forget:

$_ = “foo”
puts $_ if ($_ = “bob” && chomp)

will print “foo”!

Which has nothing to do with $_ or chomp using $_ behind the scenes.
It’s a problem with operator precedence.

It works as if you’ve written

$_ = “foo”
puts $_ if ($_ = (“bob” && chomp))

Which means you could get the same confusion if you tried

x = ‘foo’
puts x if (x = ‘bob’ && 1 > 0)

Though there’s a chance that the “true” you get in this case would
suggest better what’s the problem.

(I feel like I’m back writing Perl again)

I wish I was.

And actually talking about Perl and $_, the way you’d “remember a
subexpression to use it several times” would be

for () {
return $_ if $_ > 45 and f($) and whatever( ‘with’, $)
}

the difference from

$_ = );
return $_ if $_ > 45 and f($) and whatever( ‘with’, $)

being that the for(){} localizes the change of $. That is, $ only gets
the new value within the “loop”. Which is something the proposed “it”
would have to provide as well.

Jenda

Chad P. wrote:

On Fri, May 25, 2007 at 07:42:09AM +0900, [email protected] wrote:

return v + 1 if its < 10

I’m not really clear on how that’s any better than this:

return v + 1 if v < 10

In fact, I think the “its” version is a bit more ambiguous, and thus
probably worse. Am I missing something?

Yep, the fact that he wanted to be able to shorten something like

return f(x, y) + 47Pi/y if f(x, y) + 47Pi/y > 0

and make sure the expression is only evaluated once. Though I do think
at least in Ruby the most sensible solution is

tmp = f(x, y) + 47*Pi/y
return tmp if tmp > 0

Jenda

Greg F. wrote:

but here are a few other ideas:

puts it if (x + y) * (it = (a - b)) < 10

This one already works. No changes to ruby needed.

On May 24, 11:55 pm, Greg F. [email protected] wrote:

The less silly variable names we have to come up with, the better!

I would say that it would always belong to the current block. If you
need “it” from the parent block it’s easy enough to assign a variable
to it.

However, I wasn’t intending that ‘it’ reference the block arguments,
but the iteration itself, with useful methods about the state of
iteration, eg. it.first?, it.last?, it.even?, it.odd?, it.index. Of
course this could also include it.value.

T.

$_ = “foo”
puts $_ if ($_ = “bob” && chomp)

will print “foo”!

Which has nothing to do with $_ or chomp using $_ behind the scenes.
It’s a problem with operator precedence.
I disagree. Using ‘it’ here would avoid worries about side-effects.

Anyway, I thought ruby was moving away from these implicit magic $*
variables? Using pipes, the assignment to it would be explicit and
safe from side effects.

for () {
return $_ if $_ > 45 and f($) and whatever( ‘with’, $)
}

the difference from

$_ = );
return $_ if $_ > 45 and f($) and whatever( ‘with’, $)

being that the for(){} localizes the change of $.
Right, this is kinda sorta the pattern I am trying to kill
with “it”, except it is less verbose. In addition, you still
have side effects on $
as above, you’d generally have to
grab $_ into a local (with a name) if you were going to use
methods that could change it.

This one already works. No changes to ruby needed.
NameError: undefined local variable or method `x’ for main:Object

One thing to mention. Coming up with examples that come close
to the semantics of ‘it’ (without exact semantics) is missing
the point. If these other solutions were optimal people would
be using them already, but as far as I’ve seen lots of ruby
code has the pattern implemented lazily.

There are really two genuine criticisms to ‘it’ I can see
based upon this thread:

  • The syntax is too confusing
  • It is not worth polluting the language over, despite its utility

Upon reflection, unless I am mistaken, there is something close
with correct semantics, a la the references to ‘let’ and
‘for’ in Scheme and Perl. It seems kind of odd nobody has given
this one before, since it is pretty simple and I feel dumb for
not mentioning it until now.

def with(x)
yield x
end

with (v + 1) { |it| return it if it < 5 }

Personally, I think this is the closest we can get in Ruby, and
can serve as a concrete starting point for what we gain by
introducing a keyword. Based upon this ‘it’ now officially
gains sugar and performance, since this above example can match
the same semantics and side effects.

Why it is good sugar:

  • Less code. The value for ‘it’ moves much closer to its usage.

  • Less explicit naming. If ‘it’ becomes the default name for the
    block parameter and also is set during end of line conditionals,
    ruby code will be less polluted with throwaway user variable names.
    (This is a pet peeve of mine, why do I have to keep saying x,y,z
    and my co-worker says a,b,c? If we just use ‘it’, its consistent.)

  • It will result in ruby idioms that are clean and correct. If the
    above example works go great, why aren’t people using it? Probably
    because it’s a lot of extra typing for perceived little gain, even
    though it is the most ‘correct’ implementation. I feel like this
    common pattern should be supported in the most terse form possible.

Why it is good for performance:

  • We save a full block invocation and binding opposed to the above,
    I think. I don’t know enough about the interpreter to be sure of
    this, though.

Also, even if ‘it’ was just added as the default first name, you
could get:

def with (x); yield x; end;

with (v + 1) { puts it if it < 5 }

This might be a good compromise!

On 5/25/07, Gregory S. [email protected] wrote:

You’re working too hard:
So are you.

use_let(1,2,3) # 6
use_let(4,5,6) # nil
returning nil would not be what OP wanted right?

What is wrong with what already works?

return sum if (sum = 1 + 2 + 3) < 10

This has been pointed out already in this thread.

Cheers
Robert

This has been pointed out already in this thread.
This has also been pointed out as not working :slight_smile: Unless
sum is previously defined it will not, and if it is, it will
hold sum too long.

I also realized this case sucks:
puts it if true || |my_string|

Either it doesn’t get defined or ruby has to bind it pipes
higher than ||. I consider these both somewhat borked.

I still want that ‘it’ default first name though!

let 1+2+3 { puts it if it < 4 }

Nice.

On Fri, May 25, 2007 at 04:58:08PM +0900, Ronald F. wrote:

Of course this has the disadvantage that we have to invent a new name
let x/y in
puts ($1+foo)*($1-$2)-$2/($1-foo)

Here, $1 would be bound to x/y (innermost unnamed let binding), $2 would
be bound to a+b+c (second innermost unnamed let binding). This is similar
to the binding of parameters in the DeBrujin Lambda calculus, if my rusty
memory is right (any FP people here who can comment on this?).
[…]

You’re working too hard:

def let(*args)
yield *args
end

def use_let(a,b,c)
let a+b+c do |it|
return it if it < 10
end
end

use_let(1,2,3) # 6
use_let(4,5,6) # nil

No need for additional keywords or even global variables, just a little
method named let.

Ronald
–Greg

On 5/24/07, Greg F. [email protected] wrote:

A common pattern seen in a lot of ruby code is:

g(m(x)) if/unless/while/until h(m(x))

Hmm, this really isn’t far from the Ruby equivalent of the common
Scheme/Lisp anaphoric-if macro I’ve always wanted. Here’s the On Lisp
snippet about the same:
http://www.bookshelf.jp/texi/onlisp/onlisp_17.html#SEC111 (I picked up
the AIF habbit from a Graham colleague/protoge, so those folks may be
the only ones who use it).

Basically, I just want if to take a block. Here’s that expressed as
code:

pretend num_or_nil was very expensive

def num_or_nil
r = rand(2)
if r == 1
1
else
nil
end
end

=> nil

if num_or_nil {|it|
it + 20
}

=> 21 or nil

HTH,
Keith