Все-таки есть основания полагать, что 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:

sudo aptitude install ocaml-batteries-included ocaml-doc

Также вам может захотеться установить менеджер пакетов opam. Готового deb-пакета на момент написания этих строк еще не было (это относительно молодой проект). Однако можно установить opam следующим образом:

wget https: // raw.github.com / OCamlPro / opam / master / shell / install.sh
&& 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 поддерживает генераторы списков, но, чтобы их получить, нужно произвести какие-то дополнительные телодвижения. В этом вопросе я пока не разобрался.

let empty_maze width height =
if width <= 0 then raise ( Invalid_argument «width» )
else if height <= 0 then raise ( Invalid_argument «height» )
else (* … *)

Так в OCaml бросаются исключения. А так они ловятся:

try
Hashtbl . find visited ( x,y )
with
Not_found -> false

Чтобы запустить программу, достаточно сказать:

ocaml maze.ml

Однако этим вы запустите интерпретатор, который по совместительству является и REPL-средой (которую для удобства я лично оборачиваю в rlwrap). Для компиляции программы нужно выполнить команду:

ocaml maze.ml -o maze

Но если вы посмотрите на полученный бинарник через hexdump, до заметите в нем подозрительную строчку:

#!/usr/bin/ocamlrun

… которая как бы намекает, что что-то здесь не так. И действительно, на самом деле мы скомпилировали программу в байткод виртуальной машины Zinc. Эта программа будет работать на любой машине, но только если на ней есть ocamlrun.

Чтобы получить нормальный бинарник, требуется выполнить команду:

ocamlopt maze.ml -o maze

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

ocamlc -custom maze.ml -o maze

Но в этом случае мы не получаем ни скорости, ни переносимости. Не совсем понятно, зачем вообще нужна такая возможность.

Пример сгенерированного лабиринта:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |     |       |       | |     |                 |
+ +-+ +-+ + + +-+ +-+ + +-+-+ + +-+-+ + + + + + + +-+ +-+ + +-+-+
| |   |   | | |   | | |   |     |         | |   |   |   | |   | |
+ +-+-+ + + +-+ +-+ + +-+ +-+ +-+ +-+ +-+-+-+ +-+ +-+ +-+-+-+ + +
|   |   | |     | |         |   |   |   |       | |     |       |
+ +-+ +-+ + + +-+ +-+ +-+ +-+-+ + +-+-+ +-+ +-+-+ +-+-+ + +-+ + +
| | | |   | |   |   |   |   |   |     | |   | | | |     | |   | |
+ + + +-+ +-+-+ +-+ + +-+ +-+ +-+ +-+-+ +-+ + + + + + +-+ +-+-+ +
|   | | |     |   |   |     | |   | |   |   |     | |   | |     |
+ +-+-+ + + + +-+ +-+ + +-+-+ + +-+ + + + +-+-+-+ + +-+ +-+ +-+ +
| |   |   | | |   |   |     | |   | | | | | |     |   | |     | |
+ +-+ + +-+-+-+-+ + +-+ +-+-+ +-+ + + + +-+ +-+ +-+ +-+ + + +-+-+
| | |   |     |   | | |     | | |   | |     | | | | | | | | |   |
+ + +-+ + +-+-+ +-+ + +-+-+ + + +-+-+ +-+-+ + + + + + + +-+-+ +-+
|     |   |     |         | |   |         |   | |     |         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

В целом, язык мне очень понравился. Пусть OCaml по сравнению с Haskell выглядит несколько неказисто, зато мне он кажется куда более прагматичным. Несмотря на свою элегантность, в некоторых вопросах Haskell больно уж академичен. Например, если я хочу изменяемое состояние, Haskell предлагает мне подпорки в виде State и StateT. Нужен контролируемый ввод-вывод? Нет проблем, у нас есть другая подпорка — iteratees. Черт, я просто хочу читать из сокета и иметь изменяемое состояние!

Ставшая уже традиционной подборка ссылок:

А вы что думаете об OCaml?

Дополнение: Также совсем недавно вышла книга Real World OCaml , которая, по всей видимости, намного актуальнее всех названных выше книг.

Дополнение: См также мою заметку про OCaml Batteries Included .

EnglishRussianUkrainian