How does Ruby handle assignment semantically?
Asked Answered
M

1

13

In Ruby, we assign values to objects with the = operator.

Combine this with implicit typing and we frequently get situations like this:

myVar= :asymbol

The above line both creates a new symbol object, and binds the object to the variable name myVar.

Semantically, how is this done?

I have had it hammered into my head that the = operator is not magic syntax built into the interpreter, but is actually just syntactic sugar for the object.=(value) method.

With this in mind, my best guess is that when the interpreter sees we are trying to assign a value to an undefined variable name, it first creates a new object of some special type, like undefined or null or something, and then passes the := message to that object with the payload being the value we are trying to assign.

However, calling .class on an un-instantiated object just throws an exception because Ruby thinks we're trying to call a method (whose name is the name of the variable that you're trying to bring into existence) on self

> obj.class
    > NameError: undefined variable or method 'obj' for main:Object

So, as far as I can tell, I have no way of figuring this out experimentally.


Side note:

In the case of symbol assignment, I believe that the value assigned ( A.K.A. the value returned by the instantiated object's object_id method, A.K.A. the value of the unsigned long VALUE variable on the C level) is a number that represents an offset in a table somewhere (I believe this is how Ruby achieves 'immediate value' for symbol objects).

In other cases, the value may be a direct encoding of the object itself, or a value that is meant to be cast to a pointer in reference to a struct.

Regardless, the way that Ruby represents the object and whether we end up assigning a reference or the object itself is not what I am asking about here.

Additional question:

What class is the = method inherited from? I can't find it in the spec for Object or BasicObject.

Maishamaisie answered 20/4, 2015 at 19:19 Comment(3)
Like "There is no spoon", there is no = method.Marras
Is this a duplicate of this question? https://mcmap.net/q/907103/-object-assignment-in-ruby-closedLoose
@GeorgeStocker The referenced question is about the effect of the = operator, where as mine is about its implementation in Ruby.Maishamaisie
M
13

Variables are, in a technical sense, just pointers to objects. There's nothing remarkable about that, but a simple variable assignment to an existing object does not involve any method calls or messages being sent.

Remember variables are just there so that programmers can refer to objects by name instead of by some kind of internal identifier or memory location. So there's a bit of "magic" here, = is special when making an assignment as there's rules for what you can do on the left and right side of it.

The only way you can send messages to something, that is make method calls, is if you've defined it in a way the compiler understands. x = 1 is sufficient, it means x refers to the Fixnum in question.

Note that the Ruby interpreter will need to determine if x refers to a variable or method call, as x= may be a method that's defined on the object context in which this is evaluated.

For example:

class Example
  def x=(value)
    @x = value
  end

  def test
    # Equivalent to send(:x=, 1) because x= is a method
    x = 1

    # Is a variable definition because y= is not a method
    y = 2

    # Is always a method call because self is referenced.
    self.x = 3
  end
end

# Is a variable definition because x= is not defined in this context
x = 4

If there's no x= method for your object, x is automatically presumed to be a variable.

You can't have a := message because that would imply you can replace one object with another, something that's not allowed. Once an object is created, it cannot magically change type. For that you need to create a new instance of a different object. Variables only appear to change types, but in fact, they just end up pointing to different objects.

So in short, there's no := method call, but there may be special methods like :x= that work in very specific cases.

Marras answered 20/4, 2015 at 19:42 Comment(5)
Another thing: value = "newval" is always presumed to be a variable (rather than a method) unless there's an explicit receiver. So if your context has defined :value=, then you have to explicitly call self.value = arg to access that method.Gunlock
@Gunlock This becomes an issue when the method is introduced after the code is compiled. Good point.Marras
@CarySwoveland All good. Not uncommon for a programmer to talk to themselves to work through a problem, though!Marras
So, then there is no way to instantiate an object without using Class.new() or assigning it a value with the '=' syntax, correct?Maishamaisie
Objects are created implicitly all the time: x(y: z) creates at least one, possibly more, as at the very least a hash { :y => z } is created and :y may be instantiated if it doesn't already exist. I don't think there's any hard and fast rules for what might create an object. They're like molecules of water in the Ruby world, you live in a sea of them. Remember even numbers are objects, albeit extremely light-weight ones, so even x = 1 + 2 potentially creates three objects, 1, 2 and 3.Marras

© 2022 - 2024 — McMap. All rights reserved.