OCaml

De la Viquipèdia, l'enciclopèdia lliure
Infotaula de llenguatge de programacióOCaml
Tipusllenguatge de programació orientat a objectes, llenguatge de programació funcional, llenguatge de programació multiparadigma, llenguatge de programació imperatiu, dialect (en) Tradueix i programari lliure i de codi obert Modifica el valor a Wikidata
Data de creació1996 Modifica el valor a Wikidata
DissenyXavier Leroy i Damien Doligez Modifica el valor a Wikidata
DesenvolupadorINRIA Modifica el valor a Wikidata
EpònimCaml Modifica el valor a Wikidata
Paradigma de programacióprogramació funcional, llenguatge imperatiu, programació orientada a objectes i programació modular Modifica el valor a Wikidata
Darrera versió estable5.1.1 () Modifica el valor a Wikidata
Dialecte deML Modifica el valor a Wikidata
Llenguatge de programacióOCaml i C Modifica el valor a Wikidata
Influenciat perML Estàndard Modifica el valor a Wikidata
Sistema operatiuUnix-like Modifica el valor a Wikidata
Extensió dels fitxersml i mli Modifica el valor a Wikidata
LlicènciaQ Public License (en) Tradueix i GNU LGPL 2.1 Modifica el valor a Wikidata
Etiqueta d'Stack ExchangeEtiqueta Modifica el valor a Wikidata
Pàgina webocaml.org Modifica el valor a Wikidata

OCaml, anteriorment denominat[1] Objective Caml és un llenguatge de programació de la família ML, extensió i versió actual del llenguatge de programació Caml,[2] acrònim de "Categorical Abstract Machine Language", creat per Xavier Leroy, Jérôme Vouillon, Damien Doligez, Didier Rémy i altres el 1996, amb construccions d'Orientació a Objectes, successora de l'extensió anterior del mateix llenguatge anomenada Caml Light.

OCaml és un projecte de codi obert impulsat per l'entitat estatal francesa de recerca INRIA (Institut national de recherche en informatique et en automatique).

OCaml parteix dels patrons del llenguatge funcional ML amb un lèxic i puntuació diferents, i hi afegeix construccions dels paradigmes de programació procedimental ja incorporades a Caml Light i d'Orientació a objectes, adoptant un enfocament multiparadigma.

OCaml és el nou nom oficial (abans era Objective Caml) des del Juliol del 2011,[1] i ha estat adoptat per Microsoft com a base del seu llenguatge funcional F#.[3] No hi ha cap estàndard per al llenguatge; l'única font de compiladors per al llenguatge és el mateix centre de recerca.

Compiladors[modifica]

OCaml posa èmfasi en el rendiment. Xavier Leroy diu "El compilador OCaml proporciona pel cap baix un rendiment del 50% d'un compilador de llenguatge C"[4] i els bancs de proves mostren que generalment és així.[5]

Es disposa de dos compiladors per a OCaml,

  • ocamlc a codi intermedi (bytecode) interpretable pel mòdul ocamlrun. (ocamlc -custom genera un executable autònom (stand‑alone) i possibilita l'enllaç amb biblioteques de llenguatge C).
  • ocamlopt compila a codi nadiu del sistema per a les arquitectures[6]
  • ocaml és un intèrpret o avaluador d'expressions (ang: Top-Level Read-Eval-Print-Loop) referit habitualment per TopLevel.[7]

OCaml destaca per la gestió de memòria amb recuperació incremental de curta durada.

>> .. about soft real-time performance .. GHC's stop-the-world GC will incur prohibitively long pause times for this kind of application (soft real-time), orders of magnitude longer than the pause times of OCaml's incremental GC.[8][9]

Ocaml implementa un gestor de memòria dinàmica (ang:garbage collector) de dues generacions

  • la generació jove quan arriba al límit fa servir el sistema "stop and copy" (aturar i copiar) de dos espais amb destinació a la generació vella.
  • la gen. vella quan arriba al límit fa servir el sistema "mark and sweep" (marcar i escombrar) de manera incremental amb compactació opcional

El caràcter incremental del recol·lector a la generació més vella, possibilita un temps reduït de les pauses. A cada passada de la generació jove (minor collection) fa una part de la feina sobre la generació vella (major collection).

Fent una prova amb el programa del tutorial de la referència[10] esmentat al paràgraf "The GC module", es comprova que les pauses per recuperació es poden comptar per poques dècimes de mil·lisegon malgrat que poden augmentar ocasionalment degut a la latència o espera de commutació de fil del planificador del nucli.

(<0.0002 segons amb ocamlc i <0.0001 amb ocamlopt mesurat sobre un Intel Atom model 330 amb Linux Ubuntu 10.04 "preempt"[11] de 64 bits i 2 GB de RAM i amb el compilador recompilat a partir dels fonts modificats per calcular, en les manques de memòria dinàmica caml_check_urgent_gc, la pausa màxima havent creat totes les estructures, és a dir, partint en haver completat la primera recol·lecció de la generació vella).

Hola Món[modifica]

El programa següent "hola.ml":

 print_string "Hola Món!\n" 
 ;;

es compila a codi intermedi de la següent manera:

$ ocamlc hola.ml -o hola

i s'executa en entorns Unix:

$ ./hola
Hola Món!
$

o a l'avaluador d'expressions "ocaml"

# print_string "Hola Món!\n" ;;
Hola Món!
- : unit = ()
#

Si oblideu el fi de clàusula en doble punt-i-coma ;; el podeu afegir a la línia següent.

En-línia, a l'avaluador d'expressions ocamljava en una applet Java. (No hi oblideu el topall ;; de fi d'instrucció).

Al mòbil o tauleta Android amb l'aplic. "Ocaml toplevel" o també l'aplic. d'execució remota "IDEDroid".

A l'iPad/iPhone amb l'aplic. "CodeToGo" que executa remotament a l'IDEone igual que l'"IDEDroid"[12]

Característiques[modifica]

OCaml disposa de tipatge estàtic, inferència de tipus, polimorfisme paramètric, recursivitat final, encaixos de patrons, tractament d'excepcions, tancaments, mòduls paramètrics (functor) i recol·lector de memòria brossa generacional i incremental, objectes, classes d'objectes (parametritzables), tipus de classes, ...

Lèxic[modifica]

Depèn de la codificació del fitxer font.[13] i l'entorn del sistema operatiu, degut a l'assimilació de caràcters a bytes (rang 0..255).

Cas de codificació habitual del Windows (Windows-1250)
es poden utilitzar caràcters europeus als identificadors. (tots els caràcters ocupen un sol octet).
Cas de codificació UTF-8 a Unix
l'alfabet del codi queda limitat als caràcters ASCII (0-127) (els únics que ocupen un sol octet en UTF-8). Les tires de text literals amb caràcters no anglosaxons (en codificació UTF-8 ocupen més d'un octet), poden donar resultats inesperats si es tracten amb el tipus String (String.length "Món" = 4 !! (4 bytes en UTF-8)) en comptes de ser tractats amb tipus més adients. Vegeu #tipus Unicode i exemple

Sintaxi[modifica]

Les clàusules acaben en doble punt-i-coma, per distingir-lo del punt-i-coma separador de la seqüència d'instruccions en una definició (lligam).

let nom_definit = expr; expr; ..; expr ;;
(* definició de tipus *)
type tipus = Constructor1 of tipus_dels_components | Constructor2 of tipus_dels_components | ... ;;
exception Excepció of tipus_dels_components ;;
(* dins una signatura *)  
val nom_definit : expr_de_tipus    
(* dins una def. d'objecte *)
val [mutable] nom_de_camp = expressió 

grafia[modifica]

Precedits d'un apòstrof
variables de tipus
Precedits d'una cometa revessa (accent greu + espai)
constructors de tipus variant
Començant per majúscula
  • Constructors de tipus
  • Símbols d'enumeracions
Començant per minúscula
la resta.

comentaris[modifica]

 (* comentari multilínia
    no existeix el comentari fins a fi de línia 
    (*
       comentari niuat
     *)
  *)

 (** comentari d'autodocumentació
  *)

nova sintaxi[modifica]

De manera opcional es proposa una revisió de la sintaxi per solucionar dificultats amb l'analitzador sintàctic.[14][15]

compilació amb la nova sintaxi
ocamlc -pp camlp4r programa.ml
nova sintaxi a l'intèrpret
ocaml
# load "camlp4r.cma";;

tipus[modifica]

tipus bàsics[modifica]

 type unit (* tipus buit, el de les expressions d'efectes col·laterals que no retornen cap valor
   literals: ()  
 *)
  • () com a únic paràmetre en una funció indica que cal recalcular-ne el valor a cada invocació
 type bool (* tipus booleà 
 literals: true, false
 ops: not, &&, ||   (''or'' i ''&'' han quedat obsolets)
 *)
 type int (* sencers 31 o 63 bits segons la màq.<ref>[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#6_Integerarithmetic Aritmètica de sencers, 31 o 63 bits]{{en}}</ref> *), 
 literals: 5, -5, 6-5   (* la op. menys unària es fa amb el signe '-'
                                  contràriament a SML *)
 ops: + - * / mod abs max_int min_int
      <, <=, >, >=, = (igual valor), <> (not =), == (igualtat referencial),<ref>[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#VAL(%3D%3D) Igualtat referencial (==) respecte Igualtat estructural (=)]</ref> != (not ==)
     land, lor, lxor, lnot, lsl, lsr, asr

No hi ha literals en bases hexa, octal, binari, però la conversió int_of_string els pot llegir d'una cadena amb el prefix "0x", "0o", "0b" respectivament.

 "pseudoliterals hexa": int_of_string "0x01020304"
 "pseudoliterals octal": int_of_string "0o377"
 "pseudoliterals binaris": int_of_string "0b0101"

El tipus sencer int comparteix representació amb el tipus punter, reservant un bit per la distinció.

Tipus sencers de paraula completa (32 o 64 bits):

 type float (* coma flotant de 64 bits [[IEEE 754]] *)
 literals: 0.44 1.2E2 5. (* la part sencera no pot ser buida, la decimal sí !! *)
 ops: +. -. *. /. ** ceil floor max_float min_float int_of_float float_of_int
                        (* els operadors numèrics de floats que coincideixen amb els de sencers
                           porten un puntet diferenciador *)
                        (* no generen excepcions en cas de sobreiximent o div. per zero
                           ''classify_float'' torna un de 
          (FP_normal | FP_subnormal (valors IEEE754 amb repr. ''denormal'' inf. als normals 
                                            per evitar div.per.zero) 
          | FP_zero | FP_nan (0/0) | FP_infinity)  Vegeu ref.<ref>[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#6_Floatingpointarithmetic Aritmètica de Coma flotant] {{en}}</ref>*)
      <, <=, >, >=, =, <>, ==, !=, classify_float
 type char  (* caràcters de 8 bits *) [http://caml.inria.fr/pub/docs/manual-ocaml/libref/Char.html mòdul Char]
 literals: 'a', '\n', '\t', '\120' (* decimal 'x' *), '\x78' (* hexadecimal 'x' *)

tipus algebraics[modifica]

enumerats[modifica]
  (* enumerats *)
 type signe = Positiu | Negatiu ;;
tipus producte - tuples[modifica]
 (* tuples (tipus producte), separador en els valors la coma 
    sense parèntesis també val, la coma n'és el constructor *)
 (2, 3.5) : int * float
 fst (2, 3.5) = 2 ;;
 snd (2, 3.5) = 3.5 ;;
 type la_meva = int * float
tipus producte - registres[modifica]
 (* registres, separador el punt i coma *)
 {a=2; b=3.5} : {a: int; b: float}

 (* registres amb camps mudables *)
 type persona = {nom:string; mutable edat:int}
 let p = {nom="Joan"; edat=30} ;;
 let aniversari (p:persona) = 
                              p.edat <- p.edat +1 ;;

 (* actualització funcional (clonar i actualitzar) *)
 let q = {p with nom="Josep"} ;;
tipus suma (unió discriminada)[modifica]
 (* unió discriminada per etiquetes (constructors) *)
 type nombre = Sencer of int | Real of float ;;
 let llista_de_nombres = [ Sencer 10; Real 2.5] ;;
tipus polimòrfics[modifica]
 (* tipus polimòrfics, 
    'a : el prefix apòstrof indica variable de tipus
    cas de més d'una variable, es posen en tupla *)

 (* opcionalitat de valor, per a resultats de funcions definides parcialment *)
 type 'a option = None | Some of 'a ;;

 (* amb dues variables *)
 type ('a, 'b) a_o_b = A of 'a | B of 'b ;;
 type sencerA_o_realB = (int, float) a_o_b ;;

 (* tipus recursius *)
 type 'a arbre = Fulla of 'a | Branca of 'a * 'a arbre * 'a arbre ;;

 let a = Fulla 2 ;;            
 let b = Branca (5, a, a) ;;

tipus amb allotjament lineal[modifica]

tipus string

Els caràcters ocupen un octet (valors 0..255). (Per a tires de caràcters Unicode vegeu #tipus Unicode) Vegeu[17]

 type string  (* text *) 
 (* literals: "abc"
    ops: "abc" ^ "def" = "abcdef" *)
     let str = "abc" ;;
      String.sub str 0 2 = "ab" ;; (* subtira amb inici i llargada *)

      str.[0] ;;  (* obtenir caràcter a la posició x *)
      str.[1] <- 'm' ;;  (* estableix caràcter -- les tires són mudables *)

      (* (==) igualtat referencial (indica si són la mateixa instància) *)
        ("abc" == "abc") = false  ;; 

      ("abc" = "abc") = true ;;    (* (=) igualtat estructural *)
Vectors - tipus ('a array)
Vegeu[18]
 (* vectors / arrays *)
 let arr = [| 1; 2; 3 |] ;;    (* literals *)    

 let arr = Array.make 20 1.0  ;;     (* 20 elements amb el valor *)
 let arr = Array.init 20 (fun i -> 2*i) ;; (* 20 elements segons funció *)

 let elem = arr.(0)  ;; (* obtenir elem., el primer índex és el 0 *)
 arr.(2) <- 6        ;; (* establir elem.*)

llistes homogènies[modifica]

 (* llistes, separador el punt i coma *)
 [ 1; 2; 3 ] ;; (* literals *)
 [ 1, 2; 3, 4] = [ (1, 2); (3, 4) ] ;; (* la coma fa el parell, el separador de llistes és el; *)
 1 :: [ 2; 3] ;;   (* op. Cons --  - : int list = [1; 2; 3] *)
 [ 1; 2] @ [3] ;;   (* concatena --  - : int list = [1; 2; 3] *)
 List.hd [1; 2; 3] = 1 ;; (* hd (abreviació de "head" : cap) *)
 List.tl [1; 2; 3] = [2; 3] ;;  (* tl (abreviació de "tail" : cua) *)

Col·leccions polimòrfiques - llistes heterogènies d'elements etiquetats[modifica]

El tipus és una unió discriminada oberta, sense declaració prèvia de tipus, d'elements etiquetats anomenats (variant types).[19]

let llista_d_etiquetats = [`Pomes 10; `Sucre 2.5] ;;

let totalitza (import_acum:float) variant = match variant with
                             | `Pomes (quant: int) -> import_acum +. (float_of_int quant) *. 0.32
                             | `Sucre (quant: float) -> import_acum +. quant *. 0.21
                             ;;

let total = List.fold_left totalitza 0.0 llista_d_etiquetats
in print_float total; print_newline ()
;;

La finalitat és discriminar els elements d'una col·lecció per encaix de patrons de l'etiqueta, que ha de començar per una cometa revessa, feta amb l'accent greu seguit d'espai, i un identificador que comenci per majúscula

  • Cada etiqueta (constructor) s'acompanya d'un sol valor (component) sempre del mateix tipus
  • El tractament serà per encaix de patrons segons l'etiqueta
  • Les llistes de variants es poden comparar, encara que continguin elements amb constructors diferents.
 let llista_d_etiquetats = [`Pomes 10; `Sucre 2.5 ] ;;
      (* val llista_d_etiquetats: [> `Pomes of int | `Sucre of float ] list *)

 let novallista = llista_d_etiquetats @ [`Ous 2] ;; (* concatenació *)

 (* les llistes etiquetades es poden comparar, 
    no hi ha queixa de tipus malgrat difereixin en etiquetes *)

 (novallista = llista_d_etiquetats) ;; (* - : bool = false *)

Exemple:#llistes obertes d'elements etiquetats

tipus Unicode[modifica]

Vegeu biblioteca Camomile[20]

UChar (* caràcter unicode de 31 bits *)

(* tipus de strings *)
UTF8
UTF16
UCS4
UText (* string com a vector de sencers *)

...

tipus fantasma[modifica]

De l'anglès "Phantom types". Tipus sense definició o amb paràmetres de tipus que no intervenen a la definició.

Vegeu[21][22]

expressions[modifica]

lligant identificadors amb expressions[modifica]

 let pi = 4. *. atan 1. ;; 

 (* encaix de tuples i registres *)

 let (v_a, v_b) = (1, 2) ;; (* val v_a : int = 1
                               val v_b : int = 2
                             *)
 let v_a, v_b = 1, 2 ;; (* sense parèntesi també val: la coma és el constructor de tupla *)

 type el_meu_tipus = {a:int; b:float} ;; (* registres *) 
 let el_meu = { a = 2; b = 3.5 } ;;
 let {a = v_a; b = v_b } = el_meu ;; (* val v_a : int = 2
                                         val v_b : float = 3.5
                                      *)

restricció de tipus[modifica]

 ''expr'' : ''tipus''

 (* (#) restricció de paràmetres a ''class type'' (signatures d'objectes 
                                                  i ''interfaces'') *)

 ''paràmetre'' : # ''super_classe_o_interface''

 (* (:>) coerció a tipus de classe menys especialitzat (''up-cast'')
         (superclasses i ''class type''s) -- secció [[#herència|Herència]] i següents *)

 ''expr_subtipus'' :> ''super_tipus_de_classe''

alternatives[modifica]

 if ''expr'' then ''expr'' else ''expr'' ;;

 match ''expr'' with 
              ''patró'' when ''expr'' -> ''expr''
            | ''patró'' | ''patró'' | ''patró''   when ''expr'' -> ''expr''
            ...
            ;;

declaracions locals[modifica]

amb àmbits successius de visibilitat

 let resultat = let ''declaració'' in       (* àmbit més extern *)
                let ''declaracio2'' in        (* àmbit intermedi *)
                 ''expr'' ;;                      (* àmbit intern *)

funcions[modifica]

 let suma x y = x + y ;;     (* val suma : int -> int -> int = <fun> *)

 (* funció recursiva, ''rec'' permet fer referència al nom de la funció *)
 let rec fact n = match n with  
                   0 -> 1 
                 | m -> m * fact (m -1) 
                 ;;          (* val fact : int -> int = <fun> *)
(* lligam amb funció anònima, clàusula function definició per encaix de patrons (pattern-matching)
                              clàusula fun definició per paràmetres[23]
 *)
 let rec fact = function    
   | 0 -> 1
   | n -> n * fact (n-1)
   ;;

 (* definició simultània (''and'') amb crides circulars, 
                           ''rec'' permet fer referència als identificadors definits *)
 let rec parell = function
                    | 0 -> true
                    | n -> senar (n-1)
  and senar = function
                    | 0 -> false
                    | n -> parell (n-1)
  ;;  (* val parell : int -> bool = <fun>
         val senar : int -> bool = <fun>
       *)
operadors de funcions

Contràriament a ML i al Haskell no n'hi ha.

 (* composició de funcions, predefinit: ''compose'' *)
 (*        no hi ha cap operador específic per la composició *)
 let compose f g = function x -> f(g(x));;

La composició de funcions en un llenguatge estricte, genera estructures intermèdies i és preferible resoldre el problema de la desforestació fusionant manualment les operacions en una funció amb un sol bucle.

La biblioteca Batteries defineix (-|) com a operador de composició.[24]

pas de paràmetres etiquetat[modifica]
  • per nom
 (* amb pas de paràmetres per nom (l'ordre dels param. es pot alterar)
         ~nomparam:argument o ~nomparam com a abrev. de ~nomparam:nomparam 
         ~ es fa amb les tecles AltGr+4 *)
 let rec fact ~num:n = match n with 
                   0 -> 1 
                 | m -> m * fact ~num:(m -1) 
                 ;;

L'opcio de compilació -nolabels requereix l'ordenació estricta dels paràmetres a la crida (ignora les etiquetes de paràmetres no opcionals).[25]

paràmetres opcionals[modifica]
  • paràmetres opcionals
let salt ?incr x =         (* ? prefixa el param. opcional formal *)
   match incr with
   | None -> x * 2         (* cas de None: el param. actual no hi era *)
   | Some y -> x + y       (* cas de Some: el param. actual sí que hi era *)
 ;;                        (* val salt : ?incr:int -> int -> int = <fun> *)

(* ús *)
let () = let r = salt ~incr:1 2    (* ~ prefixa el param. opcional actual *)
        in print_int r ;
         print_endline "" ;;

Requeriment: un paràmetre opcional ha d'ésser seguit, com a mínim, d'un paràmetre anònim (no etiquetat), el qual tanca la possible seqüència de paràmetres opcionals.[26]

  • paràmetres opcionals amb valor per omissió
let salt ?(incr = 1) x = 
         x + incr ;;  (* val salt : ?incr:int -> int -> int = <fun> *)

operadors[modifica]

Permet l'ús en prefix (entre parèntesis) o bé infix dels operadors binaris.[27]

 (* definició *)
 let (++) : 'a list -> 'a list -> 'a list = fun x y -> x @ y ;;

 (++) [1; 2] [3; 4]    (* ús en posició prefix *)

 [1; 2] ++ [ 3; 4]     (* ús en posició infix *)

El caràcter inicial de l'operador determina la seva precedència (per exemple els definits començant per '*' tenen la mateixa precedència dels operadors multiplicatius). Vegeu Taula de Precedències i associativitat dels operadors[28]

excepcions[modifica]

 exception ELlistaBuida of string ;;

 let obtenir_cap llista = match llista with
      [] -> raise (ELlistaBuida "dins obtenir_cap")
     | (cap :: _) -> cap
     ;;

 let la_meva_llista = [] ;;

 let res : 'a option = 
     try (
        Some (obtenir_cap la_meva_llista)
    )
     with
         | ELlistaBuida msg -> print_endline ("ELlistaBuida: tururut " ^ msg); None
         | _ -> print_endline "altra excepció"; None
     ;;

 (* funcions per llançar Excepcions típiques *)

 failwith "motiu de la fallada"          (* llança exc. Failure *)

 invalid_arg "rutina rut, segon paràmetre"          (* llança exc. Invalid_argument *)

Programació imperativa[modifica]

La que modifica l'estat.

registres amb camps mudables[modifica]

type persona = {nom:string; mutable edat : int} ;;
let joan = {nom = "Joan"; edat = 30} ;;
let aniversari (p:persona) = 
          p.edat <- p.edat +1 ;; (* retorna unit *)
aniversari joan ;;
print_int joan.edat ;;

Referències[modifica]

  • ref introdueix una referència (punter) a l'allotjament d'un altre objecte
  • (!) és l'operador invers que ens revela el valor de l'objecte referit per la variable
  • (:=) assigna un valor a l'objecte referit per la variable
 let a = ref 5 ;;
 let a := !a +1 ;;     (* assignació -- qualsevol assignació retorna ''unit'' com a valor d'expressió*)
 let resultat = a := !a + 1    (* retornaria ''unit'' *)
             ; !a           (* ara retornarà el valor allotjat *)
                ;;

efectes laterals[modifica]

let _ = print_endline "abc" ;;   (* el patró comodí '_' obvia el resultat *)

let () = ignore (expressió) ;;  (* ignore descarta el resultat, retorna Unit *)

let a = print_string "bla bla bla\n"; "nou_valor a retornar" ;;   (* efectes laterals i resultat a la mateixa clàusula *)

let imprimeix_llista_de_sencers_amb_separador llista sep =
  print_int (List.hd llista); (* imprimeix el cap *)

  (* imprimeix la resta d'elements prefixant-hi el separador *)
  (* List.iter : ('a -> unit) -> 'a list -> unit *)

  List.iter (Printf.printf "%s%d" sep) (List.tl llista)  (* aplicació parcial de ''(printf "%s%d")''
                                 deixant un dels paràmetres per al mapeig amb List.iter *)
  ;;

imprimeix_llista_de_sencers_amb_separador [1;2;3] ", " ;;
1, 2, 3- : unit = ()

control imperatiu[modifica]

expressió_while ::= "while" expr_booleana "do" expr_amb_efectes_laterals "done"

expressió_for ::= "for" identificador "=" expr_int  ("to" ∣  "downto") expr_int "do" expr_amb_efectes_laterals "done"
let sequencia i = if i <= 4 then Some i else None ;;
let corrent = Stream.from sequencia ;;

let fi = ref false ;;

while not !fi do
     match (Stream.peek corrent) with  (* Stream.peek consulta el primer *)
       | Some v -> let _ = Printf.printf "\n%d" v in
                   Stream.junk corrent (* Stream.junk escapça el corrent (treu el primer) *)
       | None -> fi := true
done ;;

Orientació a objectes (aspectes imperatiu i funcional)[modifica]

Vegeu[29] Facilita objectes immediats d'una sola instància i classes d'objectes immutables o mudables.

  • Els camps (clàusula val) són privats, accessibles només per als mètodes propis o de classes derivades.
  • Els mètodes poden contenir el modificador private.
  • La clàusula object admet la declaració d'una ref. a la instància, anomenada habitualment self com a object (self) ... end
  • Poden incorporar una clàusula d'inicialització (initializer)
  • Objectes d'una sola instància (ang: singleton) ometent la clàusula class

objectes immediats (d'una sola instància)[modifica]

let obj = object
            val factor = 5                     (* camp de la instància (accés privat) *)
            method mult_per_factor x = x * factor
          end ;;

obj#mult_per_factor 4 ;;
- : int 20

classes d'objectes[modifica]

  • tots els camps són d'accés privat
  • new en genera una instància.
class rectangle (x:float) (y:float) = object
        val area = (x *. x) +. (y *. y)       (* camp de la instància (accés privat) *)
        method obtenir_area = area
  end ;;

let nou_rect = new rectangle 3. 4. ;;

nou_rect#obtenir_area ;;
- : float 25.0
objectes mudables[modifica]

Incorporant el qualificatiu mutable als camps

Pot incorporar una clàusula d'inicialització initializer que el generador new executa.

El desallotjament automàtic fa innecessaris els destructors. Malgrat això, es pot registrar una funció com a finaliser al recollidor de brossa.[30]

(* finalitzadors *)
let f = fun x -> ... ;; let v = ... in Gc.finalise f v
class pila_de_sencers =
  object 
    val mutable la_llista : int list = []   (* camp de la instància, accés privat *)
    method apila x =                        
     la_llista <- x :: la_llista
    method lleva =                           
     let primer = List.hd la_llista in
     la_llista <- List.tl la_llista ;
      primer
    method private mida =      (* privat: accessible només des de la mateixa classe *)             
      List.length la_llista
    method es_buida = (la_llista = [])
   initializer                        (* new com a constructor l'inicialitza *)
           print_endline "pila inicialitzada" 
  end;;

let p = new pila_de_sencers ;;        (* "pila inicialitzada"
                                         val p : pila_de_sencers = <obj> *)

(* paràmetres de funció
-- let nom_funció (param : classe_tal)  -- sense prefix, el tipus del param. ha de ser exactament classe_tal
-- let nom_funció (param : #classe_tal) -- cas de prefix '#', el tipus del param. pot ésser subtipus de classe_tal
* )

let omple (s: #pila_de_sencers) = 
        for i = 1 to 5 do
           s#apila i
        done;;

let buida (s: #pila_de_sencers) =
        while not s#es_buida do
             Printf.printf "Hem llevat %d de la pila.\n" s#lleva
        done;;

Execució a l'intèrpret ocaml

# p ;;
- : pila_de_sencers = <obj>
# omple p ;;
- : unit = ()
# buida p ;;
Hem llevat 5 de la pila.
Hem llevat 4 de la pila.
Hem llevat 3 de la pila.
Hem llevat 2 de la pila.
Hem llevat 1 de la pila.
- : unit = ()
# 
actualització funcional dels objectes immutables[modifica]

La construcció {< ... >} permet l'actualització funcional en una nova instància, retornant-ne un clon actualitzat.[31]

class passa_anys (edat_ini:int) =
  object
    val edat = edat_ini
    method aniversari = {< edat = edat +1 >}       (* clonar i actualitzar *)
    method imprimeix = print_string "edat: "
                    ; print_int edat
                    ; print_newline ()
  end ;;

let joan = new passa_anys 10 ;;

let _ = joan#aniversari#imprimeix ;;
classes abstractes[modifica]

Els mètodes es poden fer abstractes (pendents d'especificar en classes derivades) amb la qualificació "virtual". Les classes amb mètodes virtuals han de ser declarades virtuals.

class virtual punt_abstracte x_init =
   object (self)
     val mutable virtual x : int
     method virtual get_x : int
     method get_dist = self#get_x - x_init
  end ;;

class point x_init =
   object
    inherit punt_abstracte x_init
     val mutable x = x_init
     method get_x = x
   end;;
     (* tipus resultant: class point : int -> object 
                               val mutable x : int 
                               method get_dist : int 
                               method get_x : int 
                         end *)
herència[modifica]

Les classes poden tenir herència múltiple. En cas de col·lisió de noms de mètode heretats, preval l'heretat darrer.

(* provant a l'intèrpret ocaml *)

class punt (x_inicial: float) (y_inicial: float) =
  object (self)
    val mutable x = x_inicial
    val mutable y = y_inicial
    method desplassa delta_x delta_y =    (* la ç de desplaça no s'admet 
                                                  als ident. UTF-8 (vegeu lèxic) *)
      x <- x +. delta_x ;
      y <- y +. delta_y
    method imprimeix =
      Printf.printf "posició %.2f %.2f\n" x y
  end ;;

type color = Verd | Roig | Blau ;;

let string_of_color = function
    Verd -> "Verd"
  | Roig -> "Roig"
  | Blau -> "Blau"
  ;;

class acolorit (c_inicial: color) = 
  object (self)
    val mutable color = c_inicial
    method pinta nou_color =
      color <- nou_color
    method imprimeix =
      print_endline ("color: " ^ (string_of_color color))
  end ;;

class punt_acolorit (x:float) (y:float) (c:color) = 
  object (self)
   inherit punt x y as super_punt
   inherit acolorit c as super_acolorit
    method imprimeix =
      super_punt#imprimeix ;
      super_acolorit#imprimeix 
  end ;;

let p_acolorit = new punt_acolorit 1.0 2.0 Verd ;;

p_acolorit#imprimeix ;;  (* posició 1.00 2.00
                            color: Verd
                            - : unit = () *)
Interfícies d'objectes[modifica]

A l'estil dels Interface de Java, designen el conjunt de tipus que implementen la signatura descrita amb class type.

(* seguint l'anterior codi a l'intèrpret ocaml *)

class type imprimible =
  object
    method imprimeix : unit
  end ;;

let imprim_test (obj : #imprimible)  = obj#imprimeix ;; (* val imprim_test : #imprimible -> unit = <fun> *)

(* El tipus de punt_acolorit és subtipus de imprimible perquè n'inclou els mètodes i camps (en cas que n'hi hagi). *)

imprim_test p_acolorit ;;  (* posició 1.00 2.00
                             color: Verd
                             - : unit = () *)

let p_ras = new punt 1.5 1.5 ;;          (* punt ras *)

imprim_test p_ras ;;       (* posició 1.50 1.50
                             - : unit = () *)
Col·leccions de valors amb funcionalitat comuna[modifica]

Per exemple, llistes amb elements d'aquells tipus que implementin una funcionalitat determinada per una interfície. Cal fer un up-cast (caracterització a tipus menys especialitzat) de cadascun dels elements al tipus de la interfície.

(* seguint les seccions anteriors *)

(* caracterització a tipus menys especialitzat (''up-cast''), 
                                            en aquest cas un interface  *)
let cast_a_imprimible cp = (cp :> imprimible) ;;
                                    (* val cast_a_imprimible : #imprimible -> imprimible = <fun> *)

(* col·leccions homogènies *)

let coleccio_d'imprimibles = [ cast_a_imprimible p_ras; (p_acolorit :> imprimible)] ;;  (* - : imprimible list *)

let _ = List.iter imprim_test coleccio_d'imprimibles ;; (* iteració de imprim_test a la llista *)
llistes obertes d'elements etiquetats[modifica]

Unió discriminada oberta (sense definició prèvia de tipus)

let coleccio_etiquetada = [`Punt p_ras; `Punt_acolorit p_acolorit] ;;

let rec tracta_col_etiquetada = function
                      | [] -> ()
                      | `Punt (p:punt) :: cua  ->
                                                 p#desplassa 1.0 1.0 ;
                                                 p#imprimeix ;
                                                 tracta_col_etiquetada cua

                      | `Punt_acolorit (p:punt_acolorit) :: cua ->
                                                 p#pinta Blau ;
                                                 p#desplassa 1.0 1.0 ;
                                                 p#imprimeix ;
                                                 tracta_col_etiquetada cua
                      ;;
classes amb parametrització de tipus (genèrics)[modifica]

Vegeu[32]

class parell_de_sencers (x: int) (y: int) =
object
   method get_x = x
   method get_y = y
end ;;

let parelleta_de_sencers = new parell_de_sencers 5 3 ;;

class ['a, 'b] parell_de_tipus_generic (x: 'a) (y: 'b) =
object
   method get_x = x
   method get_y = y
end ;;

let parelleta_de_dos = new parell_de_tipus_generic 5 3.2 ;;
      (* val parelleta_de_dos : (int, float) parell_de_tipus_generic = <obj> *)

(* herència *)
class ['a, 'b] parell_de_classe_generica_derivada (x: 'a) (y: 'b) =
object
 inherit ['a, 'b] parell_de_tipus_generic x y
  (* ... *)
end ;;

let parelleta_del_derivat = new parell_de_classe_generica_derivada 7.5 9.5 ;;

(* funció que retorna resultat de tipus diferent
     segons la instanciació de l'objecte de la classe genèrica *)

let obtenir_x (p : ('a, 'b) #parell_de_tipus_generic) = p#get_x ;;

obtenir_x parelleta_de_dos ;;
- : int 5

obtenir_x parelleta_del_derivat ;;
- : float 7.5
restriccions als paràmetres de funcions i de classes[modifica]
class type imprimible =   (* interfície "imprimible" (signatura) *)
  object
    method imprimeix : unit
  end ;;

(* als paràmetres de funcions *)

let test_impressio (obj: #imprimible) = obj#imprimeix ;;

(* als paràmetres de les classes *)

class classe_amb_component_imprimible (obj_ini: #imprimible) = object (self:'c)

  constraint 'c = #imprimible   (* requeriment que la classe implementi l'interface *)

  val obj = obj_ini
  method imprimeix = obj#imprimeix
end ;;

(* als paràmetres de tipus de classes genèriques *)

class ['a] classe_genèrica_amb_component_imprimible (obj_ini: 'a) = object
  constraint 'a = #imprimible
  val obj = obj_ini 
  method imprimeix = obj#imprimeix
end ;;
constructors de còpia[modifica]
  • Oo.copy crea una còpia superficial (la que no duplica els valors referits per punters ref).[33]
class classe_amb_copia =
   object (self)
     method copy = Oo.copy self
   end;;

Mòduls[modifica]

Un fitxer de codi sense clàusula module es considera un mòdul en si mateix i el seu nom de mòdul és el del fitxer amb la primera lletra majúscula.

Per fer-ne servir un identificador cal prefixar-lo: "Nom_fitxer.ident"; per incorporar tots els seus identificadors a l'àmbit actual s'utilitza la clàusula open

 open Nom_fitxer          (* importa l'espai de noms del mòdul Nom_fitxer *)
(* o també *)
 let open Nom_fitxer in   (* importació d'àmbit local, versió 3.12 *)
 ...

La clàusula module introdueix un submòdul que s'adreça externament com a Nom_fitxer.Nom_modul

 (* fitxer nom_fitxer.ml *)
 module Intern =
 struct
  let ident = 3 
 end ;; 

 (* des de dins del fitxer, ens hi referirem per Intern *)
 print_endline ("resultat: " ^ (string_of_int Intern.ident)) ;;

 (* des d'un altre fitxer : *)
 open Nom_fitxer.Intern

Compilació[modifica]

En una compilació els fonts s'han d'especificar en ordre tal que si Mòdul_A crida Mòdul_B, Mòdul_B ha de precedir Mòdul_A.

En cas que les referències entre mòduls fossin circulars, cal generar i compilar primer els fitxers de signatura (secció següent).

 (* compilació a codi intermedi "bytecode" (per executar la tasca 
     caldrà la presència de l'intèrpret de bytecode ocamlrun *)

 ocamlc modul_B.ml modul_A.ml -o nom_tasca       
 (* En executar la tasca en bytecode, crida a l'intèrpret *)
 ./nom_tasca arg1..argn

 (* execució des de l'intèrpret *)                         
 ocamlrun nom_tasca arg1..argn
.
 (* compilació a codi nadiu *)
 ocamlopt modul_B.ml modul_A.ml -o nom_tasca

 ./nom_tasca arg1..argn

També es pot compilar a fitxer biblioteca, especificant -a

(* compilació a biblioteca de codi intermedi (bytecode) *)
ocamlc -a modul_B.ml modul_A.ml -o nom_llib.cma             
(* compilació a  biblioteca de codi nadiu *)
ocamlopt -a modul_B.ml modul_A.ml -o nom_llib.cmxa

Compilació amb biblioteques del sistema de paquets[modifica]

per exemple la biblioteca de tractament de tires de caràcters Unicode del paquet "Camomile"[20] incorporada amb el gestor de paquets Findlib.[34] o la distribució en codi font GODI[35]

(* fitxer mon.ml (exemple de prova) *)
module CAM = CamomileLibrary.Default.Camomile ;;
let text =  "Món" ;;
let _ = Printf.printf "UTF8.length '%s' : %d\n" text (CAM.UTF8.length text) ;
        Printf.printf "String.length '%s' : %d\n" text (String.length text) ;;
  • ocamlfind,[36] interfície del gestor de paquets, proporciona directoris, biblioteques i opcions al compilador segons la info. del fitxer META del paquet.[37]
ocamlfind ocamlc -package camomile -linkpkg mon.ml -o mon

o amb compilació i relligat separats

ocamlfind ocamlc -c -package camomile mon.ml                (* compilació, genera objecte mon.cmo *)
ocamlfind ocamlc -package camomile -linkpkg mon.cmo -o mon  (* relligat (ang:link) *)

a Linux amb la codificació descrita a la variable d'entorn LANG = ca_ES.UTF-8

./mon 
UTF8.length 'Món' : 3
String.length 'Món' : 4

"CamomileLibrary" NO és el nom del fitxer biblioteca, és un mòdul generat amb l'opció -pack del compilador (empaqueta com a submòduls els mòduls esmentats com a paràmetres).[38][39] Aquest sistema no és regla general dels paquets.

(* extracte del Makefile del paquet Camomile *)
camomileLibrary.cmo camomileLibrary.cmi : $(OBJECTS)
     $(OCAMLC) $(BOPTIONS) -pack -o camomileLibrary.cmo $(OBJECTS) $(INCLUDES)

Generació d'intèrprets personalitzats[modifica]

OcamlMkTop[40] permet crear avaluadors d'expressions OCaml (intèrprets o Toplevels en argot OCaml) amb mòduls i biblioteques precarregats.

A voltes dona millor informació d'errors en cas de manca d'adequació de crides Ocaml a biblioteques del sistema subjacent.

Incorporant l'exemple anterior

ocamlfind ocamlmktop -package camomile -linkpkg mon.cmo -o ocaml-mon

Signatura d'un mòdul[modifica]

Seqüència d'especificacions de tipus dels identificadors exportats per un mòdul.

Generació partint del codi font[modifica]

En cas que interessi declarar d'accés privat (d'ús intern) algun identif. d'un mòdul-fitxer, generarem un fitxer amb la seva signatura amb la comanda següent, redirigint la sortida:

ocamlc -i fitxer_a.ml > fitxer_a.mli

Com que només s'exporten els identificadors presents a la signatura, editarem el .mli eliminant les declaracions que volem d'accés privat, i el compilarem amb ocamlc, generant el .cmi (interfície compilada)

(* cas de compilació a codi nadiu *)
ocamlc -c fitxer_a.mli       (* genera interfície compilada .cmi *)
ocamlopt -c fitxer_a.ml      (* genera objecte de codi nadiu .cmx *)
ocamlopt fitxer_a.cmx principal.ml -o nom_tasca      (* genera executable nadiu *)
(* cas de compilació a codi intermedi (bytecode) *)
ocamlc -c fitxer_a.mli fitxer_a.ml                    (* genera interfície compilada .cmi i objecte bytecode .cmo *)
ocamlc fitxer_a.cmo principal.ml -o nom_tasca         (* genera executable bytecode *)
amb signatura explícita[modifica]
module NomMòdul : Signatura  

(:) equival a l'adscripció opaca del ML Estàndard quedant els tipus tan visibles com ho siguin a la signatura, i quedant d'accés privat els elements de l'estruct. no presents a la signatura.

module Intern :
   sig 
     val obtenir_privat : unit -> int 
   end 
 =
 struct
  let el_meu_privat = 3 
  let obtenir_privat () = el_meu_privat
 end ;;

o també

module type SignatIntern =
 sig 
   val obtenir_privat : unit -> int 
 end ;;

module Intern : SignatIntern =
 struct
  let el_meu_privat = 3 
  let obtenir_privat () = el_meu_privat
 end ;;
composició de signatures[modifica]

A partir de v3.12: herència (include) múltiple i substitució de tipus eliminant la instrucció type interna de les signatures incloses.[41]

module type SigDelMeuComponent =
  sig
    type u
   include Imprimible with type t := u    (* v. 3.12 (substitució del tipus
                                              eliminant la instr. type interna) *)
   include Ordenable with type t := u
  end

Estructures paramètriques: Functors[modifica]

Vegeu ref.[42]

module type Igualable =
  sig
    type t
    val iguals: t -> t -> bool
  end ;;

module type SigConjunt =
  sig
    type tip_elem 
    type t
    val buit : t
    val es_membre: tip_elem -> t -> bool
    val afegeix: tip_elem -> t -> t
  end ;;

module type SigConjuntLlistaDIgualables = functor (E: Igualable) ->
                                      (SigConjunt with type tip_elem = E.t 
                                                   and type t = E.t list
                                     ) ;;

module ConjuntLlistaDIgualables : SigConjuntLlistaDIgualables = functor (E: Igualable) ->
  struct
    type tip_elem = E.t 
    type t = E.t list
   let buit = ([] : E.t list)

   let rec es_membre = fun (elem:E.t) (conj :E.t list) -> match conj with
                                   | [] -> false
                                   | cap :: cua -> (E.iguals elem cap) || (es_membre elem cua)
                                   ;;

   let afegeix = fun (elem :E.t) (conj :E.t list) -> if not (es_membre elem conj) 
                                        then elem :: conj 
                                        else conj
  end ;;

module Sencers =
  struct
    type t = int 
   let iguals: t -> t -> bool = fun a b -> a = b
  end ;;

module Reals =
  struct
    type t = float
   let iguals: t -> t -> bool = fun a b -> compare a b = 0
  end ;;

module type SigConjuntLlistaDeSencers = (SigConjunt with type tip_elem = Sencers.t 
                                                   and type t = Sencers.t list
                                       ) ;;

module ConjuntLlistaDeSencers = ConjuntLlistaDIgualables (Sencers) ;;

module ConjuntLlistaDeReals = ConjuntLlistaDIgualables (Reals) ;;

Serialització[modifica]

Amb el mòdul Marshal.[43]

Autodocumentació[modifica]

Els comentaris entre (** i *) es poden fer servir per generar un fitxer d'autodocumentació mitjançant l'eina ocamldoc[44]

Els comentaris entre (* i *) no entren a l'autodocumentació.

S'hi poden incloure camps estàndard per documentar el mòdul i els paràmetres de les definicions. (els tipus no cal posar-los-hi que ja surten automàticament) com ara

@author     text
@version    text
@param      rol descripció   (* no cal posar-hi tipus, ja l'obté de la def. *)
@return     descripció
@raise      Excepció descripció
@see        url  (vegeu ..)
@since      text (des de versió tal)
@deprecated text (obsolet s'avisa que aquesta funció ...)

Vegeu-ne l'ús a l'exemple més avall. Un exemple, amb el fitxer de signatura i sense, seria

ocamldoc -html modul.ml
o bé
ocamldoc -html modul.ml -intf modul.mli

Generaria els fitxers de documentació Modul.html i type_Modul.html

Arrencada[modifica]

Vegeu[45]

(* fitxer arrenca.ml, utilitza mòduls Sys i Arg *)
(* prog. d'anàlisi de línia d'ordres *)

let nomprog = Sys.argv.(0) ;;    (* argc = Array.length Sys.argv *)

let usage_msg = Printf.sprintf "Ús: %s -v -o sortida --num 10 fitxer_a fitxer_b\n" nomprog ;;

(* variables per a opcions i paràmetres *) 
let ref_flag_v = ref false ;;
let ref_opcio_o = ref "" ;;
let ref_opcio_num = ref 0 ;;
let ref_fitxers = ref ([] : string list) ;;

(* especificació d'opcions: llista de (opció, comanda : Arg.spec, text_d'ajuda) *)
let spec_opcions = [ ("-v", Arg.Set ref_flag_v, "versió") ;
                     ("-o", Arg.Set_string ref_opcio_o, "output") ;
                     ("--num", Arg.Set_int ref_opcio_num, "numeric")
                   ]  ;;

(* paràmetres posteriors a les opcions, apilar-los a !ref_fitxers, quedaran en ordre invers *)
let en_llegir_arg arg = ref_fitxers := arg :: !ref_fitxers ;;

(* capgira la llista per recuperar-ne l'ordre *)
let obtenir_args_de_comanda () = List.rev !ref_fitxers 

let processa_arg arg = Printf.printf "tracta param. %s\n" arg ;;

let run = Arg.parse spec_opcions en_llegir_arg usage_msg 
       ; Printf.printf "flag_v: %B\n" !ref_flag_v 
       ; Printf.printf "opcio_o: %s\n" !ref_opcio_o 
       ; Printf.printf "opcio_num: %d\n" !ref_opcio_num 
       ; List.iter processa_arg (obtenir_args_de_comanda ())   (* aplica funció a params. retornant unit *)
          ;;
(* fi de fitxer *)

compilació

ocamlc -o arrenca arrenca.ml
./arrenca -v -o sortida --num 10 fitxer_a fitxer_b

dona la següent sortida

flag_v: true
opcio_o: sortida
opcio_num: 10
tracta param. fitxer_a
tracta param. fitxer_b

Altres sistemes:

API Estàndard[modifica]

Les operacions predefinides són al mòdul Pervasives, obert d'entrada.[46]

Els tipus predefinits s'esmenten aquí.[47]

Les biblioteques estàndards s'especifiquen aquí[48]

Extensions dels tipus de fitxers[modifica]

de fonts[modifica]

.ml
fitxer font
.mli
fitxer d'interfície
.mll
document de lèxic per al generador d'analitzadors de lèxic (ocamllex)[49]
.mly
document de gramàtica per al generador d'analitzadors gramaticals (ocamlyacc)[49]

compilats[modifica]

.cmo
fitxer de codi objecte bytecode (codi intermedi)
.cmi
fitxer d'interfície, compilat
.cmx
fitxer de codi objecte nadiu (propi de l'arquitectura del processador)
biblioteques[modifica]
.cma
biblioteca de codi objecte bytecode de relligat estàtic (en temps de compilació)
.cmxa
biblioteca de codi objecte nadiu de relligat estàtic
.cmxs
biblioteca de codi objecte nadiu de relligat dinàmic (com .dll (a windows)/.so (a unix)) (només per a la plataforma amd64 (x86-64))[50]

Gestió de paquets[modifica]

Findlib[modifica]

El gestor de paquets Findlib funciona només sobre sistemes Unix. Vegeu enllaços.[34][36]

El fitxer de projecte del paquet duu per nom META.[51]

Per instal·lar un paquet precompilat, descarregat manualment:

ocamlfind install nom_del_paquet camí_al_fitxer_META

Per especificar-lo en la compilació vegeu secció més amunt #Compilació amb biblioteques del sistema de paquets

Distribució GODI[modifica]

GODI[35] és un rebost de paquets de codi font que en facilita la instal·lació i actualització compilant a destinació.

Incorpora un programa anomenat godi_console que proporciona una interfície de menús en una consola de comandes, per a la selecció, instal·lació i actualització dels paquets[52]

A l'hora d'instal·lar un paquet, cal fer primer l'operació test per comprovar si manquen paquets dels quals depèn, que haurem d'assenyalar "per instal·lar" individualment.

Per anul·lar les operacions GODI marcades, si no ens en sortim, cal cercar l'arxiu "build/wishes.txt" de la instal·lació GODI i buidar-lo o eliminar-lo.

Els paquets de distribució Godi poden tenir un nom lleugerament diferent del nom amb Findlib (per exemple godi-camomile per al paquet camomile). Per utilitzar-los en compilació cal fer servir el nom original del paquet (per exemple camomile) i el sistema ja descrit per al Findlib.

Compartint recursos[modifica]

En cas d'executar més d'un programa ocaml alhora, de cara a minimitzar l'ús de memòria hi ha les següents opcions:

Un únic gestor de memòria
La compilació a bytecode permet tenir una única instància del gestor de memòria i interfase amb el sistema operatiu. el programa ocamlrun interpreta el codi intermedi.[53]
Relligat dinàmic amb biblioteques
Vegeu ref.[54] ocamlmklib genera biblioteques estàtiques i dinàmiques partint d'objectes i biblioteques de codi OCaml i codi C.[55]

Portabilitat[modifica]

El codi intermedi (bytecode) pot córrer a, pràcticament, qualsevol plataforma compatible amb POSIX amb un compilador compatible amb ANSI-C.

La llista oficial de plataformes suportades aquí.[56]

JoCaml[modifica]

JoCaml[57] és una extensió del llenguatge que implementa l'àlgebra de processos CSP anomenada "càlcul Join"[58] i facilita la concurrència per pas de missatges amb sincronització per encaix de patrons per a sistemes distribuïts amb migració de processos.

OCaml sobre JavaVM[modifica]

Amb el programari OCamlJava[59][60] Requereix JVM >= 1.6

export OCAMLJAVA=carpeta_distrib_ocamljava/bin

Execució de codi intermedi OCaml (projecte Cadmium)[modifica]

ocamlc hola.ml -o hola                      # compilació a codi intermedi
java -jar $OCAMLJAVA/ocamlrun.jar hola      # execució a JVM de bytecode ocaml

Cadmium per a l'Android: joecaml[61] constitueix una reimplementació per a una versió més antiga de Java, la 1.5 compatible amb Android. (Cadmium de OCamlJava requereix Java 1.6)

Compilació a codi intermedi JVM (projecte Cafesterol)[modifica]

(amb "-standalone" incorpora totes les biblios necessàries a l'arxivador .jar de sortida)
java -jar $OCAMLJAVA/ocamljava.jar  hola.ml -o hola.jar -standalone
java -jar hola.jar                          # execució de bytecode JVM

OCaml a Javascript[modifica]

ocamljs
rerefons de compilador generant Javascript per al compilador ocaml[62][63]
js_of_ocaml
traductor de codi intermedi ocamlc a javascript[64]

OCaml per a l'iPhone / iPad[modifica]

OCaml a l'Android[modifica]

  • OCaml Toplevel:[68] intèrpret (de Keigo Imai) descarregable des del Google Market

OCaml sobre Xen[modifica]

  • OpenMirage[69] és una implementació per desenvolupar micronuclis servidors d'aplicacions Núvol d'internet sobre l'hipervisor Xen (sense sistema operatiu).

Exemples[modifica]

Ent./Sortida[modifica]

(** fitxer volca_fitxer.ml (Autodocumentació)
    especifiquem els tipus de sortida de les rutines 
       per assegurar-ne la comprovació a les diferents branques,
       els tipus dels params. d'entrada els dedueix de les operacions involucrades 
    .- les operacions sense prefix de mòdul les trobareu al Pervasives
    @author jo i en pitus
    @version 1.0
    *)

(**
  llegeix una línia i l'escriu
   @param inp canal d'entrada
   @raise End_of_file fi de fitxer
   *)
let volca_linia (inp:in_channel) : unit =
        let línia = input_line inp in
         print_endline linia
         ;;

(**
  llegeix d'un canal d'entrada
   @param inp canal d'entrada
   *)
let volca_canal inp : unit =
         try (while true do 
                             volca_linia inp
                          done
            )
         with 
           End_of_file -> print_endline "\n-- fi de fitxer"
         | _ -> print_endline "\n-- altra excepció"
         ;;

(**
   obre el fitxer comprovant error
   @param nom_fitxer nom del fitxer
   @return possible canal d'entrada
   *)
let obre_fitxer nom_fitxer : in_channel option =
         try (
               let inp = open_in nom_fitxer in
                Some inp
             )
         with _ -> let _ = Printf.printf "Error en obrir fitxer %s per llegir\n" nom_fitxer in 
                   None
         ;;

(**
    volca el fitxer especificat si és que existeix
    @param nom_fitxer nom del fitxer
    *)
let volca_fitxer nom_fitxer : unit =
        if not (Sys.file_exists nom_fitxer)
         then Printf.printf "El fitxer de nom %s no existeix\n" nom_fitxer
         else let possible_inpc = obre_fitxer nom_fitxer in
               match possible_inpc with
                 None -> ()
               | Some inpc -> volca_canal inpc ;
                              close_in inpc
         ;;

(**
    comprova que la tasca té un argument i el pren de nom de fitxer a volcar
   *)
let main = let nom_prog = Sys.argv.(0) in
         if Array.length Sys.argv <> 2 
          then Printf.printf "ús: %s nom_fitxer\n" nom_prog
          else volca_fitxer Sys.argv.(1) 
          ;;

Compila, executa, genera interfície (signatura) i genera fitxers d'autodocumentació (de la implementació nom-amb-primera-lletra-majúscula.html i de la interfície type_nom-amb-primera-lletra-majúscula.html)

ocamlc volca_fitxer.ml -o volca_fitxer
./volca_fitxer nom_fitxer
ocamlc -i volca_fitxer.ml > volca_fitxer.mli
ocamldoc -html volca_fitxer.ml -intf volca_fitxer.mli

Llistes d'avaluació tardana[modifica]

Estalvien espai en el tractament de llistes llargues o infinites.

(* tipus de llista d'avaluació tardana (ang:lazy evaluation) *)

type 'a node_t =
    | Nil
    | Cons of 'a * 'a zlist_t
and 'a zlist_t = 'a node_t Lazy.t
;;

(** generador tardà de m elements; ''lazy'' deixa la generació en suspens *)

let rec genera_llista (m:int) :'a zlist_t =
         lazy (match m with
                 | 0 -> Nil
                 | n -> Cons (n, genera_llista (n-1))
              ) ;;

(** fold_left_tardà: fold_left sobre llista d'avaluació tardana;

      ''Lazy.force'' força l'avaluació del generador de la llista 
                        obtenint-ne 1 elem.
  *)

let rec fold_left_tardà f acum (llista:'a zlist_t) =
      match (Lazy.force llista) with        
         | Nil -> acum
         | Cons (x, xs) -> fold_left_tardà f (f acum x) xs
;;

let op_binaria = fun (a:float) (b:int) -> a +. sqrt (float_of_int b) ;; (* qualsevol que interessi *)

let _ =
        Printf.printf "resultat: %.2f\n" (fold_left_tardà op_binaria 0. (genera_llista 100000)) 
;;

Interfície d'entorn gràfic -- Hola món a GTK amb Glade[modifica]

Glade és una eina de disseny d'interfícies gràfiques d'usuari per a l'entorn GTK que desa el disseny en format GladeXML que els programes poden carregar fàcilment per instanciar-ne els elements gràfics i connectar-hi els gestors d'esdeveniments.

El programari lablgtk2[70] conté els connectors a les API de GTK i Glade així com un convertidor que del format GladeXML genera un mòdul OCaml amb la definició de la interfície. Per exemple, dissenyant al Glade una finestra de nom window1 amb l'etiqueta "Hola món" desant el fitxer com a hola_mon_interficie.glade.

A la versió més recent de Glade, cal especificar a les preferències, com a format de projecte, el libGlade (GladeXML v.2.0)[71] en comptes del GtkBuilder que ve predeterminat.

lablgladecc2 hola_mon_interficie.glade > hola_mon_interficie.ml
(* fitxer hola_mon.ml *)

open Hola_mon_interficie

let en_sortir () = GMain.quit () ;;

let () =
  let w1 = new window1 () in                                 (* crea instància de la finestra importada de "Hola_mon_interficie" *)
  let _ = w1#window1#connect#destroy ~callback:en_sortir in  (* connecta el senyal de tancament ''on destroy'' *)
   w1#toplevel#show ();                                      (* mostra la finestra *)
   GMain.Main.main () ;;                                      (* engega el bucle de despatx de senyals *)
ocamlc -I +lablgtk2 lablgtk.cma lablglade.cma gtkInit.cmo hola_mon_interficie.ml hola_mon.ml -o hola_mon
./hola_mon

L'antic convertidor ml-glade[72] que traduïa fitxers GladeXML versió 1, no ha sigut actualitzat a les versions de Glade actuals i ha quedat obsolet.

Composició de signatures[modifica]

A partir de la versió 3.12 permet la inclusió múltiple de signatures mitjançant la substitució de tipus de la segona en endavant. Vegeu ref.[41]

module type PUNT_ABSTRACTE =
sig
  type t
  val getX : t -> float
  val setX : float -> t -> t
  val modul: t -> float 
  val dist: t -> t -> float
end ;;

module type PUNT1D =
sig
 include PUNT_ABSTRACTE
  val nou: float -> t         
end ;;

module type PROP_Y =
sig
  type u
  val getY : u -> float
  val setY : float -> u -> u
end ;;

module type PUNT2D =
sig
 include PUNT_ABSTRACTE
 include PROP_Y with type u := t     (* inclusió de signatures amb substitució de tipus v3.12 *)
  val nou: float -> float -> t
  val desplassaX: float -> t -> t
end ;;

module Punt1D : PUNT1D =
struct
  type t = { x: float}

 let nou x = {x}

 let getX {x} = x                        (* v3.12: encaixos {x} equivalents a {x=x} *)
 let setX nova_x p = {p with x = nova_x}

 let modul p = p.x
 let dist p q = modul { x = q.x -. p.x} 
end ;;

module Punt2D : PUNT2D = 
struct
  type t = { x:float; y: float}

 let nou x y = {x; y}

 let getX {x} = x
 let setX nova_x p = {p with x = nova_x}

 let getY {y} = y
 let setY nova_y p = {p with y = nova_y}

 let modul p = sqrt (p.x ** 2.0 +. p.y ** 2.0)

 let dist p q = modul { x = q.x -. p.x; y = q.y -. p.y} 

 let desplassaX dx ({x} as p) = {p with x = x +. dx}       (* ''as pattern'' al revés del Haskell *)
end ;;

Productor / consumidor[modifica]

Més info al manual "Unix systems programming in Objective Caml"[73]

open Event

type info = Info of int | Plega

exception EFinal of string

let obtenir_hora : unit -> string =                       (* obtenir hora d'Unix.time *)
     fun () -> let open Unix in                           (* ''open'' local, versió 3.12 *)
              let tm = localtime (time ()) in
               Printf.sprintf "%02d:%02d:%02d" tm.tm_hour tm.tm_min tm.tm_sec

let productor : info channel -> unit =
     fun canal -> for valor = 3 downto 0 do
                           Thread.delay 1.0
                        ; sync(send canal (Info valor))        (* tramesa síncrona *)
                   done

let consumidor : info channel * info channel -> unit =
     fun (canal1, canal2) ->
                 try (while true do
                         match sync (choose [receive canal1; receive canal2])   (* recepció síncrona amb selecció *)
                         with
                            | Info valor -> Printf.printf "%s - compte: %d \n" (obtenir_hora ()) valor 
                                         ; flush stdout

                            | Plega -> raise (EFinal "s'ha acabat")    (* excepció per sortir del bucle *)
                      done
                    )
                 with 
                 | EFinal msg -> Printf.printf "excep %s\n" msg
                               ; flush stdout
                                  ;;

let main () = let canal1 = Event.new_channel () in
             let canal2 = Event.new_channel () in

             let consumidor_id = Thread.create consumidor (canal1, canal2) in
             let productor_id = Thread.create productor canal1 in  (* pel canal1 *)

              Thread.join productor_id     (* espera fi productor *)

           ; sync(send canal2 Plega)    (* ordre de plegar al consumidor *) (* pel canal2 *)

           ; Thread.join consumidor_id  (* espera fi consumidor *)

           ; print_string "Fi del programa\n"
              ;;

let _ = main ()
  • Compilació amb fils d'exec. lleugers (del planificador de l'intèrpret) (Lightweight threads)[74] (només a bytecode): ocamlc -vmthread
 ocamlc -vmthread -o prova_vmt unix.cma threads.cma prova.ml
 ./prova_vmt
  • Compilació amb fils d'exec. del sistema (només a codi nadiu), en cas que els suporti: ocamlopt -thread
 ocamlopt -thread -o prova_ost unix.cmxa threads.cmxa prova.ml
 ./prova_ost

dona:

11:44:15 - compte: 3 
11:44:16 - compte: 2 
11:44:17 - compte: 1 
11:44:18 - compte: 0 
excep s'ha acabat
Fi del programa

STM - Memòria transaccional -- Emulació de les var. de sincronització TMVar del Haskell - GHC[modifica]

Amb la biblioteca coThreads[75]

A l'Ubuntu:

sudo apt-get install libcothreads-ocaml-dev

Vegeu també:

(* fitxer mvar.ml, adaptat dels exemples de cothreads *)
module Thread=Cothread
open Stm

type 'a mvar = 'a option tvar

(* -------- primitives similars a les del Haskell -------- *)

let new_empty_mvar () =  tvar None

let take_mvar mv = 
  read_tvar mv >>= (function
    None -> retry
  | Some v ->
      write_tvar mv None >>= (function _ -> return v))

let put_mvar mv v =
  read_tvar mv >>= (function
    None -> write_tvar mv (Some v)
  | Some v -> retry)

(* -------- aplicació -------- *)

let productor mv =
 let c = ref 0 in
  while true do
    Thread.delay (Random.float 0.00005);
    atom (put_mvar mv !c);
   incr c
  done

let consumidor mv =
  while true do
    Printf.printf "Rebut %d\n" (atom (take_mvar mv));
    flush_all ();
  done

let main () =
 let mv = new_empty_mvar () in
 let prod_id = Thread.create productor mv in
 let consum_id = Thread.create consumidor mv in
  Thread.join prod_id; 
  Thread.join consum_id;
  ()

let () = main ()
  • Compilació amb fils d'exec. lleugers (del planificador de l'intèrpret), a codi intermedi ocaml.
ocamlc -vmthread -o mvar-vmt cothreads.cma  mvar.ml
./mvar-vmt
  • Compilació amb fils d'exec. del sistema, a codi nadiu
ocamlopt -thread -o mvar-ost unix.cmxa cothreads.cmxa  mvar.ml
./mvar-ost

Referències[modifica]

  1. 1,0 1,1 Canvi de nom de Objective Caml a OCaml
  2. El llenguatge Caml i extensions(anglès)
  3. Microsoft Research - F sostingut -- "El nucli del llenguatge és compartit amb OCaml"(anglès)
  4. Linux Weekly News (anglès)
  5. The Computer Language Benchmarks Game - GNU GCC respecte de OCaml[Enllaç no actiu](anglès)
  6. Plataformes suportades(anglès)
  7. Intèrpret ocaml o toplevel Arxivat 2010-08-09 a Wayback Machine.(anglès) Avaluador d'expressions de nivell global (toplevel)
  8. Quin sistema és més eficient, Haskell o bé OCaml?(anglès)
  9. Garbage Collection(anglès) Recol·lecció de memòria brossa
  10. Ocaml-tutorial - Garbage collection(anglès)
  11. Ubuntu - modalitats per a temps-real(anglès)
  12. IDEone - Interfícies amb l'entorn d'execució remota
  13. És possible escriure tires de text literal als fonts en UTF-8 en OCaml? Arxivat 2009-01-07 a Wayback Machine. (anglès)
  14. nova sintaxi opcional(anglès)
  15. Guia sobre la sintaxi revisada(anglès)
  16. «Literals int32, int64, nativeint». Arxivat de l'original el 2010-08-13. [Consulta: 13 setembre 2010].
  17. mòdul String Tipus String(anglès)
  18. Vectors - mòdul Array(anglès)
  19. Variant types (anglès)
  20. 20,0 20,1 Biblioteca Camomile de tipus Unicode
  21. Control d'accés estàtic fent servir Phantom types(anglès)
  22. Tipus fantasmes (Phantom types) a Haskell(anglès)
  23. Expressions(anglès)
  24. Operador de composició (-|)
  25. «Opcions de compilació». Arxivat de l'original el 2009-12-04. [Consulta: 6 desembre 2009].
  26. Optional arguments
  27. operadors infix(anglès)
  28. taula de precedències i associativitat dels operadors(anglès)
  29. Objectes a OCaml Arxivat 2009-11-29 a Wayback Machine. (anglès)
  30. finalitzadors al recollidor de brossa(anglès)
  31. Objectes funcionals Arxivat 2009-11-29 a Wayback Machine.(anglès)
  32. Tipus i Genericitat (anglès)
  33. Clonant objectes Arxivat 2009-11-29 a Wayback Machine.(anglès)
  34. 34,0 34,1 El gestor de paquets Findlib (anglès)
  35. 35,0 35,1 GODI distribució en codi font(anglès)
  36. 36,0 36,1 Ocamlfind, interfície del gestor de paquets(anglès)
  37. Linker options(anglès) Vegeu opcions als fitxers META dels paquets.
  38. Opcions del compilador ocamlc Arxivat 2009-12-04 a Wayback Machine. (anglès) Vegeu opció -pack
  39. Makefile del paquet Camomile (anglès) Genera mòdul CamomileLibrary per empaquetament (-pack) dels altres fitxers objecte
  40. OcamlMkTop(anglès)
  41. 41,0 41,1 Substitució del tipus en signatures Arxivat 2010-08-13 a Wayback Machine.(anglès)
  42. Mòduls parametritzats(anglès)
  43. Serialització: mòdul Marshal
  44. El generador d'autodocumentació Arxivat 2009-02-20 a Wayback Machine. (anglès)
  45. Biblio estàndard Arg (anglès)
  46. «Operacions predefinides». Arxivat de l'original el 2009-04-17. [Consulta: 27 abril 2009].
  47. «Tipus i excepcions predefinits». Arxivat de l'original el 2009-04-27. [Consulta: 27 abril 2009].
  48. Biblioteques estàndards
  49. 49,0 49,1 Lexer and parser generators (ocamllex, ocamlyacc) Arxivat 2019-08-16 a Wayback Machine. (anglès)
  50. Generació de biblioteques de relligat dinàmic (opció -shared) Arxivat 2010-08-26 a Wayback Machine.(anglès) "Només a la plataforma amd64"
  51. Fitxer META(anglès)
  52. Paquets del rebost del GODI(anglès)
  53. El programa ocamlrun Arxivat 2010-08-09 a Wayback Machine.(anglès)
  54. biblioteques de relligat dinàmic (DLL) Arxivat 2010-08-09 a Wayback Machine. (anglès)
  55. El programa ocamlmklib Arxivat 2010-07-24 a Wayback Machine.(anglès)
  56. Portabilitat - Plataformes suportades(anglès)
  57. Jocaml
  58. Join calculus tutorial
  59. OcamlJava(anglès)
  60. Presentació OcamlJava Paris 2008(anglès)
  61. Cadmium per a l'Android(anglès)
  62. ocamljs - wiki amb documentació al viver de projectes de Google[Enllaç no actiu](anglès) el codi més recent cal cercar-lo al GitHub
  63. ocamljs al viver GitHub(anglès)
  64. js_of_ocaml - traductor a javascript(anglès)
  65. Compilador Ocaml creuat per a l'iphone (anglès)
  66. Compilador creuat Ocaml per a l'iPhone treballant dins l'entorn Xcode
  67. OCaml a l'iPad(anglès)
  68. Keigo Imai - OCaml Toplevel a l'Android Arxivat 2011-07-15 a Wayback Machine.(japonès)
  69. OpenMirage Arxivat 2015-05-10 a Wayback Machine.(anglès) Desenvolupament sobre Xen
  70. biblioteca lablGTK de connectors a GTK i Glade(anglès)
  71. format libGlade: GladeXML v.2.0 Arxivat 2010-07-02 a Wayback Machine.(anglès)
  72. Traductor obsolet ml-glade (glade v.1 a ocaml)(anglès)
  73. Unix system programming in Objective Caml(anglès)
  74. Biblioteca threads Arxivat 2010-08-27 a Wayback Machine.(anglès)
  75. Projecte coThreads(anglès) Biblioteca STM (Memòria transaccional)

Vegeu també[modifica]

  • Haskell llenguatge de programació funcional pur que facilita el paral·lelisme i l'aprofitament dels múltiples nuclis dels processadors actuals

Enllaços externs[modifica]

A Wikimedia Commons hi ha contingut multimèdia relatiu a: OCaml