Adding an Increment Operator?

As Logan pointed out in a different thread:

% cat increment.rb
class Fixnum
  def increment
    self = self + 1
  end
end

a = 1
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.

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.

As Logan pointed out in a different thread:

% cat increment.rb
class Fixnum
  def increment
    self = self + 1
  end
end

a = 1
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 = <anything>
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 Wright

···

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

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

require 'delegate'

class Fixbox < DelegateClass(Fixnum)
  def self.(num); 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:

···

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

As Logan pointed out in a different thread:

> % cat increment.rb
> class Fixnum
> def increment
> self = self + 1
> end
> end
>
> a = 1
> 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.

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk

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

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.

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

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

···

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

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

--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk

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.

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.

···

On Friday 02 June 2006 4:02 pm, Dave Howell 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."

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

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.

···

On Jun 2, 2006, at 6:02 PM, Dave Howell 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."