Сопоставление языков программирования Часть 8. Ocaml

Comments

Ocaml — ещё один объектно-ориентированный язык функционального программирования общего назначения (странное такое сочетание, но та же характеристика может быть отнесена и к обсуждавшемуся выше Scala, и, в каком-то смысле к Python). Язык разрабатывался с ориентацией на безопасность исполнения и надёжность программ. Утверждается, что этот язык имеет высокую степень выразительности, что позволяет его легко выучить и использовать. Язык CaML поддерживает функциональную, императивную и объектно-ориентированную парадигмы программирования. Ocaml разработан в 1996 году во французском институте INRIA, который занимается исследованиями в области информатики (авторы: Xavier Leroy, Jérôme Vouillon, Damien Doligez и Didier Rémy). Разработка сделана основываясь на языке CaML, существующем с 1985 года. Это самые распространённые в практической работе диалекты языка ML, одного из самых старых и известных функциональных языков, который, вообще-то говоря, имеет много самых разных реализаций.

Инструментарий Ocaml включает в себя интерпретатор, компилятор в байт-код и оптимизирующий компилятор в машинный код (по утверждению авторов, превосходящий по своим параметрам аналогичные компиляторы C/C++ для многих задач, особенно связанных с синтаксическим анализом и т. п.).

В дистрибутивах Linux представлено действительно много инструментальных средств, связанных с пакетом Ocaml:

$ aptitude search ocaml | grep ' ocaml' 
p   ocaml                           - реализация языка ML с объектной системой н 
v   ocaml-3.12.1                    -                                           
p   ocaml-base                      - Runtime system for OCaml bytecode executab 
v   ocaml-base-3.12.1               -                                           
p   ocaml-base-nox                  - Runtime system for OCaml bytecode executab 
v   ocaml-base-nox-3.12.1           -                                           
p   ocaml-batteries-included        - Batteries included: OCaml development plat 
v   ocaml-best-compilers            -                                           
p   ocaml-book-en                   - English book: "Developing applications wit 
p   ocaml-book-fr                   - French book: "Developpement d'applications 
p   ocaml-compiler-libs             - OCaml interpreter and standard libraries  
v   ocaml-compiler-libs-3.12.1      -                                           
p   ocaml-core                      - OCaml core tools (metapackage)            
p   ocaml-doc                       - Documentation for Objective Caml          
p   ocaml-findlib                   - management tool for OCaml libraries       
p   ocaml-findlib-wizard            - Makefile and META wizard for OCaml librari 
p   ocaml-interp                    - OCaml interactive interpreter and standard 
v   ocaml-interp-3.12.1             -                                           
p   ocaml-libs                      - OCaml core libraries (metapackage)        
p   ocaml-melt                      - LaTeX with OCaml (tools)                  
p   ocaml-mode                      - major mode for editing Objective Caml in E 
p   ocaml-native-compilers          - Native code compilers of the OCaml suite ( 
p   ocaml-nox                       - реализация языка ML с объектной системой н 
v   ocaml-nox-3.12.1                -                                           
p   ocaml-source                    - Sources for Objective Caml                
v   ocaml-source-3.12.1             -                                           
p   ocaml-tools                     - tools for OCaml developers                
p   ocaml-ulex                      - OCaml lexer generator with Unicode support 
v   ocaml-ulex-8nxh1                -                                           
p   ocaml-ulex08                    - OCaml lexer generator with Unicode support 
v   ocaml-ulex08-h2ns9              -                                           
p   ocamldsort                      - dependency sorter for OCaml source files  
p   ocamlduce                       - OCaml extended with XML types             
v   ocamlduce-3.12.1.0              -                                           
p   ocamlduce-base                  - OCaml extended with XML types (runtime)   
v   ocamlduce-base-3.12.1.0         -                                           
p   ocamlgraph-editor               - graphical graph editor based on hyperbolic 
p   ocamlify                        - include files in OCaml code               
p   ocamlmakefile                   - general makefile for the Objective Caml pr 
p   ocamlmod                        - generate OCaml modules from source files  
p   ocamlviz                        - real-time profiling tools for Objective Ca 
p   ocamlwc                         - count the lines of code and comments in OC 
p   ocamlweb                        - Literate programming tool for Objective Ca

Установка, которая также потянет довольно много по зависимостям:

$ aptitude install ocaml 
...
$ ls -w100 /usr/bin/*ocaml* 
/usr/bin/ocaml  /usr/bin/ocamlc.opt /usr/bin/ocamllex.opt /usr/bin/ocamloptp 
/usr/bin/ocamlbuild /usr/bin/ocamlcp /usr/bin/ocamlmklib /usr/bin/ocamlprof 
/usr/bin/ocamlbuild.byte /usr/bin/ocamldebug /usr/bin/ocamlmktop /usr/bin/ocamlrun 
/usr/bin/ocamlbuild.native /usr/bin/ocamldep /usr/bin/ocamlobjinfo /usr/bin/ocamlyacc 
/usr/bin/ocamlbyteinfo /usr/bin/ocamldep.opt  /usr/bin/ocamlopt 
/usr/bin/ocamlc /usr/bin/ocamllex /usr/bin/ocamlopt.opt

Ocaml, как и предыдущие рассмотренные языки, позволяет вести тестирование или отладку в режиме интерактивного консольного калькулятора:

$ ocaml 
        OCaml version 4.00.1 
# let pi=4.0*.atan 1.0;; 
val pi : float = 3.14159265358979312 
# let square x = x*.x;; 
val square : float -> float = <fun> 
# square(sin(pi))+.square(cos(pi));; 
- : float = 1. 
# ^D

(Операции «с точкой»: *. , +. — указывают на вещественный тип операции, целочисленные операции будут выглядеть по-другому: * , +). Во 2-м утверждении мы определили функцию square() как объект, что характерно для функционального программирования.

Специфической «фишкой» Ocaml является заточенность его средств на реализацию лексических анализаторов: с одной стороны, есть Ocaml-версии лексического анализатора Lex и синтаксического анализатора YACC, обрабатывающие LALR-языки с помощью автоматов с магазинной памятью, с другой — предопределенные типы потоков (символов и токенов) и операции сопоставления с образцом для потоков, облегчающие написание рекурсивных нисходящих анализаторов для LL-языков. Вычислительный сорт приложений, которые мы сравниваем, не подпадает под специфику Ocaml, но и они с успехом реализуются в языке.

В составе пакета Ocaml очень большое количество прикладных библиотек, смотрим их в /usr/lib/ocaml:

$ ls /usr/lib/ocaml/*.mli 
/usr/lib/ocaml/arg.mli  /usr/lib/ocaml/genlex.mli  /usr/lib/ocaml/printexc.mli 
/usr/lib/ocaml/arith_status.mli  /usr/lib/ocaml/graphics.mli /usr/lib/ocaml/printf.mli 
/usr/lib/ocaml/arrayLabels.mli  /usr/lib/ocaml/hashtbl.mli  /usr/lib/ocaml/queue.mli 
/usr/lib/ocaml/array.mli /usr/lib/ocaml/int32.mli /usr/lib/ocaml/random.mli 
/usr/lib/ocaml/bigarray.mli  /usr/lib/ocaml/int64.mli /usr/lib/ocaml/ratio.mli 
/usr/lib/ocaml/big_int.mli /usr/lib/ocaml/lazy.mli /usr/lib/ocaml/scanf.mli 
/usr/lib/ocaml/buffer.mli /usr/lib/ocaml/lexing.mli /usr/lib/ocaml/set.mli 
/usr/lib/ocaml/callback.mli /usr/lib/ocaml/listLabels.mli /usr/lib/ocaml/sort.mli 
/usr/lib/ocaml/camlinternalLazy.mli /usr/lib/ocaml/list.mli  /usr/lib/ocaml/stack.mli 
/usr/lib/ocaml/camlinternalMod.mli /usr/lib/ocaml/map.mli /usr/lib/ocaml/stdLabels.mli 
/usr/lib/ocaml/camlinternalOO.mli /usr/lib/ocaml/marshal.mli /usr/lib/ocaml/stream.mli 
/usr/lib/ocaml/char.mli /usr/lib/ocaml/moreLabels.mli  /usr/lib/ocaml/stringLabels.mli 
/usr/lib/ocaml/complex.mli /usr/lib/ocaml/mutex.mli /usr/lib/ocaml/string.mli 
/usr/lib/ocaml/condition.mli /usr/lib/ocaml/nativeint.mli /usr/lib/ocaml/str.mli 
/usr/lib/ocaml/digest.mli  /usr/lib/ocaml/nat.mli /usr/lib/ocaml/sys.mli 
/usr/lib/ocaml/dynlink.mli /usr/lib/ocaml/num.mli /usr/lib/ocaml/thread.mli 
/usr/lib/ocaml/event.mli /usr/lib/ocaml/obj.mli /usr/lib/ocaml/threadUnix.mli 
/usr/lib/ocaml/filename.mli /usr/lib/ocaml/oo.mli /usr/lib/ocaml/unixLabels.mli 
/usr/lib/ocaml/format.mli /usr/lib/ocaml/parsing.mli /usr/lib/ocaml/unix.mli 
/usr/lib/ocaml/gc.mli /usr/lib/ocaml/pervasives.mli /usr/lib/ocaml/weak.mli

Это интерфейсы модулей (.mli), доступные в текстовом виде для изучения. Важность этого источника информации заключается ещё в том, что вся документация, описания, книги по Ocaml — отвратительного качества. Объясняется это, скорее всего, неблагоприятным наложением целого ряда факторов:

  1. Документация Ocaml писалась на французском языке и давно, с неё делались не очень умелые переводы на английский, а уже с него — те рваные переводы и описания, которые доступны на русском.
  2. Ещё одна черта информационных источников по Ocaml, которая явно прослеживается, состоит в том, что написано это всё в академических, университетских кругах: много замысловатых конструкций и примеров, но нет систематического изложения и мало пригодно для практического использования.
  3. Время жизни этого языка (начиная с клона ML) достаточно велико, совместимостью между версиями авторы не озадачивались, а в документации не указываются ни версии, ни сроки написания — большинство примеров из документации и учебников просто не компилируется.

Из-за всех этих особенностей, порог начального вхождения в программирование Ocaml — высокий, и к этому нужно быть готовым. И именно из-за этого в этой части описания мы уделяем некоторое излишнее внимание таким рутинным вещам как инсталляция, библиотеки и подобные им.

Разнообразные способы использования стандартных модулей поставки (показанных выше) можно наблюдать в приведенном ниже листинге на примере использования API из модулей Complex и Format:

Листинг 5. Реализация задачи на функциональном языке Ocaml (файл triangle_ml.ml):
open Complex;; 

let print_point pt =                   (* вывод комплексного числа - координаты *) 
   print_string "["; 
   print_float pt.re; 
   print_string ","; 
   print_float pt.im; 
   print_string "] ";; 

let rec poligon shape n =                       (* ввод координат многоугольника *) 
   print_string "вершина №"; 
   print_int n; 
   print_string " : "; 
   Format.print_flush(); 
   try 
      let str = read_line() in 
      try 
         let pt = Scanf.sscanf str "%f %f" (fun x y -> { re=x; im=y }) in  
         let lst = pt :: shape in 
         poligon lst ( n + 1 ); 
      with float_of_string ->                   (* ошибка формата ввода*) 
         print_string "ошибка ввода!\n"; 
         poligon shape n; 
   with End_of_file ->                          (* ^D - конец списка вершин *) 
      shape;; 

let rec show shape =                            (* вывод координат многоугольника *) 
   match shape with 
   | [] -> 
     print_newline(); 
   | hd :: tl -> 
     print_point hd; 
     show tl;; 

let perimeter shape = 
   let dist p1 p2 = norm( sub p1 p2 ) in        (* расстояние между 2-мя точками*) 
   let rec add_line lst sum = 
      if List.tl( lst ) = [] then 
         dist (List.hd lst) (List.hd shape)     (* последняя точка *) 
      else 
         dist (List.hd lst) (List.hd( List.tl lst )) +. 
         add_line (List.tl lst ) sum            (* промежуточные точки *) 
   in                                           (* end add_line *) 
   add_line shape 0.0;;                         (* накапливающая сумма *) 

let square shape = 
   let triang p1 p2 = 
      let s1 = sub p1 ( List.hd shape ) in 
      let s2 = sub p2 ( List.hd shape ) in 
         norm( s1 ) *. norm( s2 ) *. abs_float( sin( ( arg( s1 ) -. 
         arg( s2 ) ) ) ) /. 2.0 
   in                                           (* end triang *) 
   let rec add_squa lst sum = 
      let squa = triang (List.hd lst) (List.hd( List.tl lst )) in 
      if List.tl( List.tl lst ) = [] then squa 
      else 
         squa +. add_squa (List.tl lst ) sum   
   in                                           (* end add_squa *) 
   add_squa (List.tl shape) 0.0;; 

let rec next() = 
   print_string "координаты вершин в формате: X Y (^D конец ввода)\n"; 
   let shape = poligon [] 1 in 
   begin 
      print_string "\rвершин "; 
      print_int( List.length shape ); 
      print_string " : "; 
      show shape ; 
      print_string "периметр = "; 
      print_float( perimeter shape ); 
      print_newline(); 
      print_string "площадь = "; 
      print_float( square shape ); 
      print_newline(); 
      print_string "---------------------------------\n"; 
      next();              (* рекурсивно организованный бесконечный цикл *) 
   end;; 

next();;

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

  • непосредственной интерпретацией кода:
    $ ocaml triangle_ml.ml
    координаты вершин в формате: X Y (^D конец ввода) 
    ...
  • компиляцией в байт-код с последующим его выполнением:
    $ ocamlc triangle_ml.ml -o triangle_ml 
    $ ocamlrun triangle_ml 
    координаты вершин в формате: X Y (^D конец ввода) 
    ...
  • оптимизирующей компиляцией в бинарный исполнимый формат (ELF в случае Linux) и его выполнение:
    $ ocamlopt triangle_ml.ml -o triangle_ml 
    $ ./triangle_ml 
    координаты вершин в формате: X Y (^D конец ввода) 
    вершина №1 : 1 1 
    вершина №2 : 1 2 
    вершина №3 : 2 1 
    вершин 3 : [2.,1.] [1.,2.] [1.,1.] 
    периметр = 3.41421356237 
    площадь = 0.5 
    --------------------------------- 
    координаты вершин в формате: X Y (^D конец ввода) 
    вершина №1 : 1 1 
    вершина №2 : 1 2 
    вершина №3 : 2 2 
    вершина №4 : 2 1 
    вершин 4 : [2.,1.] [2.,2.] [1.,2.] [1.,1.] 
    периметр = 4. 
    площадь = 1. 
    --------------------------------- 
    координаты вершин в формате: X Y (^D конец ввода) 
    вершин 0 : ^C

Обработка ошибок ввода: вы должны видеть что-то подобное следующему:

$ ocaml triangle_ml.ml 
координаты вершин в формате: X Y (^D конец ввода) 
вершина №1 : 1 2 
вершина №2 : 2 r 
ошибка ввода! 
вершина №2 : 5,5 6 
ошибка ввода! 
вершина №2 : 3 4 
...

Обсуждение

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

Другие рассмотренные интерпретирующие языки (Scheme, Scala, Ocaml, Haskell) демонстрируют тот рост интереса, который наметился в последние годы к парадигме функционального программирования. Для реализаций исполняющих систем таких языков уже стало характерным обеспечивать возможность использования их в режиме интерактивного консольного калькулятора. Это сильно снижает «порог начального вхождения» в инструмент, что, возможно, особенно важно для функционального программирования. Кроме того, такой режим позволяет использовать интерпретатор (в отдельном терминале) для тонкой отработки и тестирования синтаксических конструкций непосредственно по ходу разработки основного проекта.

Заключение

Аналогично тому, как это сделано в первой половине обзора, где рассмотрены самые используемые в коммерческих проектах языки программирования (C, C++, Java, Perl, Python, Ruby, JavaScript, PHP, Lua, bash) в этой, продолжающей тему, части обзор расширен на языки менее известные, или реже используемые в практических проектах. Причины тому разные: некоторые языки относительно новые (Go, Scala), другие хотя и со стажем, но более «модные» в академических кругах и в образовании (Lisp, Scheme), чем в практике разработки.

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

Приведены перечни ресурсов (документации, описаний) по каждому языку в объёме, вполне достаточном, чтобы начать писать свои собственные приложения.


Ресурсы для скачивания


Похожие темы

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=969253
ArticleTitle=Сопоставление языков программирования Часть 8. Ocaml
publish-date=04222014