Все-таки есть основания полагать, что Haskell местами излишне сложен и несколько оторван от действительности. Когда речь заходит о реальной разработке, возникает желание писать на языке попроще, где по умолчанию не используются ленивые вычисления , где при необходимости можно прибегнуть к ссылкам, наследованию, побочным эффектам и тп. И чтобы никаких матанов типа монад , аппликативных функторов , iteratees и застежек. Примерно как в OCaml.
Некоторые сведения об OCaml:
- Язык был разработан во французском институте INRIA в 1985 году и развивается по сей день;
- Это мультипарадигменный ( функциональный , императивный, объектно-ориентированный ) язык программирования общего назначения c автоматической сборкой мусора ;
- В OCaml используется cтрогая статическая типизация , предусмотрен автоматический вывод типов;
- Язык поддерживает лямбды, замыкания, каррирование, хвостовую рекурсию, паттерн матчинг и неизменяемые данные (таковыми они являются обычно);
- Также в OCaml есть указатели, циклы, изменяемые данные и функции с побочными эффектами;
- По умолчанию вычисление является строгим, но при необходимости можно использовать и ленивые вычисления или просто передавать функции в качестве аргументов;
- Программа на OCaml может интерпретироваться, компилироваться в байткод виртуальной машины (под названием Zinc) или в машинный код;
- Еще на OCaml можно писать под JVM , а также под .NET, где язык почему-то называется F# (шутка, но на самом деле есть и настоящий OCaml под .NET );
- Отступы в OCaml можно ставить как угодно;
- В плане производительности OCaml сравним с Java, Haskell и Go ;
Установка OCaml в Linux:
Также вам может захотеться установить менеджер пакетов opam. Готового deb-пакета на момент написания этих строк еще не было (это относительно молодой проект). Однако можно установить opam следующим образом:
&& sh . / install.sh
После установки говорим opam init
и следуем инструкциям. Затем читаем opam help
.
По аналогичной схеме и почти без танцев с бубнами OCaml устанавливается под FreeBSD. Также на официальном сайте языка доступны бинарные пакеты для Windows и MacOS .
В качестве своей первой программы на OCaml я решил написать генератор лабиринтов (ага, еще один ). Программа получилась довольно большой, поэтому здесь я приведу только некоторые ее части для демонстрации особенностей синтаксиса OCaml. Посмотреть исходный код полностью вы можете в этом архиве .
type cell = {
mutable move_up : bool ;
mutable move_left : bool ;
}
(* Лабиринт — это матрица ячеек *)
type maze = cell array array
Объявление типов в OCaml выглядит почти так же, как в Haskell. Ключевое слово mutable означает, что поле записи является изменяемым. Однострочных комментариев не предусмотрено, зато корректно обрабатываются вложенные комментарии.
let print_maze_row m y =
let width = maze_width m in
let top_str = String . create ( width * 2 )
and middle_str = String . create ( width * 2 ) in
for x = 0 to width — 1 do
let c = m . ( x ) . ( y ) in
let top_char = if c . move_up then ‘ ‘ else ‘-‘
and left_char = if c . move_left then ‘ ‘ else ‘|’ in
String . set top_str ( x * 2 ) ‘+’;
String . set top_str ( x * 2 + 1 ) top_char ;
String . set middle_str ( x * 2 ) left_char ;
String . set middle_str ( x * 2 + 1 ) ‘ ‘
done ;
print_string top_str ;
print_endline «+» ;
print_string middle_str ;
print_endline «|»
(* Вывод лабиринта *)
let print_maze m =
for y = 0 to maze_max_y m do
print_maze_row m y
done ;
for x = 0 to maze_max_x m do
print_string «+-»
done ;
print_endline «+»
В отличие от Haskell в OCaml строки — это не списки символов. К тому же, строки в OCaml являются изменяемыми. Функции в OCaml могут иметь побочные эффекты, наличие или отсутствие которых не отражается в типе функции. Обратите также внимание на использование циклов.
в лабиринте заданного размера *)
let not_visited_cells_list width height =
let lst = ref [ ] in
for y = height — 1 downto 0 do
for x = width — 1 downto 0 do
lst := ( x,y ) :: ! lst
done
done ;
! lst
Здесь lst — это указатель на список. Во вложенном цикле создается новая cons-ячейка с указателем на голову списка (восклицательный знак означает разыменование ссылки), а в lst записывается указатель на эту cons-ячейку. В интернетах пишут, что OCaml поддерживает генераторы списков, но, чтобы их получить, нужно произвести какие-то дополнительные телодвижения. В этом вопросе я пока не разобрался.
if width <= 0 then raise ( Invalid_argument «width» )
else if height <= 0 then raise ( Invalid_argument «height» )
else (* … *)
Так в OCaml бросаются исключения. А так они ловятся:
Hashtbl . find visited ( x,y )
with
Not_found -> false
Чтобы запустить программу, достаточно сказать:
Однако этим вы запустите интерпретатор, который по совместительству является и REPL-средой (которую для удобства я лично оборачиваю в rlwrap). Для компиляции программы нужно выполнить команду:
Но если вы посмотрите на полученный бинарник через hexdump, до заметите в нем подозрительную строчку:
… которая как бы намекает, что что-то здесь не так. И действительно, на самом деле мы скомпилировали программу в байткод виртуальной машины Zinc. Эта программа будет работать на любой машине, но только если на ней есть ocamlrun.
Чтобы получить нормальный бинарник, требуется выполнить команду:
В этом случае программа будет работать быстрее и не будет зависеть от наличия виртуальной машины, но за это придется заплатить переносимостью. Еще, как вариант, можно прицепить виртуальную машину к байткоду, сказав:
Но в этом случае мы не получаем ни скорости, ни переносимости. Не совсем понятно, зачем вообще нужна такая возможность.
Пример сгенерированного лабиринта:
| | | | | | | |
+ +-+ +-+ + + +-+ +-+ + +-+-+ + +-+-+ + + + + + + +-+ +-+ + +-+-+
| | | | | | | | | | | | | | | | | | |
+ +-+-+ + + +-+ +-+ + +-+ +-+ +-+ +-+ +-+-+-+ +-+ +-+ +-+-+-+ + +
| | | | | | | | | | | | | |
+ +-+ +-+ + + +-+ +-+ +-+ +-+-+ + +-+-+ +-+ +-+-+ +-+-+ + +-+ + +
| | | | | | | | | | | | | | | | | | | | |
+ + + +-+ +-+-+ +-+ + +-+ +-+ +-+ +-+-+ +-+ + + + + + +-+ +-+-+ +
| | | | | | | | | | | | | | | | | |
+ +-+-+ + + + +-+ +-+ + +-+-+ + +-+ + + + +-+-+-+ + +-+ +-+ +-+ +
| | | | | | | | | | | | | | | | | | | | |
+ +-+ + +-+-+-+-+ + +-+ +-+-+ +-+ + + + +-+ +-+ +-+ +-+ + + +-+-+
| | | | | | | | | | | | | | | | | | | | | | |
+ + +-+ + +-+-+ +-+ + +-+-+ + + +-+-+ +-+-+ + + + + + + +-+-+ +-+
| | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
В целом, язык мне очень понравился. Пусть OCaml по сравнению с Haskell выглядит несколько неказисто, зато мне он кажется куда более прагматичным. Несмотря на свою элегантность, в некоторых вопросах Haskell больно уж академичен. Например, если я хочу изменяемое состояние, Haskell предлагает мне подпорки в виде State и StateT. Нужен контролируемый ввод-вывод? Нет проблем, у нас есть другая подпорка — iteratees. Черт, я просто хочу читать из сокета и иметь изменяемое состояние!
Ставшая уже традиционной подборка ссылок:
- Справка по языку: http://caml.inria.fr/pub/docs/manual-ocaml/ ;
- Хорошие туториалы: http://mirror.ocamlcore.org/ocaml-tutorial.org/ ;
- Планета OCaml: http://planet.ocamlcore.org/ ;
- Книга Introduction to Objective Caml ;
- Книга Developing Applications With Objective Caml , есть незаконченный перевод на русский , а также проект по продолжению перевода ;
- Документация по OCaml на русском языке: http://ocaml.spb.ru/ ;
- Список рассылки caml-list: https://sympa.inria.fr/sympa/subscribe/caml-list ;
А вы что думаете об OCaml?
Дополнение: Также совсем недавно вышла книга Real World OCaml , которая, по всей видимости, намного актуальнее всех названных выше книг.
Дополнение: См также мою заметку про OCaml Batteries Included .