Adding an Increment Operator?


#1

As Logan pointed out in a different thread:

% ruby increment.rb
-:13: Can’t change the value of self
self = self + 1
^

So, how WOULD I create an .increment operator? I get why I can’t put it
in class Fixnum (well, I think I know why); Fixnums are symbol-ish-ly
unalterable. 5.increment should fail, for sanity’s sake.

So I have to add .increment to class . . . hmm. I can’t add it to
variables; they’re just references. But I want my_var.increment to
change ‘my_var’ so that it references the next integer in line.

Strings have methods that alter strings in place, but a String object
is a composite object, made up of characters, and !-type methods alter
the characters of the string without altering the variable’s reference.
Numbers aren’t composite objects. Maybe it can’t be done? But people
are constantly amazing me with crazy magical Ruby tricks; it seems so
improbable that there isn’t a way to do this as well . . .

Somebody else pointed out I could just my_var += 1. Yes, but to me,
that implies that the “1” is deliberate, and at some other point I
might add 2 or 5 or something, whereas using “inc” or “++” or whatever
says “it really doesn’t matter if I’m counting by ones, tens, Roman
numerals, or letters. This variable is just stepping upwards.”

But the main reason I’m asking is not to slightly improve the esthetics
of a couple lines of code. I’m certain that I’m going to learn
something interesting from the answer to the question.


#2

On Jun 1, 2006, at 7:15 PM, Dave H. wrote:

a.increment

% ruby increment.rb
-:13: Can’t change the value of self
self = self + 1
^

So, how WOULD I create an .increment operator? I get why I can’t
put it in class Fixnum (well, I think I know why); Fixnums are
symbol-ish-ly unalterable. 5.increment should fail, for sanity’s sake.

Fixnum objects aren’t containers for integer values. Each Fixnum
object represents a different integer via a one-to-one mapping. So
just as you can’t increment the integer 1 you can’t increment the
Fixnum that represents the integer one.

The concept of ‘the next integer’ works though and that is available
via Fixnum#succ

Ruby doesn’t allow you to use assignment to bind self to a new object
either so
self =
is a syntax error.

So I have to add .increment to class . . . hmm. I can’t add it to
variables; they’re just references. But I want my_var.increment to
change ‘my_var’ so that it references the next integer in line.

myvar = myvar.succ

Gary W.


#3

On Fri, 2006-06-02 at 08:15 +0900, Dave H. wrote:

a.increment

% ruby increment.rb
-:13: Can’t change the value of self
self = self + 1
^

So, how WOULD I create an .increment operator? I get why I can’t put it
in class Fixnum (well, I think I know why); Fixnums are symbol-ish-ly
unalterable. 5.increment should fail, for sanity’s sake.

Depending on what you want to do, maybe you could cheat:

require ‘delegate’

class Fixbox < DelegateClass(Fixnum)
def self.; new(num); end
def decrement; getobj - 1; end
def decrement!; setobj(decrement); end
def increment; getobj + 1; end
def increment!; setobj(increment); end
end

f = Fixbox[3]

=> 3

f.increment!

=> 4

f

=> 4

f.decrement!

=> 3

f

=> 3

:slight_smile:


#4

On Fri, 2006-06-02 at 09:34 +0900, Dave B. wrote:

Dave H. wrote:

So, how WOULD I create an .increment operator? I get why I can’t put it
in class Fixnum (well, I think I know why); Fixnums are symbol-ish-ly
unalterable. 5.increment should fail, for sanity’s sake.

I wasn’t going to mention the kind of generic delegating container Ross
just wrote about; have a look, but I don’t think you’ll find a place to
use that where there isn’t a better solution.

I have to admit to being at most semi-serious with that suggestion :).


#5

Dave H. wrote:

characters of the string without altering the variable’s reference.
But the main reason I’m asking is not to slightly improve the esthetics
of a couple lines of code. I’m certain that I’m going to learn something
interesting from the answer to the question.

As has been pointed out, you can’t modify a Fixnum, nor can you ever
assign to self.

I wasn’t going to mention the kind of generic delegating container Ross
just wrote about; have a look, but I don’t think you’ll find a place to
use that where there isn’t a better solution.

Another approach might be to encapsulate a counter like so:

class Counter
attr_accessor :value
def initialize(i = 0)
@value = i
end
def inc
@value = @value.succ
end
end

i = Counter.new
i.value #=> 0
i.inc #=> 1
i.inc #=> 2
i.value = “X”
i.inc #=> “Y”

Or you can do like this:

def counter(i = 0)
proc { i = i.succ }
end

i = counter
i.call #=> 1
i.call #=> 2
i[] #=> 3

Cheers,
Dave


#6

So, how WOULD I create an .increment operator? … 5.increment should
fail, for sanity’s sake.

Gary’s solution of
myvar = myvar.succ
is a slight improvement over
myvar = myvar + 1
but it’s still more verbose than I’d hoped to have.

I don’t even understand Ross’s somewhat tongue-in-cheek DelegateClass.
Goodness.

Creating a whole new Counter class, OTOH, seems like an excellent
alternative. Since I’d indicated that the reason I wanted .increment in
the first place was to make it quite clear that the actual value of the
thing being incremented wasn’t necessarily relevant, having an object
that does NOT like to be divided, .times{}'ed, or otherwise cooperate
with more normal ‘integer’ like behavior makes sense. (Yes, Dave’s
Counter, as written, would do those things, but it doesn’t have to be
so…)

Although I will admit to being rather surprised that the answer to the
question
How do I do this?
whatever = 1
whatever.increment
p whatever
=> 2
is apparently “You Can’t.”

Interesting.


#7

On Friday 02 June 2006 4:02 pm, Dave H. wrote:

Although I will admit to being rather surprised that the answer to the
question
How do I do this?
whatever = 1
whatever.increment
p whatever
=> 2
is apparently “You Can’t.”

Why is it surprising?

And you almost can, sort of, by relying on the syntactic similarity
between a
method call and a variable reference and using a simple counter object.

self.whatever = 1
whatever.increment
p whatever
=> 2

That, you can do.


class IncObj
def initialize(val)
@me = @val.to_i
end

    def to_s
            @me.to_s
    end

    def to_i
            @me
    end

    def increment
            @me += 1
    end

end

def whatever; @whatever; end

def whatever=(val); @whatever = IncObj.new(val); end

self.whatever = 1
whatever.increment
p whatever

Kirk H.


#8

On Jun 2, 2006, at 6:02 PM, Dave H. wrote:

Although I will admit to being rather surprised that the answer to
the question
How do I do this?
whatever = 1
whatever.increment
p whatever
=> 2
is apparently “You Can’t.”

If you are surprised by this then you haven’t fully absorbed Ruby’s
object model and/or assignment semantics.

I know that assignments such as ‘whatever = 1’ are often described as
copying the value 1 into the variable ‘whatever’ but I find that
language highly misleading for Ruby. Not everyone thinks about it
the way I do but read on if you are curious.

Variables in Ruby are not containers for objects (or for values). If
they can be said to ‘hold’ anything, then they hold references to
objects (not the object itself). Similarly, integer literals do not
represent values or objects but instead are labels bound
(permanently) to particular references to instances of Fixnum (I’m
ignoring BigNums).

4

is a constant reference to the Fixnum instance that behaves like the
number 4. There is exactly one Fixnum instance that behaves that way.
There are many ways to identify that object (4, 0b100, 0x4, 0x04) but
all those labels are references to the same Fixnum instance.

Assignment in Ruby is just the re-binding of a label from one object
reference to another object reference.

a = b

causes ‘a’ to be bound to the same object reference that is
associated with ‘b’.

a = 5

causes ‘a’ to be associated with the same object reference that is
associated with 5, i.e. the reference (there can be only one)
to the Fixnum object that behaves like the integer 5. If you want an
expression like:

a.increment

to cause ‘a’ to be associated with the Fixnum instance 6 then you are
talking about rebinding the variable ‘a’ so that it no longer is
bound to the reference to the Fixnum 5. But the only way to rebind a
variable is via assignment, so you are back to something like

a = a + 1
or
a = a.succ

Internally Ruby doesn’t actually allocate memory for instances of
Fixnum. The internal representation of object references is
structured so that Ruby can fake the existence of Fixnum instances
(and other objects such as nil, true, and false). Ruby manipulates
the references, the objects themselves are never explicitly realized.