Как работать с Ruby Duck Typing

Я изучаю Ruby, и у меня возникла серьезная концептуальная проблема, связанная с набором текста. Позвольте мне подробно объяснить, почему я не понимаю парадигмы.

Скажем, я использую цепочку методов для краткого кода, как вы это делаете в Ruby. Я должен точно знать, какой тип возвращаемого значения для каждого вызова метода в цепочке, иначе я не могу знать, какие методы доступны по следующей ссылке. Должен ли я каждый раз проверять документацию метода ?? Я выполняю эти постоянно повторяющиеся обучающие упражнения. Кажется, я застрял в процессе ссылки, вывода, запуска, сбоя, исправления, повторения, чтобы запустить код, вместо того, чтобы точно знать, с чем я работаю во время кодирования. Это противоречит обещанию Руби об интуиции.

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

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

Ruby and duck typing: разработка по контракту невозможна?

Обсуждения в предыдущем вопросе stackoverflow на самом деле не дают ответа ни на что, кроме «есть процессы, которым вы должны следовать», и эти процессы не кажутся стандартными, у всех свое мнение о том, какому процессу следовать, и язык имеет нулевое принуждение. Проверка метода? Дизайн, основанный на тестах? Документированный API? Строгие соглашения об именах методов? Что такое стандарт и кто его диктует? Что мне делать? Решат ли эти рекомендации эту проблему https://stackoverflow.com/questions/616037/ruby-coding-style-guidelines? Есть ли редакторы, которые могут помочь?

Концептуально я тоже не получаю преимущества. Вам нужно знать, какие методы необходимы для любого вызываемого метода, поэтому независимо от того, набираете ли вы, когда что-либо кодируете. Вы просто не информируете язык или кого-либо еще явно, если не решите задокументировать это. Тогда вы застряли, выполняя всю проверку типов во время выполнения, а не во время кодирования. Я занимался программированием на PHP и Python и там тоже этого не понимаю.

Что я упускаю или не понимаю? Пожалуйста, помогите мне понять эту парадигму.


person kamegami    schedule 03.10.2013    source источник
comment
Конечно, у утиного набора есть свои преимущества, но, конечно, есть и недостатки. Я могу передать любой объект методу foo(a), который вызывает a.bar, пока a.responds_to?(:bar). Это мило. Никаких интерфейсов, никаких дженериков, просто добавьте метод bar и вперед. Конечно, статическая типизация также имеет много преимуществ. Похоже, вам просто нужен некоторый опыт работы с Ruby, чтобы оценить и то, и другое. Также похоже, что вы, возможно, были немного испорчены intellisense (или аналогичными функциями). Многим (многим) из нас приходится проверять документацию рано и часто.   -  person Ed S.    schedule 03.10.2013
comment
Интуиция - это результат тренировки, поэтому ничего не будет интуитивно понятным, пока вы не научитесь ее использовать.   -  person mu is too short    schedule 03.10.2013
comment
Этим вопросом страдает TL; DR. Было бы полезно резюмировать его и отбросить остальное, иначе он будет слишком широким. Это противоречит обещанию Руби об интуиции. Интуитивность для кого? Мац говорит, что он разработан так, чтобы быть интуитивно понятным для него, и что, по его мнению, он станет интуитивно понятным для других, которые использовали его в достаточной степени. Невозможно создать язык, который был бы интуитивно понятен каждому, поэтому его цель - точная.   -  person the Tin Man    schedule 03.10.2013
comment
Мне кажется смешным, что вы говорите «испорченный intellisense», когда говорите о языке, заявленная цель которого - снизить сложность кодирования. Несмотря на это, я программирую оба способа ежедневно.   -  person kamegami    schedule 03.10.2013
comment
Я читаю ту ссылку, которую вы разместили, и теперь у меня ЕЩЕ БОЛЬШЕ вопросов, связанных с набором текста -_-   -  person kamegami    schedule 03.10.2013
comment
Я нашел ваши замечания очень интересными, но у меня есть просьба: после того, как вы некоторое время использовали Ruby, отредактируйте, чтобы сообщить нам, изменились ли ваши взгляды, и если да, то как и почему.   -  person Cary Swoveland    schedule 03.10.2013
comment
@kamegami: Когда вы жалуетесь на то, что вас заставляют читать документацию, я могу только предположить, что вы испорчены такими функциями, как intellisense. Интуитивность не означает, что все волшебно так, как вы надеетесь. Жаловаться на чтение документации немного похоже на то, как американец среднего класса жалуется на то, что водит машину старой модели вместо Mustang 2013 года выпуска.   -  person Ed S.    schedule 03.10.2013


Ответы (3)


Это не специфическая проблема Ruby, она одинакова для всех языков с динамической типизацией.

Обычно нет никаких указаний, как это задокументировать (и в большинстве случаев это невозможно). См., Например, карту в документации по ruby.

map { |item| block } → new_ary
map → Enumerator

Что здесь item, block и new_ary и как они связаны? Невозможно сказать, если вы не знаете реализацию или не можете как-то вывести ее из имени функции. Указать тип также сложно, поскольку new_ary зависит от того, что возвращает block, что, в свою очередь, зависит от типа item, который может отличаться для каждого элемента в массиве.

Часто вы также натыкаетесь на документацию, в которой говорится, что аргумент имеет тип Object, что опять же ничего не говорит вам, поскольку все является объектом.

У OCaml есть решение для этого, он поддерживает структурную типизацию, поэтому функция, которой требуется объект со свойством foo, равным String, будет считаться { foo : String } вместо конкретного типа. Но OCaml по-прежнему статически типизирован.

Стоит отметить, что это также может быть проблемой для языков со статической типизацией. Scala имеет очень общие методы для коллекций, что приводит к сигнатурам типа, например ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Array[T], B, That]): That, для добавления двух коллекций.

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

Вот почему я предпочитаю статическую типизацию;)

Редактировать Возможно, имеет смысл делать то же, что и Scala. На самом деле он не показывает вам сигнатуру типа для ++ по умолчанию, вместо этого он показывает ++[B](that: GenTraversableOnce[B]): Array[B], который не является общим, но, вероятно, охватывает большинство случаев использования. Таким образом, для карты Ruby она может иметь сигнатуру мономорфного типа, например Array<a> -> (a -> b) -> Array<b>. Это правильно только в тех случаях, когда список содержит только значения одного типа, а блок возвращает только элементы одного другого типа, но это намного проще для понимания и дает хороший обзор того, что делает функция.

person Adam Bergmark    schedule 03.10.2013
comment
Вы попали в точку, это моя точная проблема. - person kamegami; 03.10.2013

Да, похоже, вы неправильно понимаете эту концепцию. Это не замена статической проверки типа. Это просто другое. Например, если вы конвертируете объекты в json (для их рендеринга клиенту), вас не волнует фактический тип объекта, если у него есть метод #to_json. В Java вам нужно будет создать IJsonable интерфейс. В Ruby никаких накладных расходов не требуется.

Что касается знания, что передавать, где и что что возвращает: запомните это или каждый раз обращайтесь к документации. Все мы так делаем.

Буквально в другой день я видел, как программист rails с более чем 6-летним опытом жаловался в твиттере, что не может запомнить порядок параметров для alias_method: новое имя идет первым или последним?

Это противоречит обещанию Руби об интуиции.

Не совсем. Может это просто плохо написанная библиотека. В core ruby, осмелюсь сказать, все довольно интуитивно понятно.

Статически типизированные языки с их мощными IDE имеют здесь небольшое преимущество, потому что они могут очень быстро показать вам документацию прямо здесь. Тем не менее, это все еще доступ к документации. Только быстрее.

person Sergio Tulentsev    schedule 03.10.2013
comment
Я думаю, вы близки к тому, что я имею в виду. Как вы говорите, вы должны объявить IJsonable. В Ruby этого не объявляют. Тогда КАК вы знаете, что вам нужен этот интерфейс ?? Как вы говорите, вы ДОЛЖНЫ обращаться к документации, и в документации ДОЛЖНО указываться, что вам нужен этот интерфейс, иначе нет способа узнать, не проверив код. Тогда в чем преимущество, которое вы должны объявить в документах, почему бы не объявить его в коде, тогда ваша IDE знает интерфейс и может действительно автоматизировать вещи за вас. Что еще за накладные расходы на объявление вашего интерфейса. - person kamegami; 03.10.2013
comment
Накладные расходы на интерфейс - это еще одна сущность в системе. Что это за странная библиотека, с которой у вас проблемы? Большая часть того, с чем я работал, они принимают либо примитивы (строки, массивы, хэши и т. Д.) (Что очевидно из документации), либо объекты из той же библиотеки (которые, опять же, документированы). - person Sergio Tulentsev; 03.10.2013
comment
Если вы передадите методу неправильный объект, это вызовет ошибку. Это будет обнаружено тестами. - person Sergio Tulentsev; 03.10.2013
comment
Преимуществом здесь является возможность написать одну-две строки кода, не загроможденные информацией о типах, которые выполняют то же (или более), что и 100 строк кода Java. - person Sergio Tulentsev; 03.10.2013
comment
Я в основном говорю гипотетически, так как я не копался глубоко в библиотеке. Похоже, ответ состоит в том, чтобы спроектировать все, чтобы принимать примитивы и намекать на интерфейс с соглашением об именах методов (опять же, это подразумевает стандарты процессов, которые не являются частью языка). Я сделал достаточно кода с открытым исходным кодом, чтобы знать, что не ожидаю надлежащих комментариев и документации. Черт возьми, языки статического типа, по крайней мере, позволяют вам создавать документацию, в какой бы то ни было скучной форме. Есть ли стандарты документации для Ruby? Стандарт комментариев в стиле jDoc? - person kamegami; 03.10.2013
comment
Да, но объявление типа не занимает 100 строк кода. Я не понимаю, как утка что-то упрощает. - person kamegami; 03.10.2013
comment
RDoc / YARD широко используются для генерации документации. - person Sergio Tulentsev; 03.10.2013

Учтите, что выбор дизайна строго типизированных языков (C ++, Java, C # и др.) Требует строгих деклараций типа, передаваемого методам, и типа, возвращаемого методами. Это связано с тем, что эти языки были разработаны для проверки правильности аргументов (и поскольку эти языки скомпилированы, эту работу можно выполнить во время компиляции). Но на некоторые вопросы можно ответить только во время выполнения, и, например, C ++ имеет RTTI (интерпретатор типов времени выполнения) для проверки и обеспечения гарантий типов. Но как разработчик вы руководствуетесь синтаксисом, семантикой и компилятором для создания кода, который следует этим ограничениям типа.

Ruby дает вам возможность принимать динамические типы аргументов и возвращать динамические типы. Эта свобода позволяет вам писать более общий код (прочтите Степанов о STL и универсальном программировании) и дает вам богатый набор методов самоанализа (is_a ?, instance_of?, Response_to?, Kind_of?, Is_array? И др.), Которые вы можно использовать динамически. Ruby позволяет вам писать общие методы, но вы также можете явным образом применять дизайн по контракту и обрабатывать отказ контракта с помощью выбранных средств.

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

Возможно, ваша озабоченность связана с предвзятым отношением к строго типизированным языкам (C ++, Java, C # и др.). Утиная печать - это другой подход. Ты думаешь иначе. Утиная печать означает, что если объект выглядит как a, ведет себя как a, то это a. Все (почти) является объектом в Ruby, поэтому все полиморфно.

Рассмотрим шаблоны (они есть в C ++, в C #, в Java они есть, в C есть макросы). Вы создаете алгоритм, а затем заставляете компилятор генерировать экземпляры для выбранных вами типов. Вы не разрабатываете по контракту с дженериками, но когда вы понимаете их силу, вы пишете меньше кода и производите больше.

Некоторые из ваших других проблем,

  • сторонние библиотеки (драгоценные камни) не так сложно использовать, как вы боитесь
  • Документированный API? См. Rdoc и http://www.ruby-doc.org/
  • Документация Rdoc (обычно) предоставляется для библиотек.
  • рекомендации по кодированию - для начала посмотрите в исходный код пару простых жемчужин
  • соглашения об именах - как змеиный, так и верблюжий чехлы популярны

Предложение - непредвзято подойдите к онлайн-руководству, выполните самоучитель (http://rubymonk.com/learning/books/ хорошо), и у вас будут более конкретные вопросы.

person ChuckCottrill    schedule 03.10.2013
comment
Кажется, это ответ, который я искал. Так что я должен выполнять самоанализ как надлежащую проверку параметров во время выполнения. И затем вы ДОЛЖНЫ создавать документацию вместо объявлений типа или интерфейса. Можно ли сгенерировать документацию на основе самоанализа? Мне было бы намного удобнее сказать, что вы должны следовать соглашению, если бы существовал эквивалент PEP8, чтобы указать и сказать, что это сделано неправильно. - person kamegami; 03.10.2013
comment
@kamegami: интроспекция Ruby не обнаруживает предполагаемые типы из кода, поэтому он не может создать полную документацию без посторонней помощи. Однако инструменты документации, такие как yard, позволяют кодировщикам объявлять типы для параметров и возвращаемых значений. Поэтому вы полагаетесь на добрые намерения разработчиков библиотеки с документацией. Если есть время и желание, можно написать очень основательную и точную документацию. - person Neil Slater; 03.10.2013
comment
Похоже, что если бы существовало стандартное соглашение для объявления намерения параметра (то, что я назову объектом, имеющим ожидаемый интерфейс), вы могли бы обрабатывать документацию по интерфейсу, обработку ошибок и статический анализ намерений IDE (стиль Intellisense) одним махом. . Я НЕ МОГУ быть единственным, кто думает об этом для языка динамических типов. - person kamegami; 03.10.2013
comment
Моя степень была по математике, поэтому я изучал доказательства, а затем формальные методы спецификации, разработки и построения правильных программ. Я понимаю и ценю строгий подход к языкам со строгой типизацией. Языки, которые я знаю, не предлагают внутренних конструкций для формального объявления самых слабых предусловий или формальной спецификации поведения. Типы проверки C и C ++, но допускают принуждение и арифметику указателей, C ++ обеспечивает константную корректность. Java избегает арифметики указателей и принуждения. Но у всех есть дженерики. Ruby, Perl, Javascript, Lua, Python упрощают набор текста. - person ChuckCottrill; 04.10.2013
comment
И я обнаружил, что эти слабо типизированные языки, Ruby / Perl / Javascript / Lua, более продуктивны (быстрее создают больше рабочих функций). С оговоркой, что необходимо тщательное программирование. - person ChuckCottrill; 04.10.2013