Introducing the "it" keyword

K

Keith Fahlgren

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
 
R

Robert Dober

This has also been pointed out as *not* working :)

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 :(<snip>
Robert
 
P

Paul Battley

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

Robert Klemme

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.

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
 
C

CHubas

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

Paul Stickney

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

Austin Ziegler

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

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
 
R

Robert Dober

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"]
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
 
R

Robert Dober

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


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 :)
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
 
R

Robert Klemme

Sure it is, actually the code is simple, I still try to grasp what is
wanted here, you see?
Well see above but never mind :)
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.

We are completely on the same page with this.

Kind regards

robert
 
R

Ronald Fischer

A more
=20
You're working too hard:
=20
def let(*args)
yield *args
end
=20
def use_let(a,b,c)
let a+b+c do |it|
return it if it < 10
end
end
=20
use_let(1,2,3) # 6
use_let(4,5,6) # nil
=20
No need for additional keywords or even global variables,=20
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=20
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=20
can have more than one binding, and where it is the programmer's choice
whether=20
or not to name the variables.

Ronald
--=20
Ronald Fischer <[email protected]>
Phone: +49-89-452133-162
=20
 
D

dblack

Hi --

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

dblack

Hi --

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


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

dblack

Hi --

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

Gregory Seidman

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 http://weblog.raganwald.com/2006/10/why-are-local-variables-bad.html
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.
--Greg
 
R

Ronald Fischer

The OP brought up an interesting issue, but what made it=20
interesting is not
the implicit binding but the need for a scoped temporary=20
value. There are
three important optimization goals to it:
=20
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)
=20
See=20
http://weblog.raganwald.com/2006/10/why-are-local-variables-bad.html
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?
Importantly, an implicit variable name has two problems.=20
First, it reduces
readability. Second, even if naming the variable explicitly=20
is optional
rather than required, you get the same sorts of maintenance=20
problems caused
by the optionally braceless block notation for single=20
statements in C-derived
syntaxes, e.g.
=20
let Time.now in
puts "starting at #{$1}"
let a+b+c in
puts "sum =3D #{$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=20
language designers. Just my opinion, and I know well that quite a few
will
oppose me here...
...is as bad an issue as...
=20
for (int i=3D0; i<5; ++i)
printf("%d\n", i);
fprintf(stderr, "%d\n", i);
=20
Basically, if you need to add an outer scope, you have to=20
renumber all of
the temporaries.=20

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=20
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
 
T

Trans

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

Robert Klemme

On Tue, May 29, 2007 at 04:55:01PM +0900, Ronald Fischer 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.
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!
See http://weblog.raganwald.com/2006/10/why-are-local-variables-bad.html
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.

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
 
M

Mariusz Pękala

--zYM0uCDKw75PZbzx
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

=20
I concur. From what I understand, this _will_ eventually work:
=20
return it if ((it =3D 42) % 2).zero?
=20

irb(main):005:0> def check; return it if ((it =3D 42) % 2).zero?; end
=3D> nil
irb(main):006:0> check
NameError: undefined local variable or method `it' for main:Object
from (irb):5:in `check'
from (irb):6

Or you mean it _will_ work in future ruby version?
Anyway, it really would be nice if such construction would work, but
it's not a big problem to express it as:

if (x =3D complex_calculation); return x; end

In my humble opinion, the 'problem' is not very important (forgive me my
limited vocabulary, I really don't want to offend anyone), since I
suppose that following code clearly/better shows the *intentions*: avoiding
performing complex calculation twice, and freeing the possibly large
object for GC.

x =3D complex_calculation
use(x) if x.is_ok?
x =3D nil

--=20
No virus found in this outgoing message.
Checked by 'grep -i virus $MESSAGE'
Trust me.

--zYM0uCDKw75PZbzx
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7-ecc0.1.6 (GNU/Linux)

iD8DBQFGXTlssnU0scoWZKARAsNMAJ4vNIKFiVzfBEwQDbouIZ4/n+eo1ACgstDb
UwMCLlUGkyXa6s4ooIfJw1M=
=5ten
-----END PGP SIGNATURE-----

--zYM0uCDKw75PZbzx--
 
C

Charles Oliver Nutter

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

No argument from me :)

I still think "it" is a terrible keyword to introduce though.

- Charlie
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top