Introducing the "it" keyword

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

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

for example,

puts "User: #{opts[:user]}" if opts[:user]
in this case, g(x) = puts "User: #{x}", h(x) = x, and m(x) =
opts[:user]

or

return v+1 if v +1 < 10
In this case, g(x) = x, h(x) = x < 10, and m(x) = v + 1

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

If and only if "it" is seen on the left hand side, the ruby
interpreter should store the expression result on the conditional into
a temporary storage and evaluate that as "it". It falls out of scope
after the statement. The use of pipes can designate a subexpression to
use for "it" instead (I don't have my heart set on pipes, but you get
the idea.)

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?

Hi --

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

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

for example,

puts "User: #{opts[:user]}" if opts[:user]
in this case, g(x) = puts "User: #{x}", h(x) = x, and m(x) =
opts[:user]

or

return v+1 if v +1 < 10
In this case, g(x) = x, h(x) = x < 10, and m(x) = v + 1

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

If and only if "it" is seen on the left hand side, the ruby
interpreter should store the expression result on the conditional into
a temporary storage and evaluate that as "it". It falls out of scope
after the statement. The use of pipes can designate a subexpression to
use for "it" instead (I don't have my heart set on pipes, but you get
the idea.)

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 think something like 99% of all suggestions for changes to Ruby
involve new punctuation -- which means that only a very tiny number of
them can be accepted before Ruby turns into complete line noise. So
I'd not advocate the pipes version. Maybe you could do:

   return v + 1 if its < 10

which of course is missing an apostrophe, speaking of punctuation :slight_smile:
But something like that, maybe.

David

···

On Fri, 25 May 2007, Greg Fodor wrote:

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

Greg Fodor wrote:

return it if |v + 1| < 10

No need for it to be such a special built-in construct.
Maybe you could do it like this:

  def it(*args)
    $it = args unless args.empty?
    return *$it
  end
  puts it if it(v+1) < 10

Daniel

Well, you can do something very similar without changing ruby:

something like:

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

and you can do other things like:

puts "This: #{this} and That: #{that}" if (this=opts[:a]) and (that=opts[:b]).

I think this modification is not worth it. But I might be wrong.

Aureliano.

···

On 5/24/07, Greg Fodor <gfodor@gmail.com> wrote:

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

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

for example,

puts "User: #{opts[:user]}" if opts[:user]
in this case, g(x) = puts "User: #{x}", h(x) = x, and m(x) =
opts[:user]

or

return v+1 if v +1 < 10
In this case, g(x) = x, h(x) = x < 10, and m(x) = v + 1

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

If and only if "it" is seen on the left hand side, the ruby
interpreter should store the expression result on the conditional into
a temporary storage and evaluate that as "it". It falls out of scope
after the statement. The use of pipes can designate a subexpression to
use for "it" instead (I don't have my heart set on pipes, but you get
the idea.)

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?

Greg Fodor wrote:

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

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

for example,

puts "User: #{opts[:user]}" if opts[:user]
in this case, g(x) = puts "User: #{x}", h(x) = x, and m(x) =
opts[:user]

or

return v+1 if v +1 < 10
In this case, g(x) = x, h(x) = x < 10, and m(x) = v + 1

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

Hasn't 'it' effectively been reserved as a keyword by the RSpec team

# bowling_spec.rb
require 'bowling'

describe Bowling do
   before(:each) do
     @bowling = Bowling.new
   end

   it "should score 0 for gutter game" do
     20.times { @bowling.hit(0) }
     @bowling.score.should == 0
   end
end

···

--
Brad Phelan
http://xtargets.com

If and only if "it" is seen on the left hand side, the ruby
interpreter should store the expression result on the conditional into
a temporary storage and evaluate that as "it". It falls out of scope
after the statement. The use of pipes can designate a subexpression to
use for "it" instead (I don't have my heart set on pipes, but you get
the idea.)

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?

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=d*e 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

···

--
Ronald Fischer <ronald.fischer@venyon.com>
Phone: +49-89-452133-162

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

···

On 24.05.2007 23:35, Greg Fodor wrote:

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

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

for example,

puts "User: #{opts[:user]}" if opts[:user]
in this case, g(x) = puts "User: #{x}", h(x) = x, and m(x) =
opts[:user]

or

return v+1 if v +1 < 10
In this case, g(x) = x, h(x) = x < 10, and m(x) = v + 1

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

If and only if "it" is seen on the left hand side, the ruby
interpreter should store the expression result on the conditional into
a temporary storage and evaluate that as "it". It falls out of scope
after the statement. The use of pipes can designate a subexpression to
use for "it" instead (I don't have my heart set on pipes, but you get
the idea.)

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'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}

···

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

puts "User: #{opts[:user]}" if opts[:user]

--
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
Brian K. Reid: "In computer science, we stand on each other's feet."

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:
bookshelf.jp (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

···

On 5/24/07, Greg Fodor <gfodor@gmail.com> wrote:

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

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

I usually just do things like

a=g(x) and h(a)

Avoids the double evaluation at the cost of an assignment. This makes
perfect sense to me, but then I'm a C programmer, so I'm comfortable with
short-circuiting. I'm aware other people think it's harder to understand or
less Ruby-esque.

Quoting dblack@wobblini.net:

Hi --

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

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

for example,

puts "User: #{opts[:user]}" if opts[:user]
in this case, g(x) = puts "User: #{x}", h(x) = x, and m(x) =
opts[:user]

or

return v+1 if v +1 < 10
In this case, g(x) = x, h(x) = x < 10, and m(x) = v + 1

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

If and only if "it" is seen on the left hand side, the ruby
interpreter should store the expression result on the conditional into
a temporary storage and evaluate that as "it". It falls out of scope
after the statement. The use of pipes can designate a subexpression to
use for "it" instead (I don't have my heart set on pipes, but you get
the idea.)

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 think something like 99% of all suggestions for changes to Ruby
involve new punctuation -- which means that only a very tiny number of
them can be accepted before Ruby turns into complete line noise. So
I'd not advocate the pipes version. Maybe you could do:

  return v + 1 if its < 10

which of course is missing an apostrophe, speaking of punctuation :slight_smile:
But something like that, maybe.

We are talking about semantic sugar here, so I don't think it's crucial. But why not try?

What about this less intrusive approach?

return v + 1 if < 10

Just omit entirely the subject and make operators assume the subject is what we "return".

···

On Fri, 25 May 2007, Greg Fodor wrote:

--
Pau Garcia i Quiles
http://www.elpauer.org
(Due to the amount of work, I usually need 10 days to answer)

Hi --

> A common pattern seen in a lot of ruby code is:
>
> g(m(x)) if/unless/while/until h(m(x))
>
> for example,
>
> puts "User: #{opts[:user]}" if opts[:user]
> in this case, g(x) = puts "User: #{x}", h(x) = x, and m(x) =
> opts[:user]
>
> or
>
> return v+1 if v +1 < 10
> In this case, g(x) = x, h(x) = x < 10, and m(x) = v + 1
>
> 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
>
> If and only if "it" is seen on the left hand side, the ruby
> interpreter should store the expression result on the conditional into
> a temporary storage and evaluate that as "it". It falls out of scope
> after the statement. The use of pipes can designate a subexpression to
> use for "it" instead (I don't have my heart set on pipes, but you get
> the idea.)
>
> 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 think something like 99% of all suggestions for changes to Ruby
involve new punctuation -- which means that only a very tiny number of
them can be accepted before Ruby turns into complete line noise. So
I'd not advocate the pipes version. Maybe you could do:

   return v + 1 if its < 10

which of course is missing an apostrophe, speaking of punctuation :slight_smile:
But something like that, maybe.

Well, you fixed the thing that was bothering me. In the original
proposal, 'it' appeared too early, leaving me wonder what 'it' was.
It makes sense for 'it' to follow what 'it' is referring to.

You could solve the apostrophe problem with the longer 'it_is'.

Overall, I like the idea. It solves the lazy programmer problem which
leads to double evaluation of the same expression Those who take the
effort to assign it to a temp variable, can now be lazy, and do it
with one line.

···

On 5/24/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

On Fri, 25 May 2007, Greg Fodor wrote:

David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

--
Bill Guindon (aka aGorilla)
The best answer to most questions is "it depends".

Maybe you could do:

   return v + 1 if its < 10

-- snip

return v + 1 if < 10

Unfortunately this seems much more difficult and ambiguous
for a number of reasons. First, it is impossible to know
that our implied "it" should be "v + 1" without making
assumptions about the intent. (Why don't we include the
result of the return expression itself? What if it's more
complex?)

More importantly, semantics of order of evaluation become
confusing. Despite v + 1 being evaluated during the
if conditional, it syntactically appears in the return
statement. My approach preserves all current ruby semantics
and is more intentional, I feel. It basically transforms
this:

tmp = m(x)
h(tmp) if g(tmp)

into just

h(it) if g(it = m(x))

The it keyword is essential for one reason and nice for
a few others. It is essential because of the ruby semantics
for determining identifiers vs. methods, referenced in my
original post.

It is nice because we no longer have to come up with the
name "tmp,"ruby provides a consistent intentional name.
It is also nice because it is less code. It is also nice
because it provides correct scope semantics so it will be
GCed at the optimal time.

No need for it to be such a special built-in construct.

Depends on what you mean by "need." For an optimal solution,
I feel it does. "Optimal" means:

- It only evaluates m(x) once (yours does this)
- It is the most terse representation of intent (subjective)
- It introduces the correct scope (yours does not)
- It releases memory upon GC after the statement (yours doesn't)
- It avoids side effects (yours changes global space)

Its hard to think of a solution to this implemented in ruby
that fits these parameters without introducing a new
language level construct.

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.

Not thread safe.

  robert

···

On 25.05.2007 01:00, Daniel DeLorme wrote:

Greg Fodor wrote:

return it if |v + 1| < 10

No need for it to be such a special built-in construct.
Maybe you could do it like this:

def it(*args)
   $it = args unless args.empty?
   return *$it
end
puts it if it(v+1) < 10

Daniel

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)

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 Fri, May 25, 2007 at 07:42:09AM +0900, dblack@wobblini.net wrote:

  return v + 1 if its < 10

--
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
Dr. Ron Paul: "Liberty has meaning only if we still believe in it when
terrible things happen and a false government security blanket beckons."

When I first heard about the change from context/specify to describe/
it, I thought it was completely bone-headed. A quick glance at an
example changed my mind, but I still don't feel good about 'it'.
Considering the number of specifications that will start with
'should', why not use --- oh I don't know, 'should'?

describe Bowling do
   before(:each) do
     @bowling = Bowling.new
   end

   should "score 0 for gutter game" do
     20.times { @bowling.hit(0) }
     @bowling.score.should == 0
   end
end

···

On May 25, 2:40 am, Brad Phelan <phe...@tttech.ttt> wrote:

Greg Fodor wrote:

Hasn't 'it' effectively been reserved as a keyword by the RSpec team

# bowling_spec.rb
require 'bowling'

describe Bowling do
   before(:each) do
     @bowling = Bowling.new
   end

   it "should score 0 for gutter game" do
     20.times { @bowling.hit(0) }
     @bowling.score.should == 0
   end
end

--
Brad Phelanhttp://xtargets.com

--
-yossef

> 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=d*e 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?).

[...]

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 Fri, May 25, 2007 at 04:58:08PM +0900, Ronald Fischer wrote:

That shouldn't be a reason to prevent the addition of a keyword, if
it's suitable.

-austin, still not convinced that RSpec is useful

···

On 5/25/07, Brad Phelan <phelan@tttech.ttt> wrote:

Hasn't 'it' effectively been reserved as a keyword by the RSpec team

--
Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/
               * austin@halostatue.ca * You are in a maze of twisty little passages, all alike. // halo • statue
               * austin@zieglers.ca