dastapov: (Default)
[personal profile] dastapov
Сегодня я хочу прервать долгую тишину в эфире и написать объективное и непредвзятое сравнение отсталого и отстойного компилятора ocaml с современными и прогрессивными компиляторами для всех остальных языков.

Что же плохо в компиляторе ocaml (заодно пройдемся и по интерпретатору)?

Претензия номер раз: он не умеет компилировать проекты из нескольких файлов. Допустим, у нас есть foo.ml и bar.ml следующего содержания:

foo.ml
======
let foo = 42;;

bar.ml
======
let bar = Foo.foo + 1;;

let () =
    Printf.printf "bar = %d\n" bar;;


Попробуем поставить себя на место новичка в ocaml и скомпилировать их. Так как основной модуль у нас bar, компилируем его:
adept> ocamlopt bar.ml
File "bar.ml", line 1, characters 10-17:
Error: Unbound module Foo


Вопрос: что мешает компилятору немного проявить немного инициативы и найти у себя под носом модуль foo и скомпилировать его, раз уж его недостает? Вроде на дворе у нас 21-ый век ... Ну ладно, мы не гордые - укажем модуль вручную:
adept> ocamlopt bar.ml foo.ml
File "bar.ml", line 1, characters 10-17:
Error: Unbound module Foo


Увы и ах, компилятор надо кормить с ложечки, подавая файлы в правильном порядке - все зависимости должны быть указаны до модулей, которые их используют. Не компиляторово это дело - заниматься топологической сортировкой:
adept> ocamlopt foo.ml bar.ml 
adept> ./a.out
bar = 43


Ура! Заработало! Теперь разберемся с раздельной компиляцией.

Претензия номер два: раздельная компиляция сделана через зад.
Может, раз совместная компиляция модулей foo и bar - это такая проблема, стоит собирать их по отдельности, а потом линковать? А вот фиг - проще не будет!

adept> ocamlopt -c bar.ml
File "bar.ml", line 1, characters 10-17:
Error: Unbound module Foo


То есть, раздельную компиляцию нужно выполнять тоже с учетом топологической сортировки по зависимостям.

Кроме того, на пути заботливо разложены дополнительные грабли под названием cross-module inlining и присыпаны листиками. Давайте вручную опишем интерфейс модуля foo и скомпилируем все раздельно:
[2] adept> cat foo.mli
val foo : int;;
adept> ocamlopt -c foo.mli
adept> ocamlopt -c foo.ml
adept> ocamlopt -c bar.ml
adept> ocamlopt foo.cmx bar.cmx


Теперь вручную изменим реализацию "foo", не меняя интерфейс, напимер добавим туда неэкспортируемое определение "let boo = 666". Теперь скомпилируем модуль "foo" (только реализацию - ведь интерфейс-то мы не меняли) и перелинкуем программу:
adept> ocamlopt -c foo.ml
adept> ocamlopt foo.cmx bar.cmx
File "_none_", line 1, characters 0-1:
Error: Files bar.cmx and foo.cmx
       make inconsistent assumptions over implementation Foo


Казалось бы, что за нафиг? А все дело в том, что в процессе компиляции "bar.ml" компилятор увидел файл "foo.cmx" и подумал: "О! А почему бы не попробовать сделать cross-module inlining?". В результате в bar.cmx осталась ссылка на sha1(?) хеш foo.cmx. Именно это приводит к проблемам в линковке.

Однако, если перед вызовом "ocamlopt -c bar.ml" убрать foo.cmx куда-то в сторонку, а потом вернуть его обратно, все скомпилируется, cross-module inlining не выполнится и потом можно будет перекомпилировать foo.ml отдельно от всего остального и перелинковать программу без всяких проблем.

Что тут плохо:
1)Если сначала все собрать без inlining-а, а потом в какой-то момент перекомпилировать bar.ml, компилятор выполнит inlining

2)Нельзя получить хоть какую-то диагностику от компилятора о том, делается ли inlinining или нет

3)Нельзя запретить(!) inlining. Опция "inline=0", вопреки ожиданиям, не запрещает inlining, просто делает его очень "безинициативным"

Печальное следствие 1: если хочется для ускорения разработки наплевать на скорость скомпилированного кода и уменьшить количество пересобираемых модулей путем отключения inlining-а, надо очень сильно извращаться.

Печальное следствие 2: нет простого способа компилировать код на ocaml. Если писать для своей системы сборки правила вида "ocamlopt -o prog.exe foo.ml bar.ml baz.ml", то придется руками переставлять имена модулей туда-сюда при любом изменении графа зависимостей. А неявные правила (implicit rules) вида "%.cmx: %.ml" нельзя использовать без механизма обнаружения зависимоей (об этом - позже). Нельзя также наплевать на компиляцию вообще и сказать "да хрен с ним, проинтерпретируйте мой код" (об этом тоже подробнее будет позже).

Я подозреваю, что именно вот эта необходимость лепить нетривиальные правила сборки даже для самых тривиальных проектов отвратила от ocaml ооочень много людей.

Продолжение следует, у меня накипело :)
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

dastapov: (Default)
Dmitry Astapov

May 2022

M T W T F S S
       1
2345678
9101112131415
161718 19202122
23242526272829
3031     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags