#polymorphism #ocaml #addition
#полиморфизм #ocaml #добавление
Вопрос:
Я новичок в OCaml. Мне нравится скорость OCaml, но я не до конца понимаю его дизайн. Например, я хотел бы, чтобы
оператор был полиморфным для поддержки целых чисел, чисел с плавающей точкой и так далее.
Зачем нам это нужно .
?
Комментарии:
1. В названии вашего вопроса утверждается, что речь идет о синтаксисе, и тогда единственный пример ошибки, который вы приводите, касается системы типов. Взгляните на вопросы StackOverflow по поводу «[C] странного поведения». Многие из них вызваны системой типов C.
float f = 3 / 7;
устанавливаетf
в ноль.sizeof(int) - 5
не-1
. Ну, OCaml не использует эту проклятую систему. Вопрос должен быть в том, почему так много языков все еще используют его, когда это озадачивает так много людей?2. Это отличный вопрос, и я очень разочарован тем, что он закрыт. По этому поводу можно сделать много объективных заявлений. Разделение
и
.
делает вывод типа более простым и предсказуемым. Альтернативы менее предсказуемы (значения по умолчанию) или потенциально намного менее эффективны (отправка). Тогда возникает вопрос о том, будет ли это злоупотреблением перегрузкой, учитывая, что функции имеют разные характеристики (например, ассоциативность) или даже совершенно разные цели (деление против евклидова коэффициента).
Ответ №1:
Я бы хотел, чтобы оператор ‘ ‘ был полиморфным для поддержки целых чисел, чисел с плавающей запятой и так далее. Зачем нам ‘ .’?
Отличный вопрос. Здесь задействовано много тонких компромиссов.
Преимущества отсутствия перегрузки операторов (как в OCaml) заключаются в:
- Вывод типа проще и предсказуем.
- Код более композиционный: перемещение кода из одного места в другое не может повлиять на его значение.
- Предсказуемая производительность: вы всегда точно знаете, какая функция вызывается.
Недостатками являются:
- Количество различных операторов быстро выходит из-под контроля:
int
,.
дляfloat
,/
для рациональных чисел произвольной точности,|
для векторов,||
для матриц и комплексных чисел, низкоразмерных векторов и матриц, однородных координат и т.д.
Некоторые альтернативы:
- Закрытый одноранговый полиморфизм и типы равенства, как в стандартном ML: немного более сложный вывод типов, код больше не поддается компоновке, но является хорошим решением для фиксированного числа предопределенных типов.
- Откройте специальные типы полиморфизма и равенства, как в F #: такие же, как SML, но могут быть применены и к определяемым пользователем типам. Выразительный, быстрый и без ошибок, но перегрузка ограничена определенными операторами, а разрешение во время выполнения запрещено.
- Введите классы, как в Haskell: выразительные, но сложные и непредсказуемо медленные из-за потенциального разрешения во время выполнения.
- Неявное продвижение, как в C / C : простое, но вызывает такие ошибки, как
2.3/0
и1/12*3.7
. - Динамический набор текста, как в числовой башне Lisp: непредсказуемо медленный.
Комментарии:
1. Классы типов не обязательно должны разрешаться во время выполнения. Существуют также прагмы, гарантирующие разрешение во время компиляции. Методы класса типа могут нести значение, поэтому код не должен изменять значение в другом контексте. Вывод типа также предсказуем и всеобъемлющ в их присутствии. Проблема, кстати, не только в большом количестве операторов, но и в сложности написания универсального кода. Функция, использующая полиморфные операторы, будет более полезной и более повторно используемой, чем функция, жестко закодированная для определенного типа.
2. @Peaker «Классы типов не обязательно разрешать во время выполнения». Как вы разрешаете все классы типов во время компиляции, когда код может быть загружен динамически?
3. @Peaker «Функция, использующая полиморфные операторы, будет более полезной и более многократно используемой, чем функция, жестко закодированная для определенного типа». Арифметика является наиболее распространенным источником перегруженных операторов. Целые числа и числа с плавающей точкой являются наиболее распространенными числовыми типами. Численные методы с целыми числами и числами с плавающей запятой не имеют почти ничего общего, поскольку семантика перегруженных операторов отличается между этими числовыми типами, в первую очередь из-за округления в арифметике с плавающей запятой. Итак, классы типов позволяют вам учитывать общность, но там мало общности, которую нужно учитывать.
4. Вы можете использовать СПЕЦИАЛИЗИРОВАННЫЕ программы и давать конкретные типы. Вы можете оставить это для среды выполнения, и если нет способа узнать типы до времени выполнения, это будет оставлено для среды выполнения. Это особенность, а не ошибка.
5. Я не уверен, что арифметика является наиболее распространенным источником перегруженных операторов. Также (>>=), (>>), (<>) и многие другие неарифметические перегрузки. Я согласен, что целые числа и числа с плавающей точкой не настолько похожи, и действительно, они используют разные классы типов. Но Int, {Int, Word}{8,16,32,64}, Integer — все очень похожие типы. Float и Double имеют много общего.
Ответ №2:
OCaml не поддерживает полиморфные операторы (числовые или иные), отличные от операторов сравнения.
Versus .
устраняет множество мелких ошибок, которые могут возникнуть при преобразовании целых чисел разных размеров, чисел с плавающей точкой и других числовых типов взад и вперед. Это также означает, что компилятор всегда точно знает, какой числовой тип используется, тем самым облегчая распознавание, когда программист сделал неверные предположения о том, что число всегда имеет целочисленное значение. Требование явного приведения между числовыми типами может показаться неудобным, но в долгосрочной перспективе это, вероятно, сэкономит вам больше времени на отслеживание странных ошибок, чем вам придется потратить, чтобы написать этот дополнительный период, чтобы быть явным.
Помимо .
версий числовых операторов, я не думаю, что синтаксис OCaml особенно странный. Он во многом соответствует предыдущим языкам ML с соответствующими и разумными расширениями синтаксиса для его дополнительных функций. Если поначалу это кажется вам странным, это, вероятно, просто указывает на то, что до сих пор вы программировали только на языках с тесно связанным синтаксисом. По мере изучения новых языков вы увидите, что существует множество различных способов создания языкового синтаксиса с различными преимуществами и недостатками, но многое из этого — просто произвольные соглашения, которые кто-то выбрал.
Комментарии:
1. » Versus . thing устраняет множество мелких ошибок». Если бы это было правдой, код F # страдал бы от этих тонких ошибок, но это не так. На самом деле ошибки почти полностью вызваны неявными приведениями (например, 2.3 / 0 или 1/12 * 123.456), а не перегрузкой. F # не выполняет неявных приведений, т. е.
является
int -> int -> int
или,float -> float -> float
но неint -> float -> float
. Так что это не является веским мотивом для выбора OCaml.2. «Помимо . версии числовых операторов». И не забывайте, что
.
версии предназначены специально дляfloat
числового типа. OCaml также предоставляет/
возможность добавления произвольной точности, и мне приходилось писать свою собственную|
версию для векторов и||
версию для матриц, что очень быстро становится смешным. И OCaml даже не предлагает большинство числовых типов, таких как 32-разрядные числа с плавающей запятой…3. Этот аргумент выглядит разумным только до тех пор, пока вы не начнете учитывать все другие компромиссы, которые F # пришлось сделать, чтобы поддержать эту перегрузку.
4. Вы правы, что это действительно неявное приведение, которое вызывает большинство незначительных ошибок, и что вы можете выполнять полиморфные операторы без неявного приведения, которые не вызывают такого беспокойства. Но как только вы разрешаете полиморфные операторы, в общем, тогда люди, определяющие свои собственные, могут создавать операции int -> int -> int, float -> float -> float и int -> float -> float. Единственный способ предотвратить это — разрешить только полиморфные операторы для стандартных библиотек или какой-либо другой такой специальной оболочки. Имеет смысл иметь единые правила.
5. Некоторое другое обсуждение причин. Модульные импликации — это предложение добавить перегрузку в OCaml. Классы модульных типов — это (всего лишь) теория потенциального добавления классов типов в ML-подобный язык.
Ответ №3:
В принципе, системы типов SML и OCaml (игнорируя объектную систему и модули) не поддерживают специальный полиморфизм. Это дизайнерское решение. OCaml также, в отличие от SML, решил не включать синтаксический сахар для арифметики.
Некоторые языки семейства ML имеют чрезвычайно ограниченные формы специального полиморфизма в числовых операторах. Например, ( )
в стандартном ML имеет тип по умолчанию (int, int) -> int
, но имеет тип (float, float) -> float
, если его аргумент или возвращаемый тип известен как float
. Эти операторы являются особыми в SML и не могли быть определены, если они еще не были встроены. Также невозможно присвоить этому свойству другие значения. val add = ( )
будет иметь тип (int, int) -> int
. Особенность здесь ограничивается синтаксисом языка, в системе типов нет поддержки специального полиморфизма.
В OCaml есть несколько операторов со специальной семантикой (но числовые операторы не входят в их число), например, ||
и amp;amp;
имеют короткое замыкание (но становятся длинным замыканием, если вы присваиваете им промежуточное значение)
let long_circuit_or = (||);;
let print_true x = print_string x; true;;
(* just prints "4" *)
print_true "4" || print_true "5";;
(* prints "45" *)
long_circuit_or (print_true "4") (print_true "5");;