Introducing the "it" keyword

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.

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

This has been pointed out already in this thread.
This has also been pointed out as not working :slight_smile:

You are right I got caught by irb - again. I did not see the post
telling so,
sorry for the noise.

sum is previously defined it will not, and if it is, it will
hold sum too long.
That however could be disputed, but is not of interest any more :frowning:

Robert

On 25.05.2007 11:40, Robert K. wrote:

It’s reminiscent of the all too common ruby memory ‘leak’:
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.

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/07, Robert D. [email protected] 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.

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.

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

You can extend it and let the method do the work for you

def do_if(var, cond)
yield var if cond[var]
end

do_if(Person.new, proc{|person| person.name == ‘John’}) do |person|
puts person
end

but IMO that’s not more efficient than allocating a temporal variable,
except maybe for the persistence of the variable. Perhaps it has
better uses.

On 5/26/07, Robert K. [email protected] wrote:

irb(main):008:0> (0…10).map {|i| it_test i}
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, “nothing”, “nothing”]

That is something I am surprised about, b/c the intention was to have
an idiom for
return it if h(x) < 10
and
return it_test(h(x))
really is not the same, right?

Cheers
Robert

I think something like this would be interesting, but “it” would be
easy to confuse with “if”, among other things, and it is still a very
special case.

Oftentimes I find myself doing assignments in conditionals, such as:

if foo
blahbalh
elsif obj = Foo.find_or_something # assignment intended!
obj.whatever
end

Going along the lines of a syntax already provided by rescue
(block-form):

if something

it would be silly to do the m(x) call before here as it might not be

needed
blahblahblah
elsif m(x) => foo
foo.whatever
end

…and extending to the single case which seems to be an extension of
the general:
puts foo if m(x) => foo

Then you run into issues of where foo should be scoped (as if does not
open up a new binding)

On 26.05.2007 23:37, Robert D. wrote:

=> 1
irb(main):008:0> (0…10).map {|i| it_test i}
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, “nothing”, “nothing”]

That is something I am surprised about, b/c the intention was to have
an idiom for
return it if h(x) < 10

No, it’s “return it if it < 10” with “it” being “v+1” in this case.

and
return it_test(h(x))
really is not the same, right?

It’s not the same but I believe you misread the task. it_test is just a
method to demonstrate the idiom in lines 2-4.

m(int->int): x -> x+1
h(int->bool): x -> x<10
g(int->int): x -> x

x: v

(see also Greg’s original posting)

Kind regards

robert

On 27.05.2007 11:46, Robert D. wrote:

On 5/27/07, Robert K. [email protected] wrote:

That is something I am surprised about, b/c the intention was to have
an idiom for
return it if h(x) < 10

No, it’s “return it if it < 10” with “it” being “v+1” in this case.
Well that was the original idiom but we are looking for existing ones,
right?

Yes, and that’s why I presented what I presented. I am not sure I
understand your point here. :slight_smile:

… potentially…
… maybe…
… hardly…

Um… It’s really not that difficult. I just used instance_eval
because that will rebind “self” inside the block but retain the original
binding afterwards. That way you get something like a “temporary
variable” but without needing to manually make sure the reference is
released after the test. Remeber that the problem with my first
suggestion was that you had to add tmp=nil after the line with return in
order to make sure the object would be released again.

It is really just the built in way of doing what is known in other
languages as “with”:

irb(main):001:0> def with(x,&b) x.instance_eval(&b) end
=> nil
irb(main):002:0> with “” do
irb(main):003:1* p length
irb(main):004:1> p empty?
irb(main):005:1> end
0
true
=> nil
irb(main):009:0> with “aaaaaaaaa” do
irb(main):010:1* scan(/a{1,3}/) {|x| p x}
irb(main):011:1> end
“aaa”
“aaa”
“aaa”
=> “aaaaaaaaa”
irb(main):012:0>

Hope that explains it. If not, let me know.

Kind regards

robert

On 5/27/07, Robert K. [email protected] wrote:

That is something I am surprised about, b/c the intention was to have
an idiom for
return it if h(x) < 10

No, it’s “return it if it < 10” with “it” being “v+1” in this case.
Well that was the original idiom but we are looking for existing ones,
right?

and
return it_test(h(x))
really is not the same, right?

It’s not the same but I believe you misread the task. it_test is just a
method to demonstrate the idiom in lines 2-4.

Ah that’s why you called the method test_…, not stupid at all :wink:
Well I will get to understand what is going on here…
… eventually…
… potentially…
… maybe…
… hardly…

Cheers
Robert

Kind regards

    robert

Cheers
Robert

On 5/27/07, Robert K. [email protected] wrote:

right?

Ah that’s why you called the method test_…, not stupid at all :wink:
Well I will get to understand what is going on here…
… eventually…
… potentially…
… maybe…
… hardly…

Um… It’s really not that difficult.
Sure it is, actually the code is simple, I still try to grasp what is
wanted here, you see?
Hope that explains it. If not, let me know.
Well see above but never mind :slight_smile:
Thx for your time, I shall probably say what I think about all of this:
(a)
return v if ((v = 42) % 2).zero?
would be nice if it worked
(b)
I am against fancy enhancements that are purely for optimization but see
above.

Cheers
Robert

On 27.05.2007 20:04, Robert D. wrote:

No, it’s “return it if it < 10” with “it” being “v+1” in this case.
It’s not the same but I believe you misread the task. it_test is
Um… It’s really not that difficult.
above.
We are completely on the same page with this.

Kind regards

robert

be bound to a+b+c (second innermost unnamed let binding).
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

Hi –

On Fri, 25 May 2007, 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?

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

Hi –

On Fri, 25 May 2007, Charles Oliver N. wrote:

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

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

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

be bound to a+b+c (second innermost unnamed let binding).
end
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.

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 May 28, 6:55 am, Robert K. [email protected] wrote:

(b)
I am against fancy enhancements that are purely for optimization but see
above.

We are completely on the same page with this.

I concur. From what I understand, this will eventually work:

return it if ((it = 42) % 2).zero?

And that’s quite enough, IMHO.

(The iterator var on the other hand…)

T.

On 29.05.2007 15:17, Gregory S. wrote:

On Tue, May 29, 2007 at 04:55:01PM +0900, Ronald F. wrote:
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 couldn’t agree more.

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)

3 is also about semantics which might even be more important than speed.
There can be a huge semantic difference between evaluating an
expression once or twice.

Other than that I totally agree with your analysis. This sums it up
very nicely!

let Time.now in
fprintf(stderr, “%d\n”, i);
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.

I kind of agree although I am not sure whether I’d take Lisp as
prototype. I believe it is a narrow path to make a language readable
and maintainable. The fact that Lisp’s macros blend in with the
language syntax is very nice on one hand because it allows to define
“new syntax” easily and make certain constructs look familiar. On the
other hand the missing syntactic distinction between macro and function
invocation makes it hard to understand what’s going on at times. Maybe
it’s just me lacking Lisp practice but I think it’s a double edged
sword.

Thank you for your analysis!

Kind regards

robert

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

Well, that article raises the issue that mutal local variables are
evil. I have sympathy with this statement, though I would like to
add that mutal global variable are evil as well, and your hint of
using functional let (which, BTW, was also the inspiration to my
usage of let blocks) just emphasizes that a bit more functional style
could make the world of programming less evil. But while I certainly
would cheer any new element of Functional Programming in Ruby, certainly
Ruby is not Haskell or Scheme. If we go into that direction, maybe we
should
start the discussion from a completely different end? How much
additional
FP would enhance Ruby?

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

Though I for sure can imagine examples, where implicit variables make it
hard to read, your example is, IMO, very easy to follow: You can clearly
see that the first $1 is bound to the time, and the second one is bound
to the sum. But I am well aware that the issue of how/to what extent
variables should be named, is a controversial one, and I wouldn’t enjoy
opening that bag of worms here.

Maybe I’m now bringing too much Perl-spirit to the Ruby discussion, but
IMO if a sufficiently large number of users would find some language
feature
useful (and I mean this in a general sense, since I don’t know how many
users the “it”/“let”/… stuff would really appreciate), readibility
issues
should be more on the users side (project management), not on the side
of the
language designers. Just my opinion, and I know well that quite a few
will
oppose me here…

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

Not if you count them from inside out, as I did it. Of course
you would have to renumber if you add an inner scope, such as
when changing

let foo() in
x1($1)
x2($1)
x3($1)
x4($1)
x5($1)

into

let foo() in do
x1($1)
x2($1)
let bar() in do
y0($1)
x3($2) # <— renumbering necessary
y1($1)
end
x4($1)
x5($1)
end

but as I said, I would use anonymous variables only in a very local
way,
not for a longer piece of code. In the example above, I would likely
have
assigned a name foo(), but perhaps used anonymous $1 for bar(). Sure,
code
is undergoing changes, and I can imagine cases where I would have to
renumber (or, more likely, replace my anonymous variable by a name later
on).
It’s just that I don’t feel this as painful, if it happens only here and
then.

Ronald