![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
На своей нынешней работе я много пишу на OCaml. Не только на нем, но если это не SQL, и не простенькие скрипты, то это почти наверняка будет OCaml. И по результатам трех месяцев я решил сесть и записать свои негативные впечатления от, скажем так, перехода с Haskell. Про позитив писать особого смысла нет - ну, почитаете вы его, покиваете головой и все. А так, глядишь, кто чего посоветует :)
В этот раз, думаю, у меня получится лучше, чем в прошлый.
1 Сигнатуры типов
1.1)Сигнатуры в .ml
В Haskell можно было легко и просто написать у любой функции сигнатуру прямо рядом с реализацией:
В OCaml же такое счастье недоступно. Можно, конечно, извратится:
Но это же как-то противоестественно. Хорошо, если функция определена не через частичное применение других функций - тогда можно явно указать типы аргументов:
А иначе - счастья нет :( Или есть, но я про него не знаю?
1.2)Сигнатуры у top-level определений.
В Haskell считалось хорошим тоном писать у всех экспортируемых определений типы, чтобы код было проще читать. В OCaml интерфейс вынесен отдельно (в .mli или module type ... : sig) и можно бы сказать, что типы описаны там. Но! Когда читаешь чужой код в .ml, хочется иметь перед глазами типы, а не держать в соседнем окошке .mli и поглядывать туда. Частично ситуацию спасает ocamlspot и интерфейс к нему из emacs - можно поставить курсор на нужное место, нажать C-c C-y и увидеть тип выражения, но это резко снижает скорость чтения кода.
А еще в haskell был хороший режим "ругаться на top-level определения без сигнатур". И компилятор в этом случае говорил: "вот у вас тут foo без типа, это нехорошо. У меня получился тип вот такой: ...". Это было хорошо и удобно, и без этого как-то плоховато.
2 Импорт определений из других модулей
В OCaml модуль импортируется целиком - "open Module.Name" и получите и распишитесь все определения из Module.Name. В Haskell можно было указать список импортируемых в текущую область видимости функций, и это, опять же, сильно помогало читать чужой код.
А еще можно было попросить GHC выписать свое видение списка импортов для всех компилируемых модулей, с четким перечислением того, что откуда реально импортировалось. Временами этого сильно не хватает.
3 Компиляция
3.1) Я не скажу за всю Одессу, и, например, за godi, но я таки скажу за omake.
OMake - это такой make, который умеет много чего, и в частности умеет "автомагически" находить окамловые зависимости. И это работает, и обычно работает неплохо. Но! OMake любит пересобрать зависимости "с запасом". Причем начать пересобирать их издалека. И это, граждане, временами сильно напрягает. А все из-за следующего пункта
3.2)По одному сообщению об ошибках в руки
Возможно, я тормоз, и не дочитал документацию на omake, но в моей практике компилятор выдает мне одно сообщение об ошибке и говорит: "ну все, я так дальше не играю. Чини вот это, и пробуй еще раз". В результате вместо того, чтобы при рефакторинге один раз попробовать откомпилировать, N раз нажать "next error", поправить кучу мест сразу и все собрать, приходится N раз делать "compile - next error - edit - смыть - повторить".
3.3)Сообщения об ошибках
Сообщения об ошибках - это почти всегда "пичалька"
а)"Error: This expression has type foo but is here used with type foo". Происходит это из-за перекрытия определения более поздним (shadowing) и выбивает из колеи только в первый раз, но все равно - горсть нервных клеток оно у меня забрало :)
b)Когда типы в реализации модуля не совпадают с интерфейсом, компилятор считает своим долгов вывалить в сообщение об ошибке весь интерфейс модуля, сколько его ни есть
c)Еще очень бодрит, когда компилятор в сообщении об ошибке цитирует грамматику языка, типа: Error("[fun_def] expected after [simple_patt] (in [expr])"). Сразу становится понятно, что не так, и что делать :)
4 Синтаксис
4.1)Ну, про вложенные match и точки с запятой не писал только ленивый. Я ленивый, поэтому повторятся не буду.
А напишу я лучше про ... комментарии.
Если так случилось, что ваши исходники собираются с помощью caml4p, и у вас только что случился merge conflict при обновлении исходников из репозитория, но по счастливой случайности он случился только в тексте комментариев - рано радоваться. Если вы решили сначала устранить все ошибки в исходниках, а потом разобраться с комментариями, как это попытался сделать я, то вас может ожидать неприятный сюрприз: caml4p будет возбуждаться на "<<<<<" и ">>>>>>" в тексте комментариев и в результате совершенно правильный код не будет собираться с совершенно невнятными ошибками.
Это, право слово, был удар ниже пояса :)
4.2)[REDACTED]
4.3)Синтаксического сахара для монад в языке нет. Но так как монады удобны, их используют. В результате имеем кучу boilerplate кода для monadic binds, из-за которого, опять же, тяжело читать код.
4.4)Читать типы справа налево - это иезуитство, и меня до сих пор временами клинит. Предложение о том, чтобы писать их слева направо запихнули в совершенно упадочный по своей сути Revised Syntax, где благополучно и похоронили.
4 Функторы
Функторы - это круто, но если интерфейс к твоему коду - это функтор (смотрим на Set, Hashtbl и проч. из Core), то единственный способ написать свою функцию, которая будет работать с любым Set-ом -- это нагородить поверх еще один функтор. В резлутьтате получается "ехал функтор через функтор, видит функтор - функтор(функтор), сунул функтор функтор в функтор - функтор функтор функтор функтор!".
5 Монады
В ru_lambda я это выносить не буду, т.к. это, по большому счету, детский сад и нытье.
На самом же деле:

В этот раз, думаю, у меня получится лучше, чем в прошлый.
1 Сигнатуры типов
1.1)Сигнатуры в .ml
В Haskell можно было легко и просто написать у любой функции сигнатуру прямо рядом с реализацией:
foo :: String -> Int -> IO () foo = bar . baz
В OCaml же такое счастье недоступно. Можно, конечно, извратится:
module type Foo : sig val foo : string -> int -> () end = struct let foo = .. end include Foo
Но это же как-то противоестественно. Хорошо, если функция определена не через частичное применение других функций - тогда можно явно указать типы аргументов:
let foo (x:string) (y:int) = ...
А иначе - счастья нет :( Или есть, но я про него не знаю?
1.2)Сигнатуры у top-level определений.
В Haskell считалось хорошим тоном писать у всех экспортируемых определений типы, чтобы код было проще читать. В OCaml интерфейс вынесен отдельно (в .mli или module type ... : sig) и можно бы сказать, что типы описаны там. Но! Когда читаешь чужой код в .ml, хочется иметь перед глазами типы, а не держать в соседнем окошке .mli и поглядывать туда. Частично ситуацию спасает ocamlspot и интерфейс к нему из emacs - можно поставить курсор на нужное место, нажать C-c C-y и увидеть тип выражения, но это резко снижает скорость чтения кода.
А еще в haskell был хороший режим "ругаться на top-level определения без сигнатур". И компилятор в этом случае говорил: "вот у вас тут foo без типа, это нехорошо. У меня получился тип вот такой: ...". Это было хорошо и удобно, и без этого как-то плоховато.
2 Импорт определений из других модулей
В OCaml модуль импортируется целиком - "open Module.Name" и получите и распишитесь все определения из Module.Name. В Haskell можно было указать список импортируемых в текущую область видимости функций, и это, опять же, сильно помогало читать чужой код.
А еще можно было попросить GHC выписать свое видение списка импортов для всех компилируемых модулей, с четким перечислением того, что откуда реально импортировалось. Временами этого сильно не хватает.
3 Компиляция
3.1) Я не скажу за всю Одессу, и, например, за godi, но я таки скажу за omake.
OMake - это такой make, который умеет много чего, и в частности умеет "автомагически" находить окамловые зависимости. И это работает, и обычно работает неплохо. Но! OMake любит пересобрать зависимости "с запасом". Причем начать пересобирать их издалека. И это, граждане, временами сильно напрягает. А все из-за следующего пункта
3.2)По одному сообщению об ошибках в руки
Возможно, я тормоз, и не дочитал документацию на omake, но в моей практике компилятор выдает мне одно сообщение об ошибке и говорит: "ну все, я так дальше не играю. Чини вот это, и пробуй еще раз". В результате вместо того, чтобы при рефакторинге один раз попробовать откомпилировать, N раз нажать "next error", поправить кучу мест сразу и все собрать, приходится N раз делать "compile - next error - edit - смыть - повторить".
3.3)Сообщения об ошибках
Сообщения об ошибках - это почти всегда "пичалька"
а)"Error: This expression has type foo but is here used with type foo". Происходит это из-за перекрытия определения более поздним (shadowing) и выбивает из колеи только в первый раз, но все равно - горсть нервных клеток оно у меня забрало :)
b)Когда типы в реализации модуля не совпадают с интерфейсом, компилятор считает своим долгов вывалить в сообщение об ошибке весь интерфейс модуля, сколько его ни есть
c)Еще очень бодрит, когда компилятор в сообщении об ошибке цитирует грамматику языка, типа: Error("[fun_def] expected after [simple_patt] (in [expr])"). Сразу становится понятно, что не так, и что делать :)
4 Синтаксис
4.1)Ну, про вложенные match и точки с запятой не писал только ленивый. Я ленивый, поэтому повторятся не буду.
А напишу я лучше про ... комментарии.
Если так случилось, что ваши исходники собираются с помощью caml4p, и у вас только что случился merge conflict при обновлении исходников из репозитория, но по счастливой случайности он случился только в тексте комментариев - рано радоваться. Если вы решили сначала устранить все ошибки в исходниках, а потом разобраться с комментариями, как это попытался сделать я, то вас может ожидать неприятный сюрприз: caml4p будет возбуждаться на "<<<<<" и ">>>>>>" в тексте комментариев и в результате совершенно правильный код не будет собираться с совершенно невнятными ошибками.
Это, право слово, был удар ниже пояса :)
4.2)[REDACTED]
4.3)Синтаксического сахара для монад в языке нет. Но так как монады удобны, их используют. В результате имеем кучу boilerplate кода для monadic binds, из-за которого, опять же, тяжело читать код.
4.4)Читать типы справа налево - это иезуитство, и меня до сих пор временами клинит. Предложение о том, чтобы писать их слева направо запихнули в совершенно упадочный по своей сути Revised Syntax, где благополучно и похоронили.
4 Функторы
Функторы - это круто, но если интерфейс к твоему коду - это функтор (смотрим на Set, Hashtbl и проч. из Core), то единственный способ написать свою функцию, которая будет работать с любым Set-ом -- это нагородить поверх еще один функтор. В резлутьтате получается "ехал функтор через функтор, видит функтор - функтор(функтор), сунул функтор функтор в функтор - функтор функтор функтор функтор!".
5 Монады
В ru_lambda я это выносить не буду, т.к. это, по большому счету, детский сад и нытье.
На самом же деле:

(no subject)
Date: 2011-07-11 11:27 pm (UTC)(no subject)
Date: 2011-07-12 05:39 am (UTC)(no subject)
Date: 2011-07-12 07:50 pm (UTC)(no subject)
Date: 2011-07-11 11:44 pm (UTC)(no subject)
Date: 2011-07-12 05:39 am (UTC)Точку опоры в виде паралеллизма я вижу. Осталось набрать вес :)
(no subject)
Date: 2011-09-13 06:23 am (UTC)(no subject)
Date: 2011-07-12 03:31 am (UTC)(no subject)
Date: 2011-07-12 05:41 am (UTC)Позитив в том, что это, по меткому выражению
Но это все равно ФП и строгая типизация, поэтому (как по мне) сильно лучше большого числа альтернатив.
(no subject)
Date: 2011-07-12 07:44 am (UTC)Там тоже строгая типизация и возможность писать в функциональном стиле!
Все эти новые языки — баловство одно!
А пишут, всё равно, на общепризнанных!
;-)
(no subject)
Date: 2011-07-12 10:29 am (UTC)(no subject)
Date: 2011-07-12 10:49 am (UTC)Ну, можно некое подобие, вроде.
Но это будет длиннее map func lst ;-)
(no subject)
Date: 2011-07-12 10:54 am (UTC)Так что, нельзя на ней в функциональном стиле.
(no subject)
Date: 2011-07-12 11:02 am (UTC)Наверняка, он не только знает, как можно исполнить в джаве что-то типа функционального стиля, но и как выдать его за человеческий ;-)
А ещё есть Scala. Вишьчо, это смотря, с чем сравнивать.
Если с хацкелем сравнивать, то не получится выдать за человечский стиль ;-)
(no subject)
Date: 2011-07-14 11:39 am (UTC)(no subject)
Date: 2011-07-13 10:53 am (UTC)(no subject)
Date: 2011-07-14 11:36 am (UTC)Как-то так должно быть.
(no subject)
Date: 2011-07-12 11:31 am (UTC)И что, предполагается, что это что-то хорошее?
(no subject)
Date: 2011-07-12 08:40 pm (UTC)(no subject)
Date: 2011-07-13 06:55 pm (UTC)(no subject)
Date: 2011-07-13 10:17 pm (UTC)Контакты - в профайле (например, email), предлагаю перемещаться туда.
(no subject)
Date: 2011-07-15 06:17 am (UTC)(no subject)
Date: 2011-07-12 08:57 pm (UTC)Тот же ocamlspot+emacs/vim (ткнул курсоров с терм, получил тип или перешел на определение)
Или omake -p (при этом omake будет мониторить файлы и пересобирать все, что надо, при их изменении автоматически).
(в скобках - это для тех, кто не в курсе, но кому интересно)
(no subject)
Date: 2011-07-12 04:06 am (UTC)(no subject)
Date: 2011-07-12 04:21 am (UTC)(no subject)
Date: 2011-07-12 06:27 am (UTC)Можно раскрыть 4.2? Я не очень понял, что имелось в виду (в сравнении с Хаскелем).
(no subject)
Date: 2011-07-12 08:47 pm (UTC)4.2 - это косяк поста :) Я тут думал описать одну конкретную проблему, с которой я боролся, и пока я ее усушивал до обобщенного описания, оказалось, что описывать вобщем-то и нечего. А фраза-placeholder осталась и пошла в финальный пост :(
Ну-ка я ее вырежу.
(no subject)
Date: 2011-07-12 07:03 am (UTC)есть всякое такое: http://www.cas.mcmaster.ca/~carette/pa_monad/
(no subject)
Date: 2011-07-12 08:49 pm (UTC)И, что гораздо хуже для меня, от них обычно сносит крышу у редакторов/подсветки синтаксиса и т.п.
(no subject)
Date: 2011-07-12 07:20 am (UTC)1.2. да, или бегать ml<->mli, или прописывать сигнатуры типов явно. Неудобно. В случаях, когда тип неочевиден, я прописываю сигнатуры в ml.
2. проще "module N = Module.Name" и далее "N.foo". Если же именно импортировать несколько функций, то
При желании можно написать синтаксическое расширение, но лично мне такой фокус был не особо нужен -- обычно либо open, либо N.foo.
3.3.а. Поправили в самых новых версиях, тип foo будет кое-как отличаться. Не помню, как именно.
4.1. рекомендую использовать revised syntax, он исправляет вложенные match. Про "<<" -- есть опция вида -no_quot для camlp4, отключает quotations, если они не используются реально в коде. Заодно полезно для разных "=<<" операторов.
4.2. а иначе лишаемся shadowing'а значений и вообще явного указания того, является ли вызов foo внутри функции foo вызовом себя или вызовом предыдущей foo, определённой ранее.
4.3. есть такое дело. Для частных случаев pa_monad посоветовали.
4.4. revised syntax вполне приличный, не надо грязи.
Жаба -- ок!
(no subject)
Date: 2011-07-12 08:52 pm (UTC)Про no_quot - тоже спасибо. Документацию надо читать :)
В 4.2.а я возмущался формой сообщения об ошибке, не более того. Shadowing нужен, тут спору нет.
(no subject)
Date: 2011-07-12 07:41 am (UTC)Я все никак не доберусь до всяких там экзотических языков программирования
(no subject)
Date: 2011-07-12 08:53 pm (UTC)(no subject)
Date: 2011-07-12 07:46 am (UTC)(no subject)
Date: 2011-07-12 08:54 pm (UTC)foo _ x = ..
foo y _ = ...
:)
(no subject)
Date: 2011-07-12 08:55 pm (UTC)Camlp4 хорош, но у меня до него все руки не доходят.
Re: о-о
Date: 2011-07-13 10:16 pm (UTC)Я сам отправил резюме, знакомых до приезда там не было, большинство интервью были по телефону.
Re: о-о
Date: 2011-07-25 03:40 pm (UTC)ребята интересные
в Германии, к сожалению, такого типа компаний практически нет
(no subject)
Date: 2011-07-12 03:29 pm (UTC)(no subject)
Date: 2011-07-12 08:55 pm (UTC)