OCaml

Материал из Википедии — свободной энциклопедии
Перейти к навигацииПерейти к поиску
OCaml
Изображение логотипа
Класс языкаобъектно-ориентированный, функциональный, мультипарадигмальный, императивный, диалект и свободное и открытое программное обеспечение
Появился в1996
АвторКсавье Лерой и Damien Doligez[вд]
РазработчикINRIA
Расширение файлов.ml или.mli
Выпуск
Испытал влияниеSML
Лицензияпубличная лицензия Q и LGPL-2.1[вд]
Сайтocaml.org (англ.)
ОСUnix-подобная операционная система[2]
Логотип Викисклада Медиафайлы на Викискладе

OCaml (Objective Caml) —объектно-ориентированныйязык функционального программирования общего назначения, самый распространённый в практической работе диалект языкаML. Был разработан с учётом безопасности исполнения и надёжности программ. Поддерживает функциональную, императивную и объектно-ориентированную парадигмы программирования.

Появился в1996 году под названием Objective Caml, когда Дидье Реми (Didier Rémy) и Жером Вуйон (Jérôme Vouillon) реализовали поддержку объектно-ориентированного программирования для языка Caml, первоначально разработанного во французском институтеINRIA. Официально переименован в OCaml в 2011 году[3].

Инструментарий OCaml включает в себяинтерпретатор,компилятор вбайткод и оптимизирующий компилятор вмашинный код, сравнимый по эффективности сJava и лишь немного уступающий по быстродействиюC иC++[4].

На языке, в частности, написанрендерингформулВикипедии, использующих тег <math>, файлообменный клиентMLDonkey, стек управления гипервизоромXen xapi (является частью Xen Server/Xen Cloud Platform), язык программированияHaxe.

Содержание

Роль в информатике

[править |править код]

Является языком программирования общего назначения, но при этом имеет свои сложившиеся области применения[5].

Во-первых, это — создание «безопасных» (не только в смысле информационной безопасности) приложений. В языке используется сборка мусора, а большинство типов данных является ссылочным (англ. boxed), что означает предотвращениепереполнения буферов во время исполнения программы. Кроме того, статическая типизация и проверки времени компиляции делают невозможными некоторые другие классы ошибок, такие, как ошибкиприведения типов в силу отсутствия автоматического приведения типов. Кроме того, код может бытьформально верифицирован. Имеются утилиты автоматического доказательства типовой корректности кода, превосходящие таковые для большинства языков программирования. И что немаловажно, меры безопасности не влияют на эффективность исполняемого кода[5].

Другой областью успешного применения OCaml являются приложения,управляемые данными (data-driven). К этой области относится обработка текста, а также написание компиляторов. OCaml имеет не только средства для текстовой обработки (какими славится, например,Perl илиAWK), но и инструменты для глубокого семантического анализа и преобразования текста, что делает OCaml применимым в задачахинтеллектуального анализа данных (англ. data mining)[5].

OCaml, как и другие диалекты ML, используются в исследовательских задачах и задачах верификации, при котором основной код пишется на некотором языке программирования, а затем формально верифицируется и анализируется программой на OCaml[5]. Например, на OCaml написана система интерактивного доказательства теоремCoq.

Занимает особое место среди языков программирования благодаря сочетанию эффективности, выразительности и практичности. Среди особенностей языка, развивавшихся в течение более чем 40 лет, со времени созданияML, выделяют следующие[6]:

История

[править |править код]

OCaml ведёт своё происхождение от ML (англ. meta language), который был реализован на диалектеЛиспаРобином Милнером в1972 году в качестве программного средства для доказательства теорем, как метаязык логики вычислимых функций (LCF,англ. logic for computable functions). Позднее был сделанкомпилятор, а к1980 году ML стал полноценной системой программирования[7].

Ги Кузино (Guy Cousineau) добавил в язык алгебраические типы данных и сопоставление с образцом и определил ML в видекатегориальной абстрактной машины (CAM). Таким образом, CAM-ML мог быть описан, верифицирован и оптимизирован, что явилось шагом вперёд для ML[8].

Дальнейшим развитием был созданный к1987 году Аскандером Суарецом (Ascánder Suárez) и продолженный Пьером Вейсом (Pierre Weis) и Мичелом Мони (Michel Mauny) языкCaml (переигранное CAM-ML)[7][8].

В1990 году Ксавье Лерой (Xavier Leroy) и Дамьен Долигез (Damien Doligez) выпустили новую реализацию, названнуюCaml Light. В этой реализации наСи использовалсяинтерпретаторбайт-кода и быстрый сборщик мусора. С написанием библиотек язык стал использоваться в образовании и исследовательских институтах[7][8].

В1995 году увидел светCaml Special Light, развиваемый К. Лероем. Система программирования получила компилятор в машинные коды, что поставило эффективность исполняемого кода в один ряд с другими компилируемыми языками. В то же время была разработанасистема модулей, идея которой была заимствована изStandard ML[7].

В современном виде OCaml появился в1996 году, когда Дидье Реми (Didier Rémy) и Джером Вуйон (Jérôme Vouillon) реализовали для языка стройную и эффективную поддержкуобъектов. Эта объектная система позволяет на этапе компиляции втипобезопасной манере использовать идиомыобъектно-ориентированного программирования, без свойственныхC++ иJava провероквремени выполнения[7].

В2000-х годах язык плавно развивался, одновременно получая всё большее признание в коммерческих проектах и образовании. Среди разработанного в это время можно отметить полиморфные методы и вариантные типы, именованные и необязательные параметры, модулипервого класса,обобщённые алгебраические типы данных (GADT). Язык стал поддерживать несколько аппаратных платформ (X86,ARM,SPARC,PowerPC)[7][8].

Базовая семантика

[править |править код]

Модель вычислений OCaml как языка функционального программирования строится на трёх основных конструкцияхлямбда-исчисления: переменных, определениях функций и применении функции к аргументам[9].

Переменные

[править |править код]

Переменная — идентификатор, значение которого связано с определённой величиной. Имена переменных начинаются со строчной буквы или подчёркивания. Привязка обычно выполняется с помощью ключевого словаlet, как в следующем примере в интерактивной оболочке[10]:

letv=1;;

Переменные имеютобласть видимости. Например, в интерактивной оболочке переменную можно использовать в следующих за её привязкой командах. Аналогично, переменную, определённую в модуле, можно использовать после определения в данном модуле[10].

Привязка переменной может быть осуществлена и в области видимости, заданной конструкцией let-in, как в следующем примере по вычислению площади круга по радиусу:

#letarearadius=letpi=3.14inradius*.radius*.pi;;valarea:float->float=<fun>#area2.0;;-:float=12.56

В OCaml привязки переменных являются неизменяемыми (как в математических уравнениях), то есть, значение переменной «присваивается» только один раз (единичное присваивание). Другое дело, что внутри let-in может быть другой let-in, в котором вводится другая переменная, которая может «затенить» первую[10].

Функции

[править |править код]

Для определения функций в OCaml есть несколько синтаксических конструкций.

Функции можно определить с помощью ключевого словаfunction. Выражение для функции выглядит следующим образом[11]:

functionx->x+1

В данном случаефункция анонимная, и её можно использовать в качестве параметров других функций или применить к некоторому аргументу, например:

(functionx->x+1)5

Типом этой функции являетсяint -> int, то есть, функция принимаетцелое и возвращает целое.

Функция может иметь несколько аргументов[12]:

function(x,y)->x-y

В этом примере её тип:int * int -> int, то есть, на входе функции — пара, а на выходе — целое.

Есть и другой подход представления функций нескольких аргументов — преобразованиеN-арной функции в N функций одного аргумента —каррирование. Следующие два вида записи функции, вычисляющей произведение целочисленных аргументов, эквивалентны[12]:

functionx->functiony->x*yfunxy->x*y

Именованные функции можно получить, связав переменную с функцией[11]. Определение именованной функции настолько частая операция, что имеет отдельную синтаксическую поддержку. Следующие три записи — эквивалентные способы определить функцию (в интерактивной оболочке):

#letprod=functionx->functiony->x*y;;valprod:int->int->int=<fun>#letprodxy=x*y;;valprod:int->int->int=<fun>#letprod=funxy->x*y;;valprod:int->int->int=<fun>

Функции двух аргументов можно определить для использованияинфиксной записи[11]:

#let(^^)xy=x**2.0+.y**2.0;;val(^^):float->float->float=<fun>#2.0^^3.0;;-:float=13.#(^^)2.03.0;;-:float=13.

В этом примере определена функция(^^), вычисляющая сумму квадратов двухчисел с плавающей запятой. Последние два вида записи эквивалентны.

Рекурсивные функции, то есть функции, ссылающиеся на своё же определение, можно задать с помощьюlet rec[11]:

#letrecfacn=matchnwith|0->1|x->x*fac(x-1);;

В этом же примере вычисленияфакториала применено сопоставление с образцом (конструкцияmatch-with).

Аргументы функции можно определить как именованные. Именованные аргументы можно указывать в любом порядке[11]:

#letdivmod~x~y=(x/y,xmody);;valdivmod:x:int->y:int->int*int=<fun>#divmod~x:4~y:3;;-:int*int=(1,1)#divmod~y:3~x:4;;-:int*int=(1,1)

В OCaml можно опускать значения, используя уплотнённую запись (англ. label punning), если имя параметра и переменной совпадают[11]:

#letx=4inlety=3indivmod~x~y;;-:int*int=(1,1)

Выражения

[править |править код]

Ассоциативность операций в выражениях OCaml определяется префиксом, распространяясь таким образом на операции, определённые пользователем. Знак- работает и как префиксная, и как инфиксная операция, причём при необходимости использовать в качестве префикса совместно с применением функции параметр нужно заключить в скобки[13].

Префикс операцииАссоциативность
!?~Префикс
. .( .[ .{
применение функции, конструктора, метки,assert,lazyЛевая
- -.Префикс
** lsl lsr asrПравая
* / % mod land lor lxorЛевая
+ -Левая
::Правая
@ ^Правая
& $ !=Левая
&&&Правая
or||Правая
,
<-:=Правая
if
;Правая
letmatchfunfunctiontry

Система типов

[править |править код]

Примитивные типы

[править |править код]

Язык OCaml имеет несколькопримитивных типов: числовые типы (целый и числа с плавающей запятой),символьный,строки символов,булевый[14].

Целый тип представляет целые числа из интервала [−230, 230 − 1] и [−262, 262 − 1] для 32- и 64-битных архитектур соответственно. С целыми числами можно производить обычные операции сложения, вычитания, умножения, деления, взятияостатка от деления:+,-,*,/,mod. В случае выхода результата за допустимый интервал ошибки не происходит, а результат вычисляется по модулю границы интервала[15].

Числа с плавающей запятой представляются 53-битноймантиссой и порядком из интервала [−1022, 1023], следуя стандартуIEEE 754 для чисел с двойной точностью. В операциях эти числа нельзя смешивать с целыми. Кроме того, операции над числами с плавающей запятой синтаксически отличаются от целочисленных операций:+.,-.,*.,/.. Также имеется операция возведения в степень:**. Для преобразования целых чисел в числа с плавающей запятой и обратно доступны функции: float_of_int и int_of_float[15].

Для чисел с плавающей запятой имеются и другие математические функции:тригонометрические (sin, cos, tan, asin, acos, atan), округления (ceil, floor), экспоненциальная (exp), логарифмические (log, log10), а также извлечение квадратного корня (sqrt)[15]. Для числовых типов имеются и полиморфные операции сравнения[15].

Символьный тип — char — соответствует представлению символа с кодом от 0 до 255 (первые 128 символов совпадают сASCII).Строчный тип — string — последовательность символов (максимальная длина: 224 — 6)[16]. Пример с использованием функции преобразования целого к строке и операцииконкатенации:

#"Example "^string_of_int(2);;-:string="Example 2"

Булевый тип имеет два значения:true (истина) иfalse (ложь). Операции над величинами булевого типа:унарнаяnot (отрицание), бинарные:&& (и),|| (или). Бинарные операции вычисляют сначала левый аргумент, а правый — только если требуется[17].

Булевые значения получаются в результате сравнений:= (структурное равенство),== (тождество),<> (отрицание структурного равенства),!= (отрицание тождества),<,>,<=,>=. Для примитивных типов кроме строк и чисел с плавающей точкой структурное равенство и тождество совпадают, для других типов тождественными считаются значения, располагающиеся по одному адресу в памяти, а при структурном сравнении значения проверяются покомпонентно[17].

Кроме того, в OCaml имеется специальный тип unit, который имеет всего одно значение —()[17].

Списки

[править |править код]

В OCamlсписок — конечная неизменяемая последовательность элементов одного типа, реализованная как односвязный список. Следующий пример демонстрирует синтаксис списка[18]:

#['a';'b';'c'];;-:charlist=['a';'b';'c']#'a'::('b'::('c'::[]));;-:charlist=['a';'b';'c']#'a'::'b'::'c'::[];;-:charlist=['a';'b';'c']#[];;-:'alist=[]

Операция:: позволяет построить список на основе нового элемента и хвоста старого списка. При этом «старый» список не изменяется:

#letlst=[1;2];;vallst:intlist=[1;2]#letlst1=0::lst;;vallst1:intlist=[0;1;2]#lst;;-:intlist=[1;2]#lst1;;-:intlist=[0;1;2]
Пример: вычисление суммы элементов списка
[править |править код]

Список является одним из основных типов данных в OCaml. Следующий пример кода определяет рекурсивную (обратите внимание на ключевое слово rec) функцию, которая перебирает элементы данного списка и возвращает их сумму:

letrecsumxs=matchxswith|[]->0|x::xs'->x+sumxs'
 # sum [1;2;3;4;5];; - : int = 15

Другой способ подсчёта суммы заключается в использовании функции свёртки:

letsumxs=List.fold_left(+)0xs#sum[1;2;3;4;5];;-:int=15

Записи

[править |править код]

Записи являются важным элементом в системе типов OCaml. Запись представляет собой набор хранимых вместе значений, при котором каждый элемент значения-записи доступен по своему имени — имени поля записи. Пример описания типа, связывания записи с переменной и доступ к полю записи[19]:

#typeuser={login:string;password:string;nick:string;};;#letusr={login="myuser";password="secret";nick="aka";};;valusr:user={login="myuser";password="secret";nick="aka"}#usr.nick;;-:string="aka"

Следует заметить, что тип переменной usr был установлен компилятором автоматически.

Как и в случае с другими типами, тип может быть параметризован. Другие возможности записей[19]:

  • сопоставление с образцом (irrefutable)
  • синтаксические уплотнение полей (field punning) записи в случае совпадения имён полей и переменных
  • повторное использование полей и разрешение неоднозначности с помощью модулей
  • функциональное обновление (functional update) записи
  • изменяемые (mutable) поля
  • fieldslib и поле записи какобъект первого класса
  • итераторы полей

Вариантный тип

[править |править код]

Вариантный тип представляет данные, которые могут принимать различные формы, определяемые явно заданными метками. В следующем примере определён тип для базовых цветов[20]:

#typemain_color=Red|Green|Blue;;#Blue;;-:main_color=Blue#(Red,Blue);;-:main_color*main_color=(Red,Blue)

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

#typecolor_scheme=RGBofint*int*int|CMYKoffloat*float*float*float;;typecolor_scheme=RGBofint*int*int|CMYKoffloat*float*float*float

При определении функций вариантный тип естественно сочетается с сопоставлением с образцом.

Объекты

[править |править код]

В OCaml объекты и их типы полностью отделены от системыклассов. Классы используются для построения объектов и поддержкинаследования, но не являются типами объектов. Объекты имеют собственныеобъектные типы (object types), и для работы с объектами классы применять необязательно. Объекты не так часто используются в OCaml (так, система модулей является более выразительной, чем объекты, так как модули могут включать типы, а классы и объекты — нет). Основным преимуществом объектов перед записями — они не требуют объявления типов и обладают большей гибкостью благодаря полиморфизму строчных переменных (англ. row polymorphism). С другой стороны, преимущества объектов проявляются при использовании системы классов. В отличие от модулей, классы поддерживают позднее связывание, что позволяет ссылаться на методы объекта без статически заданной реализации и использовать открытую рекурсию (в случае с модулями можно использовать функции и функторы, но синтаксически такие описания требуют написания большего количества кода)[21].

Вывод типов

[править |править код]

Хотя OCaml являетсяязыком программирования с сильной типизацией, системавывода типов (англ. type inference) позволяет определять тип выражения на основе имеющейся информации о его компонентах. В следующем примере функции проверки числа на чётность не указано ни одной декларации типа, и тем не менее у компилятора языка есть полная информация о типе функции[22]:

#letoddx=xmod2<>0;;valodd:int->bool=<fun>

Императивное программирование и функции с побочными эффектами

[править |править код]

Помимо функциональных, язык содержит средстваимперативного программирования: функции спобочными эффектами, изменяемые (mutable) данные, императивные синтаксические конструкции, в частности, явныециклыwhile иfor[23].

Следующий пример напечатает настандартном выводе (это — побочный эффект функции printf) 11 строк:

fori=0to10doPrintf.printf"i =%d\n"idone;;

В следующем (довольно искусственном) примере элементы массива на месте увеличиваются на единицу в цикле с предусловием. Для индекса массива используется ссылка (ref), которая инкрементируется в теле цикла:

#letincr_arar=leti=ref0inwhile!i<Array.lengthardoar.(!i)<-ar.(!i)+1;incridone;;valincr_ar:intarray->unit=<fun>#letnums=[|1;2;3;4;5|];;valnums:intarray=[|1;2;3;4;5|]#incr_arnums;;-:unit=()#nums;;-:intarray=[|2;3;4;5;6|]

Побочные эффекты позволяют оптимизировать вычисления, в особенности, когда речь идёт о значительных преобразованиях на больших массивах данных. Также с их помощью реализуютсяленивые вычисления имемоизация[23].

Крупномасштабное программирование

[править |править код]

Модульность

[править |править код]

Основная статья:Язык модулей ML

OCaml можно представить себе как состоящий из двух языков: язык ядра со значениями и типами и язык модулей и ихсигнатур. Эти языки образуют два слоя в том смысле, что модули могут содержать типы и значения, а обычные значения не могут содержать модулей и модулей-типов. Тем не менее, OCaml предлагает механизм модулейпервого класса, которые могут быть значениями и при необходимости преобразуются в обычные модули и обратно[24].

Функторы

[править |править код]
Основная статья:Функтор (модуль)

Система модулей OCaml не ограничивается модульной организацией кода и интерфейсами. Одними из важных инструментовобобщённого программирования являютсяфункторы. Упрощённо говоря, функторы являются функцией из модуля в модули, что позволяет реализовать следующие механизмы[25]:

Примеры программ

[править |править код]

Запуск интерпретатора OCaml

[править |править код]

Для запуска интерпретатора языка OCaml необходимо в консоли ввести следующую команду:

$ocamlOCamlversion4.08.1#

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

#1+2*3;;-:int=7

Hello world

[править |править код]

Следующая программа «hello.ml»:

print_endline"Hello World!";;

может быть скомпилирована либо вбайт-код:

$ocamlchello.ml-ohello

либо в оптимизированныймашинный код:

$ocamlopthello.ml-ohello

и запущена:

$./helloHelloWorld!$

Быстрая сортировка

[править |править код]

В следующем примере приведён алгоритмбыстрой сортировки, который сортирует список в порядке возрастания:

letrecqsort=function|[]->[]|pivot::rest->letis_lessx=x<pivotinletleft,right=List.partitionis_lessrestinqsortleft@[pivot]@qsortright

Последовательность Фибоначчи

[править |править код]
letrecfib_auxnab=matchnwith|0->a|_->fib_aux(n-1)(a+b)aletfibn=fib_auxn01

См. также

[править |править код]

Примечания

[править |править код]
  1. https://ocaml.org/releases/5.4.0
  2. https://ocaml.org/docs/up-and-running
  3. A History of OCaml . Дата обращения: 22 апреля 2019. Архивировано 1 сентября 2015 года.
  4. Minsky, 2011.
  5. 1234Smith, 2006, p. 2—3.
  6. Minsky, Madhavapeddy, Hickey, 2013, Why OCaml?.
  7. 123456Minsky, Madhavapeddy, Hickey, 2013, A Brief History.
  8. 1234Smith, 2006, p. 3—4.
  9. Chailloux, Manoury, Pagano — Developing with OCaml, 2007, p. 11—12.
  10. 123Minsky, Madhavapeddy, Hickey, 2013, Variables.
  11. 123456Minsky, Madhavapeddy, Hickey, 2013, Functions.
  12. 12Chailloux, Manoury, Pagano — Developing with OCaml, 2007, p. 23.
  13. OCaml manual: 6.7 Expressions . Дата обращения: 6 января 2015. Архивировано 1 января 2015 года.
  14. Chailloux, Manoury, Pagano — Developing with OCaml, 2007, p. 12.
  15. 1234Chailloux, Manoury, Pagano — Developing with OCaml, 2007, p. 13.
  16. Chailloux, Manoury, Pagano — Developing with OCaml, 2007, p. 15.
  17. 123Chailloux, Manoury, Pagano — Developing with OCaml, 2007, p. 15—16.
  18. Minsky, Madhavapeddy, Hickey, 2013, List Basics.
  19. 12Minsky, Madhavapeddy, Hickey, 2013, Chapter 5. Records.
  20. Minsky, Madhavapeddy, Hickey, 2013, Chapter 6. Variants.
  21. Minsky, Madhavapeddy, Hickey, 2013, Objects.
  22. Minsky, Madhavapeddy, Hickey, 2013, Functions and Type Inference.
  23. 12Minsky, Madhavapeddy, Hickey, 2013, Imperative Programming.
  24. Minsky, Madhavapeddy, Hickey, 2013, First-Class Modules.
  25. Minsky, Madhavapeddy, Hickey, 2013, Functors.

Литература

[править |править код]

Список книг, доступных онлайн

  • Minsky, Y. and Madhavapeddy, A. and Hickey, J. Real World OCaml: Functional Programming for the Masses. — O'Reilly Media, 2013. — 510 p. —ISBN 9781449324766.
    • Перевод на русский язык:Мински, Ярон; Мадхавапедди, Анил; Хикки, Джейсон. Программирование на языке OCaml = Real World OCaml: Functional Programming for the Masses. — ДМК, 2014. — 536 с. — (Функциональное программирование). —ISBN 978-5-97060-102-0.
Важно: некорректность перевода в русском издании

Примечание — в книге используется перевод термина «first-class function» как «функция первого порядка». Но следует иметь в виду, что в многочисленных англоязычных источниках (по семантике языков вообще и поML иХиндли-Милнеру в частности) концептуально различается четыре понятия:

  • first-class,
  • second-class,
  • first-order,
  • high-order,

причём «first-class» — это «лучше», чем «second-class» (шире по возможностям, ближе к теории и выше по порогу вхождения (C. Strachey —Fundamental Concepts in Programming Languages)), но «first-order» примитивнее, чем «high-order». В частности,расширение языка модулей ML до уровня«first-class high-order» представляет собой существенно большую проблематику для исследователей, чем его расширение только до «first-class» или только до «high-order» (Rossberg A. Functors and runtime vs compile time . Дата обращения: 25 июня 2015. Архивировано изоригинала 26 июня 2015 года.).

Ссылки

[править |править код]
Источник —https://ru.wikipedia.org/w/index.php?title=OCaml&oldid=150421750
Категории:
Скрытые категории: