OCaml - OCaml

OCaml
OCaml Logo.svg
Paradigma Multi-paradigma : funcional , imperativo , modular , orientado a objetos
Familia ML
Diseñada por Xavier Leroy , Jérôme Vouillon, Damien Doligez , Didier Rémy, Ascánder Suárez
Desarrollador INRIA
Apareció por primera vez 1996 ; Hace 25 años ( 1996 )
Lanzamiento estable
4.12.0  Edita esto en Wikidata / 24 de febrero de 2021 ; hace 6 meses ( 24 de febrero de 2021 )
Disciplina de mecanografía Inferido , estático , fuerte , estructural
Lenguaje de implementación OCaml, C
Plataforma IA-32 , x86-64 , alimentación , SPARC , ARM 32-64
SO Multiplataforma : Unix , macOS , Windows
Licencia LGPLv2.1
Extensiones de nombre de archivo .ml, .mli
Sitio web ocaml .org
Influenciado por
C , Caml , Modula-3 , Pascal , ML estándar
Influenciado
ATS , Coq , Elm , F # , F * , Haxe , Opa , Rust , Scala

OCaml ( / k æ m əl / OH- KAM -əl , anteriormente Caml Objetivo ) es una de propósito general , lenguaje de programación multi-paradigma que se extiende la Caml dialecto del ML con orientación a objetos características. OCaml fue creado en 1996 por Xavier Leroy , Jérôme Vouillon, Damien Doligez , Didier Rémy, Ascánder Suárez , entre otros.

El OCaml cadena de herramientas incluye un sistema interactivo de alto nivel intérprete , un código de bytes compilador , una optimización de código nativo compilador, un reversibles depurador , y un gestor de paquetes (OPAM). OCaml se desarrolló inicialmente en el contexto de la demostración automatizada de teoremas y tiene una presencia enorme en el software de análisis estático y métodos formales . Más allá de estas áreas, ha encontrado un uso serio en programación de sistemas , desarrollo web e ingeniería financiera , entre otros dominios de aplicaciones.

El acrónimo CAML originalmente significaba Categorical Abstract Machine Language , pero OCaml omite esta máquina abstracta . OCaml es un proyecto de software gratuito y de código abierto gestionado y mantenido principalmente por el Instituto Francés de Investigación en Informática y Automatización (INRIA). A principios de la década de 2000, muchos lenguajes adoptaron elementos de OCaml, en particular F # y Scala .

Filosofía

Los lenguajes derivados de ML son más conocidos por sus sistemas de tipos estáticos y compiladores de inferir tipos . OCaml unifica la programación funcional , imperativa y orientada a objetos en un sistema de tipo similar al ML. Por lo tanto, los programadores no necesitan estar muy familiarizados con el paradigma del lenguaje funcional puro para usar OCaml.

Al requerir que el programador trabaje dentro de las limitaciones de su sistema de tipos estáticos, OCaml elimina muchos de los problemas de tiempo de ejecución relacionados con los tipos asociados con los lenguajes de tipos dinámicos. Además, el compilador de inferencia de tipos de OCaml reduce en gran medida la necesidad de las anotaciones de tipos manuales que se requieren en la mayoría de los lenguajes de tipo estático. Por ejemplo, el tipo de datos de las variables y la firma de las funciones generalmente no necesitan declararse explícitamente, como lo hacen en lenguajes como Java y C # , porque pueden inferirse de los operadores y otras funciones que se aplican a las variables y otros valores. en el código. El uso eficaz del sistema de tipos de OCaml puede requerir cierta sofisticación por parte de un programador, pero esta disciplina se ve recompensada con un software confiable y de alto rendimiento.

OCaml se distingue quizás más de otros idiomas con orígenes en la academia por su énfasis en el desempeño. Su sistema de tipo estático evita los desajustes de tipos en tiempo de ejecución y, por lo tanto, evita las comprobaciones de seguridad y el tipo de tiempo de ejecución que sobrecargan el rendimiento de los lenguajes escritos dinámicamente, al tiempo que garantiza la seguridad del tiempo de ejecución, excepto cuando la verificación de límites de matriz está desactivada o cuando se utilizan algunas características no seguras como la serialización. . Estos son lo suficientemente raros como para evitarlos en la práctica.

Aparte de la sobrecarga de verificación de tipos, los lenguajes de programación funcionales son, en general, difíciles de compilar en un código de lenguaje de máquina eficiente, debido a problemas como el problema de funarg . Junto con bucle estándar, registro, e instrucción optimizaciones , compilador de optimización de OCaml emplea análisis estático de software métodos para optimizar el valor de boxeo y el cierre de la asignación, que ayuda a maximizar el rendimiento del código resultante incluso si se hace un amplio uso de construcciones de programación funcionales.

Xavier Leroy ha declarado que "OCaml ofrece al menos el 50% del rendimiento de un compilador de C decente", aunque una comparación directa es imposible. Algunas funciones de la biblioteca estándar OCaml se implementan con algoritmos más rápidos que las funciones equivalentes en las bibliotecas estándar de otros lenguajes. Por ejemplo, la implementación de la unión de conjuntos en la biblioteca estándar de OCaml en teoría es asintóticamente más rápida que la función equivalente en las bibliotecas estándar de lenguajes imperativos (por ejemplo, C ++, Java) porque la implementación de OCaml explota la inmutabilidad de los conjuntos para reutilizar partes de la entrada. conjuntos en la salida (ver estructura de datos persistentes ).

Características

OCaml presenta un sistema de tipo estático , inferencia de tipo , polimorfismo paramétrico , recursividad de cola , coincidencia de patrones , cierres léxicos de primera clase , functores (módulos paramétricos) , manejo de excepciones y recolección de basura automática generacional incremental .

OCaml se destaca por extender la inferencia de tipos de estilo ML a un sistema de objetos en un lenguaje de propósito general. Esto permite la tipificación estructural , donde los tipos de objetos son compatibles si las firmas de sus métodos son compatibles, independientemente de su herencia declarada (una característica inusual en los lenguajes de tipificación estática).

Se proporciona una interfaz de función externa para vincular a primitivas de C , que incluye soporte de lenguaje para matrices numéricas eficientes en formatos compatibles con C y Fortran . OCaml también admite la creación de bibliotecas de funciones OCaml que se pueden vincular a un programa principal en C, de modo que una biblioteca OCaml se puede distribuir a programadores de C que no tienen conocimiento o instalación de OCaml.

La distribución OCaml contiene:

El compilador de código nativo está disponible para muchas plataformas, incluidas Unix , Microsoft Windows y Apple macOS . La portabilidad se logra mediante el soporte de generación de código nativo para las principales arquitecturas: IA-32 , X86-64 (AMD64), Power , RISC-V , ARM y ARM64 .

Los programas de código de bytes y código nativo OCaml se pueden escribir en un estilo multiproceso , con cambio de contexto preventivo. Sin embargo, debido a que el recolector de basura del sistema INRIA OCaml (que es la única implementación completa disponible actualmente del lenguaje) no está diseñado para la concurrencia, el multiprocesamiento simétrico no es compatible. Los subprocesos OCaml en el mismo proceso se ejecutan solo por tiempo compartido. Sin embargo, existen varias bibliotecas para computación distribuida como Functory y ocamlnet / Plasma .

Entorno de desarrollo

Desde 2011, se han aportado muchas herramientas y bibliotecas nuevas al entorno de desarrollo de OCaml:

  • Herramientas de desarrollo
    • opam es un administrador de paquetes para OCaml.
    • Merlin proporciona una funcionalidad similar a IDE para varios editores, incluidos retroceso de tipos, ir a la definición y autocompletar.
    • Dune es un sistema de compilación componible para OCaml.
    • OCamlformat es un formateador automático para OCaml.
    • ocaml-lsp-server es un protocolo de servidor de idiomas para la integración de OCaml IDE .
  • Sitios web:
  • Compiladores alternativos para OCaml:
    • js_of_ocaml, desarrollado por el equipo de Ocsigen , es un compilador de optimización de OCaml a JavaScript .
    • BuckleScript , que también se dirige a JavaScript , con un enfoque en producir una salida de JavaScript idiomática y legible.
    • ocamlcc es un compilador de OCaml a C, para complementar el compilador de código nativo para plataformas no compatibles.
    • OCamlJava, desarrollado por INRIA, es un compilador de OCaml para la máquina virtual Java (JVM).
    • OCaPic, desarrollado por Lip6, es un compilador OCaml para microcontroladores PIC .

Ejemplos de código

Los fragmentos de código OCaml se estudian más fácilmente ingresándolos en el REPL de nivel superior . Esta es una sesión OCaml interactiva que imprime los tipos inferidos de expresiones resultantes o definidas. El nivel superior de OCaml se inicia simplemente ejecutando el programa OCaml:

$ ocaml
     Objective Caml version 3.09.0
#

A continuación, se puede ingresar el código en el indicador "#". Por ejemplo, para calcular 1 + 2 * 3:

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

OCaml infiere que el tipo de expresión es "int" (un entero de precisión de máquina ) y da el resultado "7".

Hola Mundo

El siguiente programa "hello.ml":

print_endline "Hello World!"

se puede compilar en un ejecutable de bytecode:

$ ocamlc hello.ml -o hello

o compilado en un ejecutable de código nativo optimizado:

$ ocamlopt hello.ml -o hello

y ejecutado:

$ ./hello
Hello World!
$

El primer argumento de ocamlc, "hello.ml", especifica el archivo fuente a compilar y el indicador "-o hello" especifica el archivo de salida.

Sumar una lista de números enteros

Las listas son uno de los tipos de datos fundamentales en OCaml. El siguiente ejemplo de código define una función recursiva suma que acepta un argumento, enteros , que se supone que es una lista de enteros. Tenga en cuenta la palabra clave recque denota que la función es recursiva. La función itera de forma recursiva sobre la lista dada de enteros y proporciona una suma de los elementos. La declaración de coincidencia tiene similitudes con el elemento switch de C , aunque es mucho más general.

let rec sum integers =                   (* Keyword rec means 'recursive'. *)
  match integers with
  | [] -> 0                              (* Yield 0 if integers is the empty 
                                            list []. *)
  | first :: rest -> first + sum rest;;  (* Recursive call if integers is a non-
                                            empty list; first is the first 
                                            element of the list, and rest is a 
                                            list of the rest of the elements, 
                                            possibly []. *)
  # sum [1;2;3;4;5];;
  - : int = 15

Otra forma es utilizar la función de plegado estándar que funciona con listas.

let sum integers =
  List.fold_left (fun accumulator x -> accumulator + x) 0 integers;;
  # sum [1;2;3;4;5];;
  - : int = 15

Dado que la función anónima es simplemente la aplicación del operador +, esto se puede abreviar a:

let sum integers =
  List.fold_left (+) 0 integers

Además, se puede omitir el argumento de lista haciendo uso de una aplicación parcial :

let sum =
  List.fold_left (+) 0

Ordenación rápida

OCaml se presta para expresar de manera concisa algoritmos recursivos. El siguiente ejemplo de código implementa un algoritmo similar a quicksort que ordena una lista en orden creciente.

 let rec qsort = function
   | [] -> []
   | pivot :: rest ->
     let is_less x = x < pivot in
     let left, right = List.partition is_less rest in
     qsort left @ [pivot] @ qsort right

Problema de cumpleaños

El siguiente programa calcula el número más pequeño de personas en una habitación para quienes la probabilidad de cumpleaños completamente únicos es menor al 50% (el problema de cumpleaños , donde para 1 persona la probabilidad es 365/365 (o 100%), para 2 es 364/365, para 3 es 364/365 × 363/365, etc.) (respuesta = 23).

let year_size = 365.

let rec birthday_paradox prob people =
  let prob = (year_size -. float people) /. year_size *. prob  in
  if prob < 0.5 then
    Printf.printf "answer = %d\n" (people+1)
  else
    birthday_paradox prob (people+1)
;;

birthday_paradox 1.0 1

Números de la iglesia

El siguiente código define una codificación de Church de números naturales , con sucesor (succ) y adición (add). Un numeral de la Iglesia nes una función de orden superior que acepta una función fy un valor xy se aplica fa tiempos xexactos n. Para convertir un número de Church de un valor funcional a una cadena, le pasamos una función que antepone la cadena "S"a su entrada y la cadena constante "0".

let zero f x = x
let succ n f x = f (n f x)
let one = succ zero
let two = succ (succ zero)
let add n1 n2 f x = n1 f (n2 f x)
let to_string n = n (fun k -> "S" ^ k) "0"
let _ = to_string (add (succ two) two)

Función factorial de precisión arbitraria (bibliotecas)

Se puede acceder directamente a una variedad de bibliotecas desde OCaml. Por ejemplo, OCaml tiene una biblioteca incorporada para aritmética de precisión arbitraria . A medida que la función factorial crece muy rápidamente, desborda rápidamente los números de precisión de la máquina (generalmente 32 o 64 bits). Por tanto, el factorial es un candidato adecuado para la aritmética de precisión arbitraria.

En OCaml, el módulo Num (ahora reemplazado por el módulo ZArith) proporciona aritmética de precisión arbitraria y se puede cargar en un nivel superior en ejecución usando:

# #use "topfind";;
# #require "num";;
# open Num;;

La función factorial se puede escribir usando los operadores numéricos de precisión arbitraria = / , * / y - /  :

# let rec fact n =
    if n =/ Int 0 then Int 1 else n */ fact(n -/ Int 1);;
val fact : Num.num -> Num.num = <fun>

Esta función puede calcular factoriales mucho más grandes, como 120 !:

# string_of_num (fact (Int 120));;
- : string =
"6689502913449127057588118054090372586752746333138029810295671352301633
55724496298936687416527198498130815763789321409055253440858940812185989
8481114389650005964960521256960000000000000000000000000000"

Triángulo (gráficos)

El siguiente programa renderiza un triángulo giratorio en 2D usando OpenGL :

let () =
  ignore (Glut.init Sys.argv);
  Glut.initDisplayMode ~double_buffer:true ();
  ignore (Glut.createWindow ~title:"OpenGL Demo");
  let angle t = 10. *. t *. t in
  let render () =
    GlClear.clear [ `color ];
    GlMat.load_identity ();
    GlMat.rotate ~angle: (angle (Sys.time ())) ~z:1. ();
    GlDraw.begins `triangles;
    List.iter GlDraw.vertex2 [-1., -1.; 0., 1.; 1., -1.];
    GlDraw.ends ();
    Glut.swapBuffers () in
  GlMat.mode `modelview;
  Glut.displayFunc ~cb:render;
  Glut.idleFunc ~cb:(Some Glut.postRedisplay);
  Glut.mainLoop ()

Se requieren los enlaces de LablGL a OpenGL. A continuación, el programa se puede compilar en código de bytes con:

  $ ocamlc -I +lablGL lablglut.cma lablgl.cma simple.ml -o simple

o al código nativo con:

  $ ocamlopt -I +lablGL lablglut.cmxa lablgl.cmxa simple.ml -o simple

o, más simplemente, usando el comando build de ocamlfind

  $ ocamlfind opt simple.ml -package lablgl.glut -linkpkg -o simple

y correr:

  $ ./simple

En OCaml se pueden desarrollar programas gráficos 2D y 3D mucho más sofisticados y de alto rendimiento. Gracias al uso de OpenGL y OCaml, los programas resultantes pueden ser multiplataforma, compilando sin cambios en muchas de las plataformas principales.

secuencia Fibonacci

El siguiente código calcula la secuencia de Fibonacci de un número n ingresado. Utiliza la recursividad de la cola y la coincidencia de patrones.

let fib n =
  let rec fib_aux m a b =
    match m with
    | 0 -> a
    | _ -> fib_aux (m - 1) b (a + b)
  in fib_aux n 0 1

Funciones de orden superior

Las funciones pueden tomar funciones como funciones de entrada y de retorno como resultado. Por ejemplo, aplicar dos veces a una función f produce una función que aplica f dos veces a su argumento.

let twice (f : 'a -> 'a) = fun (x : 'a) -> f (f x);;
let inc (x : int) : int = x + 1;;
let add2 = twice inc;;
let inc_str (x : string) : string = x ^ " " ^ x;;
let add_str = twice(inc_str);;
  # add2 98;;
  - : int = 100
  # add_str "Test";;
  - : string = "Test Test Test Test"

La función usa dos veces una variable de tipo 'a para indicar que se puede aplicar a cualquier función f mapeando desde un tipo ' a a sí misma, en lugar de solo a funciones int-> int . En particular, incluso se puede aplicar dos veces a sí mismo.

  # let fourtimes f = (twice twice) f;;
  val fourtimes : ('a -> 'a) -> 'a -> 'a = <fun>
  # let add4 = fourtimes inc;;
  val add4 : int -> int = <fun>
  # add4 98;;
  - : int = 102

Idiomas derivados

MetaOCaml

MetaOCaml es una extensión de programación de varias etapas de OCaml que permite la compilación incremental de nuevo código de máquina durante el tiempo de ejecución. En algunas circunstancias, son posibles aceleraciones significativas mediante la programación de varias etapas, porque hay disponible información más detallada sobre los datos a procesar en tiempo de ejecución que en el tiempo de compilación normal, por lo que el compilador incremental puede optimizar muchos casos de verificación de condición, etc.

Por ejemplo: si en el momento de la compilación se sabe que a menudo se necesita alguna función de potencia , pero el valor de solo se conoce en tiempo de ejecución, se puede utilizar una función de potencia de dos etapas en MetaOCaml: x -> x^nn

 let rec power n x =
   if n = 0
   then .<1>.
   else
     if even n
     then sqr (power (n/2) x)
     else .<.~x *. .~(power (n - 1) x)>.

Tan pronto como nse conozca en tiempo de ejecución, se puede crear una función de potencia especializada y muy rápida:

 .<fun x -> .~(power 5 .<x>.)>.

El resultado es:

 fun x_1 -> (x_1 *
     let y_3 = 
         let y_2 = (x_1 * 1)
         in (y_2 * y_2)
     in (y_3 * y_3))

La nueva función se compila automáticamente.

Otros lenguajes derivados

  • AtomCaml proporciona una primitiva de sincronización para la ejecución atómica (transaccional) de código.
  • Emily (2006) es un subconjunto de OCaml 3.08 que utiliza un verificador de reglas de diseño para hacer cumplir los principios de seguridad del modelo de capacidad de objeto .
  • F # es un lenguaje de .NET Framework basado en OCaml.
  • Fresh OCaml facilita la manipulación de nombres y carpetas.
  • GCaml agrega polimorfismo extensional a OCaml, lo que permite la sobrecarga y la clasificación con seguridad de tipos.
  • JoCaml integra construcciones para desarrollar programas concurrentes y distribuidos.
  • OCamlDuce amplía OCaml con características como expresiones XML y tipos de expresiones regulares.
  • OCamlP3l es un sistema de programación paralelo basado en OCaml y el lenguaje P3L.
  • Si bien no es realmente un lenguaje separado, Reason es una sintaxis OCaml alternativa y una cadena de herramientas para OCaml creada en Facebook .

Software escrito en OCaml

Usuarios

Varias docenas de empresas utilizan OCaml hasta cierto punto. Los ejemplos notables incluyen:

Referencias

enlaces externos