Зачем сеттерам Ruby нужно «я». квалификация в классе?

Установщики Ruby - независимо от того, созданы они (c)attr_accessor или вручную - кажутся единственными методами, которым требуется квалификация self. при доступе внутри самого класса. Похоже, что это делает Ruby единственным в мире языков:

  • Всем методам нужен self / this (например, Perl и, я думаю, Javascript)
  • Никакие методы не требуют self / this is (C#, Java)
  • self / this (Ruby?) Нужен только сеттерам.

Лучшее сравнение - C# и Ruby, потому что оба языка поддерживают методы доступа, которые синтаксически работают так же, как переменные экземпляра класса: foo.x = y, y = foo.x. C# называет их свойствами.

Вот простой пример; та же программа на Ruby, затем на C#:

class A
  def qwerty; @q; end                   # manual getter
  def qwerty=(value); @q = value; end   # manual setter, but attr_accessor is same 
  def asdf; self.qwerty = 4; end        # "self." is necessary in ruby?
  def xxx; asdf; end                    # we can invoke nonsetters w/o "self."
  def dump; puts "qwerty = #{qwerty}"; end
end

a = A.new
a.xxx
a.dump

уберите self.qwerty =(), и он выйдет из строя (Ruby 1.8.6 в Linux и OS X). Теперь C#:

using System;

public class A {
  public A() {}
  int q;
  public int qwerty {
    get { return q; }
    set { q = value; }
  }
  public void asdf() { qwerty = 4; } // C# setters work w/o "this."
  public void xxx()  { asdf(); }     // are just like other methods
  public void dump() { Console.WriteLine("qwerty = {0}", qwerty); }
}

public class Test {
  public static void Main() {
    A a = new A();
    a.xxx();
    a.dump();
  }
}

Вопрос: Это правда? Существуют ли другие случаи, помимо сеттеров, когда требуется self? То есть, есть ли другие случаи, когда Ruby-метод не можешь может быть вызван без self?

Конечно, есть много случаев, когда требуется self становится. Это не уникально для Ruby, просто для ясности:

using System;

public class A {
  public A() {}
  public int test { get { return 4; }}
  public int useVariable() {
    int test = 5;
    return test;
  }
  public int useMethod() {
    int test = 5;
    return this.test;
  }
}

public class Test {
  public static void Main() {
    A a = new A();
    Console.WriteLine("{0}", a.useVariable()); // prints 5
    Console.WriteLine("{0}", a.useMethod());   // prints 4
  }
}

Такая же двусмысленность разрешается таким же образом. Но пока незаметно, я спрашиваю о случае, когда

  • Был определен метод имеет, и
  • Определена локальная переменная Нет, и

мы встречаемся

qwerty = 4

что неоднозначно - это вызов метода или назначение новой локальной переменной?


@Mike Stone

Здравствуй! Я понимаю и ценю высказанные вами замечания и ваши пример был отличным. Поверьте мне, когда я говорю, если бы у меня было достаточно репутации, Я проголосую за ваш ответ. Тем не менее, мы все еще не согласны:

  • по семантике и
  • по центральному факту

Сначала я утверждаю, не без иронии, у нас ведутся семантические дискуссии о значение слова «двусмысленность».

Что касается синтаксического анализа и семантики языков программирования (предмет этого вопроса), вы, конечно, допускаете широкий спектр понятия «двусмысленность». Давайте просто воспользуемся случайными обозначениями:

  1. неоднозначный: лексическая двусмысленность (lex должен 'смотреть вперед')
  2. Неоднозначный: грамматическая двусмысленность (yacc должен полагаться на анализ дерева синтаксического анализа)
  3. НЕПОСРЕДСТВЕННЫЙ: неоднозначность, зная все в момент исполнения

(и барахло между 2-3 тоже есть). Все эти категории разрешаются сбор более контекстной информации, поиск все более глобально. Итак, когда вы сказать,

"qwerty = 4" is UNAMBIGUOUS in C# when there is no variable defined...

Я не мог с этим согласиться. Но к тому же я говорю

"qwerty = 4" is un-Ambiguous in ruby (as it now exists)

"qwerty = 4" is Ambiguous in C#

И мы пока не противоречим друг другу. Наконец, вот где мы действительно не согласен: Ruby может быть или не может быть реализован без дальнейшего языковые конструкции такие, что,

For "qwerty = 4," ruby UNAMBIGUOUSLY invokes an existing setter if there
is no local variable defined

Вы говорите нет. Я говорю да; может существовать другой рубин, который ведет себя точно так же, как текущий во всех отношениях, Кроме "qwerty = 4" определяет новый переменная, когда нет сеттера и локального не существует, она вызывает сеттер, если один существует, и он назначается локальному, если он существует. Я полностью согласен с тем, что я мог ошибаться. На самом деле, было бы интересно узнать причину, по которой я мог ошибаться.

Позволь мне объяснить.

Представьте, что вы пишете новый объектно-ориентированный язык с методами доступа, которые выглядят как экземпляры vars (например, ruby ​​и C#). Вы бы, наверное, начали с концептуальные грамматики примерно такие:

  var = expr    // assignment
  method = expr // setter method invocation

Но парсер-компилятор (даже не среда выполнения) будет блевать, потому что даже после все входные данные проверены, нет никакого способа узнать, какая грамматика подходит. Вы столкнулись с классическим выбором. Я не могу быть уверен в деталях, но в основном рубин делает это:

  var = expr    // assignment (new or existing)
  // method = expr, disallow setter method invocation without .

вот почему это однозначно, в то время как C# делает это:

  symbol = expr // push 'symbol=' onto parse tree and decide later
                // if local variable is def'd somewhere in scope: assignment
                // else if a setter is def'd in scope: invocation

Для C# «позже» все еще находится во время компиляции.

Я уверен, что Ruby мог бы сделать то же самое, но «позже» должно было быть во время выполнения, потому что как указывает Бен, вы не знаете, пока оператор не будет выполнен, в каком случае применяется.

Мой вопрос никогда не означал «действительно ли мне нужно« я »?» или что потенциальная двусмысленность избегается? "Скорее я хотел знать, почему это конкретный выбор сделан? Может дело не в производительности. Может быть, он просто получил работу сделано, или было сочтено лучшим всегда позволять однострочному локальному переопределить метод (требование довольно редкого случая) ...

Но я вроде как предполагаю, что наиболее динамичным языком может быть тот, который откладывает это решение дольше всех и выбирает семантику на основе наиболее контекстных информация: поэтому, если у вас нет локального и вы определили сеттер, он будет использовать сеттер. Не вот почему нам нравятся ruby, smalltalk, objc, потому что вызов метода решается во время выполнения, предлагая максимальную выразительность?

PHP также требует $this-> при доступе к переменным экземпляров. Это все время сбивает меня с толку.

Chloe 02.01.2014 03:50

Явный получатель необходим только для методов класса, но не для методов экземпляра.

Todd A. Jacobs 02.10.2014 03:52

Согласен - мне тоже не нравится такой способ разрешения разногласий. Нарушает принцип наименьшего удивления.

Dogweather 18.01.2016 01:14

@Dogweather Matz поясняет, что он имеет в виду под наименьший сюрприз: У каждого есть индивидуальное прошлое. ... они могут быть удивлены разными аспектами языка. Затем они подходят ко мне и говорят: «Меня удивила эта особенность языка, поэтому Ruby нарушает принцип наименьшего удивления». Ждать. Ждать. Принцип наименьшего удивления подходит не только вам. Принцип наименьшего удивления означает принцип наименьшего удивления мой. И это означает принцип наименьшего удивления после того, как вы хорошо изучите Ruby.

Kelvin 19.07.2018 22:51
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
76
4
9 741
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Что ж, я думаю, причина этого в том, что qwerty = 4 неоднозначен - вы определяете новую переменную с именем qwerty или вызываете установщик? Ruby разрешает эту двусмысленность, говоря, что он создаст новую переменную, поэтому self. требуется.

Вот еще один случай, когда вам нужен self.:

class A
  def test
    4
  end
  def use_variable
    test = 5
    test
  end
  def use_method
    test = 5
    self.test
  end
end
a = A.new
a.use_variable # returns 5
a.use_method   # returns 4

Как видите, доступ к test неоднозначен, поэтому требуется self..

Кроме того, именно поэтому пример C# на самом деле не является хорошим сравнением, потому что вы определяете переменные таким образом, который однозначно зависит от использования установщика. Если вы определили переменную в C# с тем же именем, что и средство доступа, вам нужно будет квалифицировать вызовы средства доступа с помощью this., как и в случае с Ruby.

Ответ принят как подходящий

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

Это неверно, настоящая причина в том, что простое указание x=5 создает экземпляр локальной переменной x, которая переопределяет любой существующий установщик self.x=. Чтобы устранить эту двусмысленность, если вы хотите вызвать сеттер x=, вам нужно явно указать, что вы хотите сделать, указав self.x=

bbozo 03.05.2014 22:06

Потому что иначе вообще невозможно было бы установить локальные переменные внутри методов. variable = some_value неоднозначен. Например:

class ExampleClass
  attr_reader :last_set
  def method_missing(name, *args)
    if name.to_s =~ /=$/
      @last_set = args.first
    else
      super
    end
  end

  def some_method
    some_variable = 5 # Set a local variable? Or call method_missing?
    puts some_variable
  end
end

Если self не требуется для установщиков, some_method повысит NameError: undefined local variable or method 'some_variable'. Однако как есть, метод работает так, как задумано:

example = ExampleClass.new
example.blah = 'Some text'
example.last_set #=> "Some text"
example.some_method # prints "5"
example.last_set #=> "Some text"

Ха-ха. Я собирался проголосовать за этот ответ, но понял, что это был мой ответ год назад. ;-)

Ajedi32 30.01.2015 22:45

Я думаю, что это единственный ответ, который ясно указывает, почему вы никогда не сможете обойтись без self..

amoebe 14.12.2015 21:52

Я должен добавить к этому, причина, по которой Java и подобные ей сходит с рук, заключается в том, что в качестве статически типизированных языков компилятор может заранее определить, будет ли конфликт, и соответственно вызвать соответствующую ошибку. У динамических (языки сценариев), языков со слабой типизацией (php) и утиной типизации (ruby / python) такой роскоши, увы, нет.

Shayne 01.09.2016 06:28

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