Introducing the "it" keyword

Greg Fodor wrote:

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.

How about something like: return $1 if (v + 1) < 10 if you wanted to return v + 1? If you wanted to return v you could use: return $2 if ((v) +1) < 10. Improvements would be using some of the regular expression flags to control what $1, $2, etc. refer to.

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

I believe this is to be fixed in a future version of Ruby.

While we're at it I have another use for "it" I'd appreciate very
much. An (it)erator reference.

  > 0..3.each{ p it.index }
  0
  1
  2
  3

"Blech" to _with_index.

T.

···

On May 24, 9:25 pm, Greg Fodor <gfo...@gmail.com> wrote:

Greg Fodor wrote:

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

I'm not opposed to the idea, but I really, really hate "it". "it" doesn't mean anything. "it" might as well be "that" or "him" or "this"...oh wait, perhaps not "this".

How about "her"? We don't have a good ratio of women involved in Ruby anyway, so we can feel better about ourselves then.

What about a "local global" like we have for other things ($_ and $~ for example?

return $RESULT if v + 1 < 10

-or-

return $result if v + 1 < 10

Although this raises a question....is it supposed to return the result of "v + 1" or "v + 1 < 10"?

In general I'd say introducing a keyword is the last thing that should be considered.

- Charlie

Hi,

At Fri, 25 May 2007 08:20:09 +0900,
Greg Fodor wrote in [ruby-talk:252866]:

> 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 introduces the correct scope (yours does not)
- It avoids side effects (yours changes global space)

By using $_ instead of $it, these will solve.

- It releases memory upon GC after the statement (yours doesn't)

$_ releases after the method.

···

--
Nobu Nakada

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 25.05.2007 10:21, Greg Fodor wrote:

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)

Greg Fodor 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

···

--
Posted via http://www.ruby-forum.com/\.

Chad Perrin 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) + 47*Pi/y if f(x, y) + 47*Pi/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

···

On Fri, May 25, 2007 at 07:42:09AM +0900, dblack@wobblini.net wrote:

--
Posted via http://www.ruby-forum.com/\.

You're working too hard:

So are you.

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

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

···

On 5/25/07, Gregory Seidman <gsslist+ruby@anthropohedron.net> wrote:
--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

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

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.

With the crucial difference that in your solution, you are *required*
to name the bound variable (here: it), while in my proposal, you are
not.

I think if we only want to introduce an auxiliary variable as
"abbreviation"
for an expression which occurs repeatedly in a block, Ruby indeed has
many
ways to do it, as do most other languages I know. The problem becomes
interesting
IMO if we are libarated from the requirement to invent a name for such a
variable.
The OP suggested to have a "reserved variable" named 'it' which is kind
of
implicitly bound, but this is a special case for a special type of
programming
pattern. My alternative suggestion go into a more general direction,
where you
can have more than one binding, and where it is the programmer's choice
whether
or not to name the variables.

Ronald

···

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

Hi --

···

On Fri, 25 May 2007, Chad Perrin wrote:

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

Yes: the its version is equivalent to:

   return v + 1 if v + 1 < 10

but doesn't require the v + 1 calculation to be performed twice.

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)

How about something like: return $1 if (v + 1) < 10 if you wanted to
return v + 1? If you wanted to return v you could use: return $2 if
((v) +1) < 10. Improvements would be using some of the regular
expression flags to control what $1, $2, etc. refer to.

I like this better than def it() since it seems more intentional,
but it too results in side effects into global space and prevents
GCing early.

Another problem is that you run into problems mixing the
meaning of parenthesis, since within a given expression in your
example you may not want to set aside storage space for all
parenthesized expressions, despite the fact you may need to enforce
order of operations.

For example:

puts $2 if (x + y) * (a - b) < 10

will copy the result of x + y in RAM even if your parentheses are
there to simply make the add happen first. Having it be something
new in this context like pipes separates concerns and avoids these
technical consequences.

FYI, this would be:

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

We just get temp storage for a - b and preserve a single meaning
of parentheses.

While we're at it I have another use for "it" I'd appreciate very
much. An (it)erator reference.

Wow. Yes! Only issue I see is nested blocks though.
(Which it is "it?" :)) Spose it could through a warning if 'it'
comes into scope twice.

But this would be cool:

my_strings.map { pluralize it }.filter { it =~ /tests/ }.each { p it }

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

return $result if v + 1 < 10

Although this raises a question....is it supposed to return the result
of "v + 1" or "v + 1 < 10"?

As proposed right now, by default, it will be set to the value of
the expression (v + 1 < 10), unless a subexpression is specified.

I proposed pipes to specify such a subexpression and Michael proposed
inheriting the regex style of ordered paren grouping, which has its
own set of pros and cons.

Personally, I like 'it' :slight_smile: It reads nicely and makes sense to me.

It really *shouldn't* mean or imply anything about it's value,
because "it" is whatever you're currently most likely to be 'talking
about.' I'd argue that the keyword should only be used for short
blocks or expressions, otherwise you're right, it may get confusing.

For example:

return it if |x| < 5

and

strings.map { pluralize it }

are good but

if |x| < 5
  .. lots of code ..
  my_var = it * 5
end

and

strings.map do
  .. lots of code ..
  pluralize it
end

Are less desirable.

"it" seems best to be used for short one or two liners, not
sprinkled throughout long block bodies. This may or not be
something we'd even want the interpreter to enforce.

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.

It doesn't work. But this does:

if (sum = a+b+c) > 10 then return sum end

I don't think I'd write it that way in my own code, though.

Paul.

···

On 25/05/07, Robert Dober <robert.dober@gmail.com> wrote:

What is wrong with what already works?

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

This has been pointed out already in this thread.

Btw, here is a solution that does not need new syntax and does not exibit memory issues (I think):

irb(main):001:0> def it_test(v)
irb(main):002:1> (v+1).instance_eval do
irb(main):003:2* return self if self < 10
irb(main):004:2> end
irb(main):005:1> "nothing"
irb(main):006:1> end
=> nil
irb(main):007:0> it_test 0
=> 1
irb(main):008:0> (0..10).map {|i| it_test i}
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, "nothing", "nothing"]

Kind regards

  robert

···

On 25.05.2007 11:40, Robert Klemme wrote:

On 25.05.2007 10:21, Greg Fodor wrote:

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

Hi --

···

On Fri, 25 May 2007, Charles Oliver Nutter wrote:

Greg Fodor wrote:

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

I'm not opposed to the idea, but I really, really hate "it". "it" doesn't mean anything. "it" might as well be "that" or "him" or "this"...oh wait, perhaps not "this".

How about "her"? We don't have a good ratio of women involved in Ruby anyway, so we can feel better about ourselves then.

What about a "local global" like we have for other things ($_ and $~ for example?

return $RESULT if v + 1 < 10

-or-

return $result if v + 1 < 10

The argument from precedent is tricky, though. I'd say the presence
of $_ and $~ and friends suggests, not that more local globals are
desireable, but that Ruby has reached, and perhaps exceeded, its quota
of them :slight_smile:

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)

Hi --

···

On Fri, 25 May 2007, Michael W. Ryder wrote:

How about something like: return $1 if (v + 1) < 10 if you wanted to return v + 1? If you wanted to return v you could use: return $2 if ((v) +1) < 10. Improvements would be using some of the regular expression flags to control what $1, $2, etc. refer to.

But $1, $2, ... are already taken. What would happen here, for
example?

   return $1 if /(abc)/.match("abc")

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)

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

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

With the crucial difference that in your solution, you are *required*
to name the bound variable (here: it), while in my proposal, you are
not.

Implicit variables make me queasy. I don't like Perl's $_, I don't like x86
assembly's numerous ops with implicit source and/or destination registers,
and I don't like a magical "it" appearing out of nowhere.

I think if we only want to introduce an auxiliary variable as
"abbreviation" for an expression which occurs repeatedly in a block, Ruby
indeed has many ways to do it, as do most other languages I know. The
problem becomes interesting IMO if we are libarated from the requirement
to invent a name for such a variable.

The OP suggested to have a "reserved variable" named 'it' which is kind
of implicitly bound, but this is a special case for a special type of
programming pattern. My alternative suggestion go into a more general
direction, where you can have more than one binding, and where it is the
programmer's choice whether or not to name the variables.

The OP brought up an interesting issue, but what made it interesting is not
the implicit binding but the need for a scoped temporary value. There are
three important optimization goals to it:

1) avoid holding onto memory any longer than necessary (memory)
2) avoid clumsy use of temporary values (readability)
3) avoid recomputing the same value more than once (speed)

See Why are local variables bad?
for a discussion of local variables and better ways of doing things
(including the functional let).

Importantly, an implicit variable name has two problems. First, it reduces
readability. Second, even if naming the variable explicitly is optional
rather than required, you get the same sorts of maintenance problems caused
by the optionally braceless block notation for single statements in C-derived
syntaxes, e.g.

let Time.now in
  puts "starting at #{$1}"
  let a+b+c in
    puts "sum = #{$1}" if $1 > 3
  end
end

...is as bad an issue as...

for (int i=0; i<5; ++i)
  printf("%d\n", i);
  fprintf(stderr, "%d\n", i);

Basically, if you need to add an outer scope, you have to renumber all of
the temporaries. This is unnecessary and unreasonable pain to inflict on
maintainers of the code, even if it's your own code.

Looking at it from a different perspective, consider whether any proposed
change to Ruby moves it closer to language X (which I'm equating to Perl)
or closer to language Y (which I'm equating to Lisp). Moving toward X means
making things easy to express tersely at the expense of overall readability
and maintainability. Moving toward Y means simplifying the expression of
things using existing syntax and functionality. I claim that we should
strive to avoid shortcuts that bring Ruby closer to X and strive to solve
our problems in ways that bring Ruby closer to Y.

Ronald

--Greg

···

On Tue, May 29, 2007 at 04:55:01PM +0900, Ronald Fischer wrote:

Actually, this example:

puts $2 if (x + y) * (a - b) < 10

Could avoid storing (x + y) by analyzing the AST
of the puts expression, noting that $1 does not appear.
Recall a similar optimization happens with the proposed it
keyword if it is not used on the left hand side.

So, that point about RAM I made above is partially wrong,
except for the fact that analyzing the AST for the presence
of a keyword for optimization reasons seems less kludgy than
analyzing it for references to global variables $1, $2..
(since they are in the runtime's name space as opposed to the
language syntax itself.)