Ruby - Смешивание именованных и позиционных параметров, почему порядок имеет значение? - PullRequest
0 голосов
/ 28 декабря 2018

Создание функции с именованными и позиционными параметрами

class Foo
  def initialize(bar:, bang:, bamph, &block)
    # ...
  end
end

Создает синтаксическую ошибку:

$ ruby -c scratch.rb
scratch.rb:2: syntax error, unexpected tIDENTIFIER
...f initialize(bar:, bang:, bamph, &block)
...                          ^~~~~

В этом случае

 def initialize(bamph, bar:, bang:,  &block)
    # ...
 end

нет.

Насколько я вижу, этот ответ объясняет, что порядок типов параметров должен соответствовать определенному шаблону.Но какую выгоду приносит соблюдение этой иерархии?

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

Параметры и аргументы ключевых слов являются относительно новыми в Ruby.Они были введены только в Ruby 2.0.

До того, как у Ruby были параметры и аргументы ключевых слов, широко использовалась идиома передачи литерала Hash в качестве последнего аргумента метода.Эта идиома выглядела примерно так:

DEFAULTS = {
  :mode => 'w',
  :eol => :crlf,
}

def open_file(name, options = {})
  raise ArgumentError unless options[:encoding]
  options = DEFAULTS.merge(option)
  mode, eol, encoding = options[:mode], options[:eol], options[:encoding]
  # do your thing
end

open_file('test.txt', { :mode => 'r', :encoding => 'UTF-8' })

Для того, чтобы она выглядела немного более похожей на ключевое слово, вам разрешается опускать скобки, если вы передаете литерал Hash как оченьпоследний аргумент отправки сообщения:

open_file('test.txt', :mode => 'r', :encoding => 'UTF-8')

В Ruby 1.9 был введен альтернативный синтаксис для ограниченного подмножества литералов Hash: когда ключом является Symbol, который также является допустимым идентификатором Ruby(например, :foo, но не :'foo-bar'), тогда вы можете написать это так:

{ foo: bar }

вместо

{ :foo => bar }

Итак, мы могли бы вызвать наш метод сверхукак это:

open_file('test.txt', { mode: 'r', encoding: 'UTF-8' })

и, поскольку правило об исключении скобок все еще применяется, также вот так:

open_file('test.txt', mode: 'r', encoding: 'UTF-8')

Это очень похоже на аргументы ключевых слов в других языках.Фактически, этот альтернативный буквальный синтаксис для Hash es с ключами Symbol был по крайней мере частично специально разработан для обеспечения пути перехода для введения параметров и аргументов ключевых слов в Ruby.

В Ruby 2.0 необязательные параметры ключевого словас ключевыми словами по умолчанию были введены аргументы:

def open_file(name, mode: 'w', eol: :crlf, encoding: nil)
  raise ArgumentError unless encoding
  # do your thing
end

Затем в Ruby 2.1 обязательные параметры и аргументы ключевых слов:

def open_file(name, mode: 'w', eol: :crlf, encoding:)
  # do your thing
end

Как вы, вероятно, знаете, вызов этого метода выглядит точно так же, как и раньше:

open_file('test.txt', mode: 'r', encoding: 'UTF-8')

Обратите внимание, что вы больше не можете сказать, что это значит!Вы не можете знать, является ли mode: 'r', encoding: 'UTF-8' литералом Hash или двумя ключевыми аргументами (другими словами, вы даже не знаете, является ли это одним или двумя аргументами!), Не глядя на определение метода, который вы вызываете.

Было решено, что Ruby 2.0 должен быть максимально обратно и вперед совместим с Ruby 1.9.

Следовательно, все следующее должно быть верным:

  • Метод, которыйопределяется с помощью хэша опций и вызывается с хэшем опций, он все еще должен работать.
  • Метод, который определяется с помощью хэша опций и вызывается с аргументами ключевого слова, должен работать.
  • Методэто определяется параметрами ключевого слова и вызывается с помощью хеш-литерала параметров, который все еще должен работать.

Чтобы все это работало, существует много неявных преобразований между литералами хеш-функции и аргументами ключевого слова.Чтобы заставить это работать без неприятных угловых случаев, просто намного проще, если параметры ключевого слова и аргументы ключевого слова допускаются только там, где ранее был разрешен синтаксис "поддельного" ключевого слова, то есть в самом конце списка параметров исписок аргументов.

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

Теперь, просто представьте, как это было бы, если бы правила были еще менее строгими!


Вот несколько примеров упомянутых выше проблем:

0 голосов
/ 28 декабря 2018

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

Рассмотрим метод, подобный этому:

def foo(num, hash, string); end

Это допустимый вызов:

foo(1, {a: :b}, "bar")

Это не так, потому что парсер не знает, запятая лиотделяет значения хеш-ключа или аргументы для метода:

foo(1, a: b, "bar")

Конечно, метод будет более гибким, если вместо этого вы добавите хэш в качестве последнего аргумента, потому что тогда у вас будет возможность опустить фигурные скобки вокругхэш.

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

Было бы замечательно, если бы у нас была более полнофункциональная деструктуризация, как в ES6 Javascript, но, увы, мы этого не делаем (опять же, насколько сложной задачей было бы это реализовать,Я не могу вам сказать).У нас есть некая рудиментарная деструктуризация хеша в форме ключевых слов.И, вероятная причина, по которой ключевое слово params должно быть последним, заключается в том, чтобы обеспечить доступность синтаксического сахара, в котором мы опускаем хеш-скобки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...