Тихо и незаметно вышел Ocaml 3.11. Несмотря на нумерацию релиз значимый. Изменения затронули компилятор, библиотеки и функции языка.
Некоторые главные изменения:
библиотека Dynlink тепрь доступна в нативных кодах для нескольких платформ.
Пишу на окамеле достаточно много и часто, в том числе и за деньги. В основном использую как язык прототипирования задачек, связанных с разного рода трансляторами и генераторами для формальных языков. Язык в целом неплохой: простой, достаточно популярный, высокоуровневый и шустрый. Хорошо подходит для задачек, где не требуется пресловутый enterprise :) Но синтаксис имеет существенный крен в сторону write-only.
Ну вот например, что под руку попалось непосредственно из исходников ocaml-а. Это конечно выглядит лучше чем однострочник на perl-е, но откровенно говоря особого удовольствия от копания в этом я не испытываю :)
let proceed () =
Hooks.call_hook Hooks.Before_options;
Options.init ();
if !Options.must_clean then clean ();
Hooks.call_hook Hooks.After_options;
Plugin.execute_plugin_if_needed ();
if !Options.targets = []
&& !Options.show_tags = []
&& not !Options.show_documentation
then raise Exit_silently;
let target_dirs = List.union [] (List.map Pathname.dirname !Options.targets) in
Configuration.parse_string
"<**/*.ml> or <**/*.mli> or <**/*.mlpack> or <**/*.ml.depends>: ocaml
<**/*.byte>: ocaml, byte, program
<**/*.odoc>: ocaml, doc
<**/*.native>: ocaml, native, program
<**/*.cma>: ocaml, byte, library
<**/*.cmxa>: ocaml, native, library
<**/*.cmo>: ocaml, byte
<**/*.cmi>: ocaml, byte, native
<**/*.cmx>: ocaml, native
";
Configuration.tag_any !Options.tags;
if !Options.recursive
|| Sys.file_exists (* authorized since we're not in build *) "_tags"
|| Sys.file_exists (* authorized since we're not in build *) "myocamlbuild.ml"
then Configuration.tag_any ["traverse"];
let newpwd = Sys.getcwd () in
Sys.chdir Pathname.pwd;
let entry_include_dirs = ref [] in
let entry =
Slurp.filter
begin fun path name _ ->
let dir =
if path = Filename.current_dir_name then
None
else
Some path
in
let path_name = path/name in
if name = "_tags" then
ignore (Configuration.parse_file ?dir path_name);
(String.length name > 0 && name.[0] <> '_' && name <> !Options.build_dir && not (List.mem name !Options.exclude_dirs))
&& begin
if path_name <> Filename.current_dir_name && Pathname.is_directory path_name then
let tags = tags_of_pathname path_name in
if Tags.mem "include" tags
|| List.mem path_name !Options.include_dirs then
(entry_include_dirs := path_name :: !entry_include_dirs; true)
else
Tags.mem "traverse" tags
|| List.exists (Pathname.is_prefix path_name) !Options.include_dirs
|| List.exists (Pathname.is_prefix path_name) target_dirs
else true
end
end
(Slurp.slurp Filename.current_dir_name)
in
Hooks.call_hook Hooks.Before_hygiene;
let hygiene_entry =
Slurp.map begin fun path name () ->
let tags = tags_of_pathname (path/name) in
not (Tags.mem "not_hygienic" tags) && not (Tags.mem "precious" tags)
end entry in
if !Options.hygiene then
Fda.inspect hygiene_entry
else
Slurp.force hygiene_entry;
let entry = hygiene_entry in
Hooks.call_hook Hooks.After_hygiene;
Options.include_dirs := Pathname.current_dir_name :: List.rev !entry_include_dirs;
dprintf 3 "include directories are:@ %a" print_string_list !Options.include_dirs;
Options.entry := Some entry;
List.iter Configuration.parse_string !Options.tag_lines;
Hooks.call_hook Hooks.Before_rules;
Ocaml_specific.init ();
Hooks.call_hook Hooks.After_rules;
Sys.chdir newpwd;
(*let () = dprintf 0 "source_dir_path_set:@ %a" StringSet.print source_dir_path_set*)
if !Options.show_documentation then begin
show_documentation ();
raise Exit_silently
end;
Digest_cache.init ();
Sys.catch_break true;
show_tags ();
let targets =
List.map begin fun starget ->
let starget = Resource.import starget in
let target = path_and_context_of_string starget in
let ext = Pathname.get_extension starget in
(target, starget, ext)
end !Options.targets in
try
let targets =
List.map begin fun (target, starget, ext) ->
Shell.mkdir_p (Pathname.dirname starget);
let target = Solver.solve_target starget target in
(target, ext)
end targets in
Command.dump_parallel_stats ();
Log.finish ();
Shell.chdir Pathname.pwd;
let call spec = sys_command (Command.string_of_command_spec spec) in
let cmds =
List.fold_right begin fun (target, ext) acc ->
let cmd = !Options.build_dir/target in
let link x =
if !Options.make_links then ignore (call (S [A"ln"; A"-sf"; P x; A Pathname.current_dir_name])) in
match ext with
| "byte" | "native" | "top" ->
link cmd; cmd :: acc
| "html" ->
link (Pathname.dirname cmd); acc
| _ ->
if !Options.program_to_execute then
eprintf "Warning: Won't execute %s whose extension is neither .byte nor .native" cmd;
acc
end targets [] in
if !Options.program_to_execute then
begin
match List.rev cmds with
| [] -> raise (Exit_usage "Using -- requires one target");
| cmd :: rest ->
if rest <> [] then dprintf 0 "Warning: Using -- only run the last target";
let cmd_spec = S [P cmd; atomize !Options.program_args] in
dprintf 3 "Running the user command:@ %a" Pathname.print cmd;
raise (Exit_with_code (call cmd_spec)) (* Exit with the exit code of the called command *)
end
else
()
with
| Ocaml_dependencies.Circular_dependencies(seen, p) ->
raise
(Exit_build_error
(sbprintf "@[<2>Circular dependencies: %S already seen in@ %a@]@." p pp_l seen))
;;
Не так уж и кошмарен этот код, никаких запутанных конструкций, читается достаточно легко (особенно в emacs с tuareg-mode). Правда, это плохой код, типичная "простыня", отчаянно нуждающаяся в рефакторинге на более мелкие функции. Но я бы никогда такое не назвал "write only", и не думаю, что ocaml сам по себе провоцирует на подобный стиль. Я видел до фига подобного кода и на Паскале, и даже на VB.NET.
В "ынтырпрайзе" таких задач большинство. Поскольку там не с числами работают, а с логическими структурами.
Ну да ты ж быдлокодер без мозга, тебе не понять. И ссылки на JSC и CITRIX ты проигнорируешь, поскольку тупой. Жаба съела твой и без того крошечный мозг, так что не напрягайся, всё равно ни хрена не поймёшь.
Лично мне не нравится остуствие закрывающих "тегов" у основных конструкций: let ... in ...; try ... with ...; match ... with ...; if ... then ... else ... и т.д.
В совокупности с ";" они выглядят как единый монолитный кусок текста без структурирования. К тому же провоцируют писать в строчку. Что есть плохо для читабельности.
Хотелось бы что-то вроде
let
x = ...,
y = ...
in
...
match x with
a -> {
if ... then
...
else
...
end
...
}
b -> { ... }
c -> { ... }
end
...
end
Или хотя бы синтаксически нагруженных отступов как в питоне. Но отступы имхо хуже.
Мне больше синтаксис SML или Felix нравятся.
Как я и предсказывал - жывотное ссылки проигнорировало. На то оно и жывотное. Среди жабабыдлокодеров очень редко попадаются экземпляры, хоть немного похожие на людей - способные к системному мышлению, знающие, что такое логика. Ты не из их числа, жывотное.
> Даже если функциональный язык перенесен на машину и операционную систему, имеющиеся в распоряжении, он может быть неудобным для использования. Например, типичная реакция пользователей Glasgow Haskell - его установка была "приключением".
В /dev/null подобную чушь. Если язык подходит под задачу лучше других, остановить его внедрение может только наличие программеров, способных на нем программить. Сразу стоит задуматься, сколько в мире императивщиков, сколько функциональщиков, способных начать программировать на функциональных языках. Это наверняка самый серьезный сдерживающий фактор. Другой фактор - банальная лень. Я знаю язык XXX и мне не нужен язык YYY. Причем такое часто идет от лидов. В общем эти сдерживающие факторы не имеют ничего общего с тем, насколько хорош язык.
> Или хотя бы синтаксически нагруженных отступов как в питоне. Но отступы имхо хуже. Мне больше синтаксис SML или Felix нравятся. Burbaka ** (*) (12.12.2008 17:05:12)
Haskell - либо отступы, либо фигурные скобки (по желанию)
> В совокупности с ";" они выглядят как единый монолитный кусок текста без структурирования. К тому же провоцируют писать в строчку.
Есть такое дело. Но и это не write only - достаточно расставить отступы, и всё становится понятным.
> Хотелось бы что-то вроде
Для let ... in ... концовки не нужно, а вот для match да, не помешало бы. Я в сомнительных случаях просто использую банальные скобки (да, да, тяжелое детство, lisp).
> Хотелось бы что-то вроде
эээ....
let x = ...
and y = ...
in
...
let res = match x with
a -> if ...
then (...)
else (...)
| b -> ...
| c -> ...
in
...
res
;;
что не так?
> ну и? простыня. показать такую на жабе/питоне/лиспе/etc?
Я сравнивал. Если абстрагироваться от возможностей самого языка, то с нормальной идентацией жабий и тем более питоний синтаксис будет читабельнее. Может это вопрос привычки ? хз.
В основном не так, то что на вложенность блоков кода визуализируется плохо. Не в данном примере, а вообще. На простеньких "факториальных" программках сaml смотрится достаточно хорошо.
Еще неудобно то, что синтаксис провоцирует (по крайней мере меня) на длинные "однострочники". Особенно когда используешь функции типа map, fold_*, exist, for_all и т.п.
> Еще неудобно то, что синтаксис провоцирует (по крайней мере меня) на длинные "однострочники". Особенно когда используешь функции типа map, fold_*, exist, for_all и т.п
эээ...
1. стрелки?
2. ставить ентеры?
3. активнее определять функции?
как это относитсяк языку?
вот если бы вы вспомнили про те же конструкторы, которые нельзя использовать без аргументов (в отличии от хаселя), я бы понял, а так... «толсто»...
Вообще, главной проблемой любого языка является то, что он не C. У C, конечно, этой пробемы нет, что очень выгодно отличает его от любого другого языка.
Бывает :) Но ведь это личное осучение :))))
Вот что сами чуваки с James Street пишут по поводу читаемости окамля:
Given the role of code review to our company, readability was an important metric. We eventually came to the conclusion that OCaml was considerably easier to read and review than languages such as VB, C# and Java, for a number of reasons.