Pull to refresh

Comments 80

Под «Наследованием типов» я так полагаю подразумевается «выведение типов». (type inference)
М… Да, видимо, моя ошибка. Так как я сам это учу, то про эту концепцию (точнее, про её русское название) не знал. (На перевод touple как кортеж меня всё-таки хватило).
Круто, что поправили, потому что «наследованием типов» и «выведение типов» все таки очень разные вещи.
Ну, моя ошибка, как переводчика. Речь всё-таки про выведение типов.
Интересно вообще последнее время следить за функциональными языками программирования. А вообще какие перспективы у OCaml? Почему именно его начали изучать?
По-моему, более перспективно учить F# (наследник OCaml), который есть на .NET и Mono.
Ну это на любителя. Лично меня не радует перспектива тащить моно рантайм с приложением. Да и по скорости возникают вопросы — компилятор в код у ocaml сделан очень удачно. Не уверен, что моно код сможет конкурировать с ocaml в этом.
В последнем .NET JIT работает на ура, так что со скоростью работы проблем нет, и кроме того можно использовать любые .NET-сборки, написанные на любом языке, коих существует огромное количество :)
И ocaml и f# используют сборщик мусора, значит скорость будет примерно одинаковой. Единственные языки, которым дотнет уступает по скорости — это те, где нет GC, по понятным причинам.
ИМХО, если нет религиозных предубеждений перед тем, что ноги f# растут из МС, то он куда интереснее. Судя по тому, что это едва ли не первый функциональный язык, который активно продвигает крупная корпорация, он вообще имеет все шансы стать первым мейнстримовым функциональным языком.
Не холивора ради, просто посмотрите на C#, который в 2001 году выглядел практически клоном Java, а за десять лет и четыре версии из него сделали наверное самый красивый и мощный язык среди мейнстримовых. А в Java за это время сподобились разве что прикрутить дженерики, да и то на уровне языка, а не среды выполнения.
BTW, кто не знает, F# открыт и доступен под лицензией Apache.
В OCaml'е есть компиляция в нативный код.
В F# это делает JIT перед каждым запуском программы. Далее выполняется все тот же нативный код. И, кстати, никто не отменял утилиты вроде ngen. Так что мимо.
Ага. И какого размера получится код? Особенно при статическом линкинге?
Естественно больше, но согласитесь, что аргумент «результирующие бинарники будут больше» звучит все таки менее драматично чем «F# тормозной, потому что генерирует не нативный код». К тому же последний как бы и не правда.
«И ocaml и f# используют сборщик мусора, значит скорость будет примерно одинаковой.»

Это мягко говоря неправда
Примерно правда что? Что если есть сборщик мусора, то скорость должна быть одинакова? Тогда и питоп с psyco и erlang c hipe долджны давать одинаковый результат что и моно. Ведь откровенно неправда.
Что скорость примерно одинакова у этих языков. F# немного быстрее OCaml в 4-х тестах из 7, медленнее в одном и сильно медленнее в оставшихся 2-х (это ко второму комменту).
И кстати по ссылке, которую вы привели — ну ясно же видно, что F откровенно слабей.
Аргументировать можете? Я не спорю, что возможно в среднем F# будет чуть-чуть медленнее (или быстрее), но если эта разница в пару процентов, то сами понимаете.
А вот ссылку выше от тов. VolCh посмотрите. Увидите и в 4 раза и в 6 раз.
Основная проблема F# — он генерирует не нативный код. Можно сколько угодно говорить про незначительность JIT, но это ровно до того момента, пока мы не подходим к серьёзным системным вопросам. На OCaml написан цитриксовский xapi, и одна из существенных особенностей кода на окамле — он не тащит за собой всякой дряни, а значит отлично подходит для содержимого стаб-доменов xen'а. Условно говоря, на OCaml можно написать псевдо-ядро для стаб-домена, на F# и прочих mono с грудами библиотек под жирные ядра (допустим, даже, линукса) — нет. То есть внутри уютненькой ОС — да, можно сравнивать. А если уютненькую ОС убрать?
Ну да, для кого-то куча библиотек это счастье, а для кого-то трагедия. Всё зависит от задач, так что я не спорю.
Куча библиотек — это здорово. Особенно здорово, когда при необходимости, их можно статически слинковать.
mono таки умеет компилится в нативный код со статически линковаными библиотеками. Я выше уже выложил ссылку на статью Мигеля двухлетней давности. Именно поэтому и возможны всякие MonoTouch'и, которые позволяют писать софт под те же айфоны, где никаких рантаймов моно никогда не было, нет и не будет.
Впрочем, наверное вы правы в том, что если уж писать софт для очень низкого уровня, то стоит выбирать для этого соответствующие инструменты.
Ну, у меня это исключительно производственная нужда. xapi написан на OCaml и демонстрирует, что строгая типизация и компиляция таки рулит в сравнении с питоновским охламонством xend'а.
Как раз, возможно, с кодом на OCaml работать придется скоро, спасибо!)
Вот зачем изобретать велосипед, с комментариями? Я про то, что они (*, а не как принято /*.
А в Великом и Ужасном Паскале, например, (*… *) (кроме {}). Вы, наверное, выросли на C-подобных языках. Есть куча языков, в которых не /*… */.
Это в C так принято. А ML примерно в то же время появился, что и C. В паскале, например, тоже (* комментарии.
Надеюсь вы дочитали статью не только до того момента, где говориться о комментариях.
Просто здесь много всего, что сделано «не как принято» и комментарии думаю не самая важная из этого списка вещь.
Велосипед не мой, я просто разместил перевод.
Python, TCL, perl, bash, большинство конфиг-файлов — везде комменты начинаются с #. На самом деле, языков с C-подобными комментариями меньшинство.
> double average… Сравните с более компактной версией на OCaml.
Это спорная оценка.
а для комплексного, векторного, цветового и других видов умножений какие операторы используются?

как опредлить функцию sqr которая бы принимала на вход как целые так и вещественные числа? нельзя? тогда где тут полиморфизм?
1. Выведение типа:
# let hello x = «Hello » ^ x;;
val hello: string -> string = # let hello x = x;;
val hello: 'a -> 'a = (* 'a — означает любой тип *)
2. Ограничение типа:
# let hello (x:string) = «Hello » ^ x;;
val hello: string -> string = 3. Полиформизм
# let hello (x:'a) = «Hello » ^ x;;
val hello: string -> string =
И все равно этот вариант примет только строку.
# let hello (x:'a) = x;;
val hello: 'a -> 'a = (* то же самое что и let hello x = x;;, лишь с явным указанием того что доступен любой тип *)
и что полезного сможет сделать функция с «любым типом» кроме как вернуть переданное значение?
это просто пример, как указать, что функция может принимать аргумент любого типа.
зачем указывать? тут же автоматическое выведение типов!
С ними всё просто. Как и практически всегда, с ними нельзя сделать ничего иного, кроме как выбросить либо организовать в какую-либо структуру. Например, для организации произвольного дерева нужно знать тип данных? Нет, не нужно. Вот когда это дерево потребуется сделать упорядоченным, придётся писать специализированные функции. Кроме того, есть еще другие случаи, когда функции, работающие с 'a, определены в самом языке или импортируются из внешних библиотек. Например, хэш-таблица в окамле пользуется специальной сишной функцией, которая любой тип просто сжуёт как набор байт и создаст хэш.
то есть строка и число, которые имеют одинаковые байты будут выдавать один и тот же хэш? суперская хэш-таблица х) сишная магия и тут во всей красе
Ну в данном случае можно сделать так:
# type num = First of int | Second of float | Triple of int*int*int;;
type num = First of int | Second of float | Triple of int * int * int
# let multiply_by_five x =
match x with
First(a) -> First(a*5)
| Second(b) -> Second(b*.5.)
| Triple(a,b,c) -> Triple(a*5,b*5,c*5);;
val multiply_by_five : num -> num = # multiply_by_five (First 42);;
- : num = First 210
# multiply_by_five (Triple (1,2,3));;
- : num = Triple (5, 10, 15)


Иначе говоря, вы можете на основе существующих типов определить некий общий тип (что-то вроде union в С, только сложнее и умнее), с которым и работать дальше. Заметьте, обобщив разрешенные входные данные в какой-то единый тип, мы можем легко и изящно определять «а какой тип данных скормили функции».
ничего не понял кроме того, что это совсем не функция sqr и на «легко и изящно» совсем не похоже.
Ок, напишу для Вас sqr:
# let sqr x =
match x with
First(a) -> First(a*a)
| Second(a) -> Second(a*.a)
| Triple(a,b,c) -> Triple(a*a,b*b,c*c);;
val sqr : num -> num = # sqr (First 5);;
- : num = First 25
# sqr (Triple (1,2,3));;
- : num = Triple (1, 4, 9)

Разница колоссальна, да.
А что касается «легко и изящно», тут надо понять две вещи. Чтобы вызвать операцию умножения (деления, прибавления 148) для какого-то аргумента, эта операция для данного типа должна быть определена. Если складывать штаны с табуретками, надо определить соответствующий оператор в любых языках. Просто в Си (бэйсик, пхп, ява, whatever) операторы и функции могут иметь одинаковые имена, вследствие чего выбор нужной функции и автоматическое приведение типа происходит автоматически на этапе компиляции. В чём-то это удобно, зачастую ещё и опасно. Окамл требует явного указания, какую функцию мы вызываем, взамен этого он позволяет нам выбирать поведение, в зависимости от типа, не только при вызове функции, но и абсолютно в любой момент. Более того, вышеупомянутым match можно, в зависимости от шаблонов, работать не только с типами, но и с конкретными значениями, содержимыми списков:
# let check_list x =
match x with
[] -> print_endline "list is empty"
| x :: [] -> print_endline "list has one element"
| x :: y :: [] -> print_endline "list has two elements"
| _ -> print_endline "too many";;
довольно кривой способ определения полиморфной функции
да, с перегрузкой туго во всех ML-подобных языках. что-то приходится жертвовать ради нормального выведения типов.
под полиморфизмом понимается совсем не то, что вы думаете. полиморфной функция может быть, только если она не зависит от типа.
sqr/sqrt делают разные вещи в зависимости от типа аргумента. поэтому это должны быть 2 разные функции. ведь в C бы это тоже было 2 разные функции, хоть и с одинаковым именем.
если функция не зависит от типа, то как она может зависеть от значения? а если она не зависит и от значения, то на кой чёрт вообще это значение передавать?

полиморфизм в том и заключается, что мы указываем что нужно сделать, но конкретная реализация для заданного типа подбирается автоматически.
>> если функция не зависит от типа, то как она может зависеть от значения?
легко. в любом языке, поддерживающем параметрический полиморфизм вагон примеров. всякие List.Remove(T item) и ежи с ними.
мы не про любой язык а про конкретный окамл.
ну вот вам пример из OCaml'я: List.map, List.fold_left, List.fold_right :)
в общем функции над списком чего-то. неважно чего. они одинаково работают с любыми типами, будь то целое или кортеж.
«Автоматически» формально к полиморфизму не относится, можно сказать, что это синтаксический сахар в некоторых языках :) Главное для полиморфизма, что можно выбрать одну из нескольких реализаций, а автоматически или ручками — не суть.
это только в твоём вымышленном мире.
Так выше кривой способ определения полиморфной функции или она полиморфна только в моём вымышленной мире?
комментарии в OCaml очень похожи на комментарии в Си

Комментарии (* *) — это же в чистом виде Pascal. Когда-то давным-давно IDE не умели их нормально обрабатывать (например, считали, что выражение (*) — это открытый и тут же закрытый комментарий, и неправильно разукрашивали код). На этом можно было даже всякие приколы делать вроде:
write('Hello');
(*) write(', world!'); (*)
И срабатывают оба write'а. Естественно, что вместо вывода могло быть что-нить похлеще =)
Пардон, не так написал: срабатывает-то один write, но вот подсвечиваются как код оба, несмотря на то, что второй на самом деле закомментирован.
> Стандартная функция OCaml int_of_char casting:
Потеряли перевод слова casting.

Хорошо, что взяли для перевода этот мануал, он куда лучше, чем официальный.
спасибо, сейчас поправлю.
Спасибо за перевод! Больше функциональщины разной в массы!
а сюда лучше не смотреть? ocaml.spb.ru/

И еще вопрос, тот окамл, что указан в статье (apt-get install ocaml) это компилятор в нативный код?
насколько я понимаю, да, и он используется в продакт системах с большим успехом. Собственно, вопрос стоит так: либо Си, либо OCaml, выбор, думаю, очевиден, ибо GC.
За перевод, безусловно, спасибо — но местами стоило перечитать результат. Например:
Шайзе. Тем не менее:
«Пока что не очень понятно, почему полиморфные функции полезны, то на самом деле они очень полезны и очень распространены;» -> потеряно «если» в начале?
«Темой этого учебника является мысль, что...» -> "… мысль о том что..."
«Наша функция average, принимающая два числа с плавающей запятой и возвращающая одно число с плавающей запятой описывается так:» -> "… запятой, описывается так:"
«то мы записываем, что она возвращает» — не нужна запятая
«Как насчёт функции, которая принимает» — то же самое
«Мы можете подумать, что явное приведение уродливо, что это нудное занятие» — мы такие, мы можете. ну и первая запятая гоу хоум, да.
«оно работает даже для больших, и это экономит массу времени, потому что это удаляет целый класс ошибок» — гоу хоум обе две.
Ну и, вероятно, там кроется ещё некоторое количество. Но, повторюсь — за статью спасибо, было довольно интересно.
(покорнейше прошу простить за занудство)
«Синтаксис OCaml изящен и лаконичен Вот функция»
«трёхкратно» — «троекратно»
«строка helo» — ll
«OCaml не осущетсвляет»
«плавающей запятой, используйте» =~ s/,//;
Спасибо, поправил. В чём-то профит от хабра есть. Сейчас пойду допереводить вторую часть.
Не понял, почему Вы так не любите запятые в сложносочиненных и сложноподчиненных предложениях?
Во всех местах, про которые сказано, что запятая не нужна, запятые стоят правильно.
ну, «можете подумать, что явное приеведение уродливо» — тут как-то совсем не к месту.
«аже для больших, и это экономит» — это же не перечисление.
«времени, потому что» — тут я пожалуй неправ, но в другую сторону — «времени потому, что»
1. К месту — это сложноподчиненное предложение, запятая обязательна.
2. «Оно работает, и это экономит». Сложносочиненное предложение, запятая нужна.
3. Здесь запятую допустимо поставить двумя равнозначными вариантами, убирать её в любом случае нельзя.

Параграфы §140, §137 и §141 соответственно.
Лучше не спорьте :) Я корректурой лет 5 уже как не промышляю, и албанский плотно вошёл в мою жизнь, но русская языка ещё помню.
почему ocalm, а не всемилюбимый haskell?
Потому что мне нужно изучить окамл. А перевод текста — это побочный результат процесса.
На всякий случай, для интересующихся: code.google.com/p/funprog-ru/
Перевод лекций по функциональному программированию, с примерами на ML.
UFO just landed and posted this here
UFO just landed and posted this here
Как минимум вторая статья переведена на 80%. Дальше не знаю, посмотрим. Переводить там текста оказывается много больше, чем читать.
Допустим, вы написали функцию, назовём её repeated, которая берёт исходную строку s, число n и возвращает новую строку, состоящую из троекратно повторённой строки s.


Не троекратно, а n-кратно.
Sign up to leave a comment.

Articles