Can't change the value of self

This code:

···

--------------
class Time
  
  # Rounds a time sensibly.
  # Currently just hour is supported (as that's the only one needed)
  def round_to(unit = :hour)
    unless min == 0
      new_hour = hour
      new_hour += 1 if min > 30
      time = Time.local(year, month, day, new_hour)
    end
    time
  end
  
  def round_to!(*args)
    self = round_to *args
  end
  
end
--------------

Throws this error:

Can't change the value of self (SyntaxError)
    self = round_to *args

Why doesn't it work? What can I do instead?

Thanks

--
Jonathan Leighton
http://turnipspatch.com/ | http://jonathanleighton.com/ | http://digital-proof.org/

Jonathan Leighton wrote:

Can't change the value of self (SyntaxError)
    self = round_to *args

Why doesn't it work? What can I do instead?

"Destructive" methods must alter self using that self's existing destructive methods.

James

···

--

http://www.ruby-doc.org - Ruby Help & Documentation
http://www.artima.com/rubycs/ - Ruby Code & Style: Writers wanted
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
http://www.30secondrule.com - Building Better Tools

Instead of

  a.round_to!(:hour)

just write

  a = a.round_to(:hour)

Or maybe a different name would help:

  a = a.closest(:hour)

Gary Wright

···

On Jan 1, 2006, at 7:37 PM, Jonathan Leighton wrote:

Can't change the value of self (SyntaxError)
    self = round_to *args

Why doesn't it work? What can I do instead?

One way of thinking about reassigning self is if you imagine in a numeric class
saying

def double
  self *= 2
end

That would amount to saying, if we did 1.double,
  1 becomes 2
which would mean that 1+1 would thereafter be 4, and that would be bad.

What we need is not the value 1 to become 2, but to get the value 2 back when we
say 1.double. So we say

def double
  return self*2
end

or, since "return" is implied

def double
self*2
end

In general in OO, an object can change its contents, but it cannot change its
"self". What should happen in this program?

one = 1
another_one = 1
assert_equal(2, one+one)
assert_equal(2, another_one+another_one)
one.double
assert_equal(4, one+one)
assert_equal(???,another_one+another_one)
assert_equal(???, 1+1)

When we double one, if the self*=2 thing worked, the ??? asserts would also find
4. We might be able to understand another_one being 2, but to have all
occurrences of 1+1 in the program turn into 4 ... that would be bad.

Certain objects are called "value objects", and the idea is that like numbers,
they never change value. For many objects it is a design choice whether or not
to make them value objects, even if they are quite complex. For example, in the
articles I'm currently writing on extended set theory on my site, I'm moving in
the direction of making my sets value objects. They are essentially arbitrarily
large collections of records (like relational database relations), and most DB
apps think of relations as mutable. I'm going to make a different decision,
namely that any set, once created, is a constant. It'll be interesting to see
what happens.

···

On Mon, 2 Jan 2006 09:37:45 +0900, Jonathan Leighton <lists@turnipspatch.com> wrote:

Can't change the value of self (SyntaxError)
   self = round_to *args

Why doesn't it work? What can I do instead?

--
Ron Jeffries
www.XProgramming.com
I'm giving the best advice I have. You get to decide if it's true for you.

James Britt wrote:

Jonathan Leighton wrote:

Can't change the value of self (SyntaxError)
    self = round_to *args

Why doesn't it work? What can I do instead?

"Destructive" methods must alter self using that self's existing destructive methods.

To which we should add: Time has no destructive methods, since
Time objects are immutable.

But someone once created a MutableTime class -- anyone recall
who/when/where and whether it's complete?

Hal

Thanks very much for this explanation, the restriction makes a lot more
sense to me now.

Jon

···

On Mon, 2006-01-02 at 19:32 +0900, Ron Jeffries wrote:

On Mon, 2 Jan 2006 09:37:45 +0900, Jonathan Leighton <lists@turnipspatch.com> > wrote:

>Can't change the value of self (SyntaxError)
> self = round_to *args
>
>Why doesn't it work? What can I do instead?

One way of thinking about reassigning self is if you imagine in a numeric class
saying

def double
  self *= 2
end

That would amount to saying, if we did 1.double,
  1 becomes 2
which would mean that 1+1 would thereafter be 4, and that would be bad.

What we need is not the value 1 to become 2, but to get the value 2 back when we
say 1.double. So we say

def double
  return self*2
end

or, since "return" is implied

def double
self*2
end

In general in OO, an object can change its contents, but it cannot change its
"self". What should happen in this program?

one = 1
another_one = 1
assert_equal(2, one+one)
assert_equal(2, another_one+another_one)
one.double
assert_equal(4, one+one)
assert_equal(???,another_one+another_one)
assert_equal(???, 1+1)

When we double one, if the self*=2 thing worked, the ??? asserts would also find
4. We might be able to understand another_one being 2, but to have all
occurrences of 1+1 in the program turn into 4 ... that would be bad.

Certain objects are called "value objects", and the idea is that like numbers,
they never change value. For many objects it is a design choice whether or not
to make them value objects, even if they are quite complex. For example, in the
articles I'm currently writing on extended set theory on my site, I'm moving in
the direction of making my sets value objects. They are essentially arbitrarily
large collections of records (like relational database relations), and most DB
apps think of relations as mutable. I'm going to make a different decision,
namely that any set, once created, is a constant. It'll be interesting to see
what happens.

--
Jonathan Leighton
http://turnipspatch.com/ | http://jonathanleighton.com/ | http://digital-proof.org/

Thanks for the replies. Why is Time immutable (presumably it's
deliberately designed like that)? The MutableTime class sounds
interesting but it's probably overkill for what I wanted to do -- I'll
just modify variables at a higher level.

Thanks

···

On Mon, 2006-01-02 at 09:47 +0900, Hal Fulton wrote:

James Britt wrote:
> Jonathan Leighton wrote:
>
>> Can't change the value of self (SyntaxError)
>> self = round_to *args
>>
>> Why doesn't it work? What can I do instead?
>
>
> "Destructive" methods must alter self using that self's existing
> destructive methods.

To which we should add: Time has no destructive methods, since
Time objects are immutable.

But someone once created a MutableTime class -- anyone recall
who/when/where and whether it's complete?

--
Jonathan Leighton
http://turnipspatch.com/ | http://jonathanleighton.com/ | http://digital-proof.org/

This is why I can't do:
class Fixnum
  def double!
    self *= 2
  end
end
..right? Too bad.

Speaking of methods.. is there a way to do:
class Something
  def <<(a, b)
    # do something with a and b
  end
end

s = Something.new
s << 'a', 'b'
results in a syntax error.

Is that another of those YACC limitations?

···

On 1/1/06, Hal Fulton <hal9000@hypermetrics.com> wrote:

James Britt wrote:
> Jonathan Leighton wrote:
>
>> Can't change the value of self (SyntaxError)
>> self = round_to *args
>>
>> Why doesn't it work? What can I do instead?
>
>
> "Destructive" methods must alter self using that self's existing
> destructive methods.

To which we should add: Time has no destructive methods, since
Time objects are immutable.

But someone once created a MutableTime class -- anyone recall
who/when/where and whether it's complete?

Jonathan Leighton wrote:

Thanks very much for this explanation, the restriction makes a lot more
sense to me now.

You should also know that more complex objects (as you know) can
often change their state. In particular, Array and String both
implement a replace method, which totally replaces their contents.
But even then, the object id stays the same.

Hal

That'd be me, and it's documented at http://phrogz.net/RubyLibs/rdoc/classes/MutableTime.html and available for download at http://phrogz.net/RubyLibs/MutableTime.rb

As to whether it's 'complete'...I dunno. I nominally labeled it v1.0.5, but who knows what that means :slight_smile:

···

On Jan 1, 2006, at 5:47 PM, Hal Fulton wrote:

James Britt wrote:

Jonathan Leighton wrote:

Can't change the value of self (SyntaxError)
    self = round_to *args

Why doesn't it work? What can I do instead?

"Destructive" methods must alter self using that self's existing destructive methods.

To which we should add: Time has no destructive methods, since
Time objects are immutable.

But someone once created a MutableTime class -- anyone recall
who/when/where and whether it's complete?

Wilson Bilkovich wrote:

"Destructive" methods must alter self using that self's existing
destructive methods.
     

This is why I can't do:
class Fixnum
def double!
   self *= 2
end
end
..right? Too bad.

Well, that, and what would "2.double!" do?

Speaking of methods.. is there a way to do:
class Something
def <<(a, b)
   # do something with a and b
end
end

s = Something.new
s << 'a', 'b'
results in a syntax error.

My guess is you're out of luck, there. But you could change it to take s << ['a','b'] or s << 'a' << 'b'.

Devin

Wilson Bilkovich <wilsonb@gmail.com> writes:

Speaking of methods.. is there a way to do:
class Something
  def <<(a, b)
    # do something with a and b
  end
end

s = Something.new
s << 'a', 'b'
results in a syntax error.

You could do s.<<('a', 'b'), but it seems unrubyish. Perhaps you
could settle for:

  s << ['a', 'b']

Is that another of those YACC limitations?

Hmmm, I don't know if it's a limitation, but it could get confusing.
Consider:

  four, three = 1 << 2, 3
  arr = [x << 1, x << 2]

And if you do it for `<<', then what about `+', `-', `%'? If you do
do it for those, then you start to lose really basic stuff like:

  arr = [a + 1, b + 1]
  b, a = a % b, b until b == 0

Immutable classes are less prone to bugs than mutable classes. This is
because they have a single state. They are also thread safe.

It is good practise to favor immutability when there are no compelling
reasons for making a class mutable.

A Time object represents an instance in time. Making it mutable could
easily get confusing. Consider:

time = MutableTime.at(946702800)
rocket.launch_time = time
...
time.change(946701565) # Changes the launch time of the rocket

The launch time of the rocket can be changed without setting the
launch_time property explicitly. This is a potential source of bugs.

/Henrik

···

On Mon, 2006-01-02 at 02:11, Jonathan Leighton wrote:

On Mon, 2006-01-02 at 09:47 +0900, Hal Fulton wrote:
> James Britt wrote:
> > Jonathan Leighton wrote:
> >
> >> Can't change the value of self (SyntaxError)
> >> self = round_to *args
> >>
> >> Why doesn't it work? What can I do instead?
> >
> >
> > "Destructive" methods must alter self using that self's existing
> > destructive methods.
>
> To which we should add: Time has no destructive methods, since
> Time objects are immutable.
>
> But someone once created a MutableTime class -- anyone recall
> who/when/where and whether it's complete?

Thanks for the replies. Why is Time immutable (presumably it's
deliberately designed like that)? The MutableTime class sounds
interesting but it's probably overkill for what I wanted to do -- I'll
just modify variables at a higher level.

Thanks

Henrik Martensson wrote:

Immutable classes are less prone to bugs than mutable classes. This is
because they have a single state. They are also thread safe.

It is good practise to favor immutability when there are no compelling
reasons for making a class mutable.

A Time object represents an instance in time. Making it mutable could
easily get confusing. Consider:

time = MutableTime.at(946702800)
rocket.launch_time = time
...
time.change(946701565) # Changes the launch time of the rocket

The launch time of the rocket can be changed without setting the
launch_time property explicitly. This is a potential source of bugs.

Thanks, Henrik. That's a great discussion and a great example.

Hal