Can't change the value of self


#1

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


#2

Jonathan L. 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


#3

James B. wrote:

Jonathan L. 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


#4

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

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


#5

On 1/1/06, Hal F. removed_email_address@domain.invalid wrote:

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?

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?


#6

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

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

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

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


#7

Wilson B. wrote:

…right? Too bad.

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

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

Devin


#8

Wilson B. removed_email_address@domain.invalid writes:

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


#9

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

Jon


#10

On Mon, 2 Jan 2006 09:37:45 +0900, Jonathan L.
removed_email_address@domain.invalid
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.


#11

Jonathan L. 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


#12

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


#13

Henrik M. wrote:

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


#14

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

Time objects are immutable.

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

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: