Почему метод назначения верхнего уровня Ruby не может назначить переменные экземпляра в REPL?

Сеттер работает внутри класса; Сбои в REPL Top-Level

В связанном вопросе я пытался понять, почему метод присваивания возвращал неожиданное значение, и узнал, что это удивительный, но задокументированный крайний случай в Ruby. Однако, когда я пытался отладить проблему, я пошел дальше по кроличьей норе и столкнулся с некоторыми дополнительными сюрпризами, которые я не могу объяснить.

Сеттер внутри класса

Когда у меня есть метод установки внутри класса, такой как:

class Setter
  def foo=(bar)
    @foo = Integer(bar).succ
  end
end

затем я получаю задокументированную странность с возвращаемыми значениями из метода setter, но переменная экземпляра по-прежнему установлена ​​правильно. Например:

s = Setter.new
s.foo = 1
#=> 1

s.instance_variable_get :@foo
#=> 2

Сеттер в объекте верхнего уровня REPL

Однако в REPL (например, Pry или IRB) переменная экземпляра фактически никогда не устанавливается, хотя я понимаю, что переменные экземпляра должны храниться в верхнем уровне "main" объекта:

self.name
#=> NoMethodError: undefined method `name' for main:Object

# This is expected to set the @foo instance variable for main.
def foo= int
  @foo = int
end

foo = 1

@foo
#=> nil

instance_variable_get :@foo
#=> nil

TOPLEVEL_BINDING.eval('self').instance_variables
#=> []

И все же объект верхнего уровня хранит переменные экземпляра! Например:

@bar = 1 + 1; @bar
#=> 2

instance_variable_get :@bar
#=> 2

Вопрос, пересчитанный

Учитывая, что REPL хранит переменные экземпляра, почему метод присваивания класса работает, когда метод присвоения верхнего уровня не выполняется? Я ожидаю, что оба будут функционировать одинаково.

1 ответ

Решение

Оператор присваивания Руби = создаст локальную переменную, если вы явно не выписываете получателя. В твоем случае:

foo = 1

создает локальную переменную foo, а не вызывает метод foo=, Вам придется использовать

self.foo = 1

На самом деле вызвать метод, который вы определили выше. Теперь это установит @foo:

def foo= i # define foo= on self
  @foo = i
end
#=> :foo=

foo = 3
#=> 3

@foo
#=> nil

foo # here's the new local variable
#=> 3

instance_variables
#=> [:@prompt]

instance_variable_get :@foo 
#=> nil

self.foo = 4 # now calling the foo= method
#=> 4

foo # local foo is still 3
#=> 3

@foo # now the ivar is set
#=> 4

В вашем примере класса у вас есть явный получатель с s.foo = 1, Руби знает, что ты звонишь foo= установить на s, Документация по методам назначения гласит:

При использовании метода назначения у вас всегда должен быть получатель. Если у вас нет получателя, Ruby предполагает, что вы присваиваете локальную переменную [.]

Другие вопросы по тегам