OCaml

De Viquipèdia
Dreceres ràpides: navegació, cerca

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 enfoc multiparadigma.

OCaml és el nou nom oficial (abans era Objective Caml) des del Juliol del 2011.[1]

OCaml 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.

Pros i contres del llenguatge.[4]

Taula de continguts

Compiladors[modifica | modifica el codi]

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"[5] i els bancs de proves mostren que generalment és així.[6]

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 bilioteques de lleng. C).
  • ocamlopt compila a codi nadiu del sistema per a les arquitectures[7]
  • ocaml és un intèrpret o avaluador d'expressions (ang: Top-Level Read-Eval-Print-Loop) referit habitualment per TopLevel .[8]

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.[9]

Vegeu ref.[10] 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 gen. 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[11] 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"[12] 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 | modifica el codi]

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"[13]

Característiques[modifica | modifica el codi]

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 | modifica el codi]

Depèn de la codificació del fitxer font.[14] i l'entorn del Sist.Op., 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 !!) en comptes de ser tractats amb tipus més adients. Vegeu #tipus Unicode i exemple

Sintaxi[modifica | modifica el codi]

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 | modifica el codi]

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 | modifica el codi]

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

nova sintaxi[modifica | modifica el codi]

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

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

tipus[modifica | modifica el codi]

tipus bàsics[modifica | modifica el codi]

 type unit (* tipus buit, el de les expressions d'efectes coŀ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 | modifica el codi]

enumerats[modifica | modifica el codi]
  (* enumerats *)
 type signe = Positiu | Negatiu ;;
tipus producte - tuples[modifica | modifica el codi]
 (* 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 | modifica el codi]
 
 (* 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 | modifica el codi]
 
 (* 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 | modifica el codi]
 
 (* 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 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 | modifica el codi]

tipus string

Vegeu [18]

 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 [19]
 
 (* vectors / arrays *)
 let arr = [| 1 ; 2 ; 3 |] ;;        
 
 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 | modifica el codi]

 
 (* llistes, separador el punt i coma *)
 [ 1 ; 2 ; 3 ] ;;
 [ 1, 2 ; 3, 4] = [ (1, 2) ; (3, 4) ] ;;
 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 | modifica el codi]

Llista d'elements etiquetats per discriminar-los en encaixar.

Unió discriminada oberta, sense declaració prèvia de tipus, anomenada (variant types).[20] Vegeu exemple

  • Les etiquetes o constructors comencen per la 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 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 | modifica el codi]

Vegeu biblioteca Camomile[21]

UChar (* caràcter unicode de 31 bits *)
 
(* tipus de strings *)
UTF8
UTF16
UCS4
UText (* string com a vector de sencers *)
 
...

tipus fantasma[modifica | modifica el codi]

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

Vegeu [22][23]

expressions[modifica | modifica el codi]

lligant identificadors amb expressions[modifica | modifica el codi]

 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 igual que abans (la coma fa la 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 | modifica el codi]

 ''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 | modifica el codi]

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

declaracions locals[modifica | modifica el codi]

amb àmbits successius de visibilitat

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

funcions[modifica | modifica el codi]

 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, cas d'un sol argument / patró -> clàusula function
                              cas de diversos patrons -> clàusula fun[24]
 *)
 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ó.[25]

pas de paràmetres etiquetat[modifica | modifica el codi]
  • 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).[26]

paràmetres opcionals[modifica | modifica el codi]

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.[27]

  • paràmetres opcionals
let salt ?incr x =         (* ? prefixa el param. opcional formal *)
   match incr with
   | None -> x * 2         (* el param. actual no hi era *)
   | Some y -> x + y       (* 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 "" ;;
  • paràmetres opcionals amb valor per omissió
let salt ?(incr = 1) x = 
         x + incr ;;  (* val salt : ?incr:int -> int -> int = <fun> *)

operadors[modifica | modifica el codi]

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

 (* 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[29]

excepcions[modifica | modifica el codi]

 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 | modifica el codi]

La que modifica l'estat.

registres amb camps mudables[modifica | modifica el codi]

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 | modifica el codi]

  • 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 | modifica el codi]

let _ = print_endline "abc" ;;   (* expressió d'efectes laterals descartant resultat *)
 
let a = print_string "bla bla bla\n" ; "nou_valor" ;;
 
let imprimeix_llista_de_sencers_amb_separador llista sep =
  print_int (List.hd llista) ;
  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 | modifica el codi]

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 | modifica el codi]

Vegeu [30] Facilita classes i objectes mudables, inalterables i també funcionals.

  • 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 | modifica el codi]
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
objectes immutables[modifica | modifica el codi]

Sense el qualificatiu mutable a cap camp de la classe. 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 | modifica el codi]

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.[31]

(* 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)  -- el tipus del param. ha de ser exactament classe_tal
-- let nom_funció (param : #classe_tal) -- el tipus del param. cas de prefix '#', 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 = ()
# 
objectes funcionals[modifica | modifica el codi]

La construcció {< ... >} retorna una còpia modificada de l'objecte[32]

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 | modifica el codi]

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 | modifica el codi]

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 = () *)
Tipus de classes (interfícies d'objectes)[modifica | modifica el codi]

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 imprimtest (obj : #imprimible)  = obj#imprimeix ;; (* val imprimtest : #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). *)
 
imprimtest p_acolorit ;;  (* posició 1.00 2.00
                             color: Verd
                             - : unit = () *)
 
let p_ras = new punt 1.5 1.5 ;;          (* punt ras *)
 
imprimtest p_ras ;;       (* posició 1.50 1.50
                             - : unit = () *)
Col·leccions de valors amb funcionalitat comuna[modifica | modifica el codi]

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 imprimtest coleccio_d'imprimibles ;; (* iteració de imprimtest a la llista *)
llistes obertes d'elements etiquetats[modifica | modifica el codi]

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 | modifica el codi]

Vegeu [33]

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 | modifica el codi]
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 | modifica el codi]
  • Oo.copy crea una còpia superficial (la que no duplica els valors referits per punters ref).[34]
class classe_amb_copia =
   object (self)
     method copy = Oo.copy self
   end;;

Mòduls[modifica | modifica el codi]

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
(* o també *)
 let open Nom_fitxer in   (* open 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 | modifica el codi]

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 | modifica el codi]

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

(* 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,[37] interfície del gestor de paquets, proporciona directoris, biblioteques i opcions al compilador segons la info. del fitxer META del paquet.[38]
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).[39][40] 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 | modifica el codi]

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

A voltes dóna 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[modifica | modifica el codi]

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

Generació partint del codi font[modifica | modifica el codi]

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 .cmx *)
ocamlopt fitxer_a.cmx principal.ml -o nom_tasca      (* genera executable nadiu *)

(* cas de compilació a bytecode *)
ocamlc -c fitxer_a.mli fitxer_a.ml                    (* genera .cmi i objecte .cmo *)
ocamlc fitxer_a.cmo principal.ml -o nom_tasca         (* genera executable bytecode *)
amb signatura explícita[modifica | modifica el codi]
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 | modifica el codi]

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

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 | modifica el codi]

Vegeu ref.[43]

 
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 | modifica el codi]

Amb el mòdul Marshal.[44]

Autodocumentació[modifica | modifica el codi]

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

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 (desde versió tal)
@deprecated text (obsolet s'avisa que aquesta funció ...)

Vegeu-ne l'ús a l'exemple més avall. Un exemple, amb i sense el fitxer de signatura, 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 | modifica el codi]

Vegeu [46]

(* 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

dóna 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 | modifica el codi]

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

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

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

Extensions dels tipus de fitxers[modifica | modifica el codi]

de fonts[modifica | modifica el codi]

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

compilats[modifica | modifica el codi]

.cmo
fitxer de codi objecte (codi intermedi bytecode)
.cmi
fitxer d'interfície, compilat
.cmx
fitxer de codi objecte (codi nadiu)
biblioteques[modifica | modifica el codi]
.cma
biblioteca de codi objecte (codi intermedi bytecode)
.cmxa
biblioteca de codi objecte (codi nadiu)
.cmxs  
biblioteca de relligat dinàmic (com .dll/.so) (codi nadiu) (només per a la plataforma amd64 (x86-64))[51]

Gestió de paquets[modifica | modifica el codi]

Findlib[modifica | modifica el codi]

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

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

Per instal·lar un paquet precompilat, descarregat manualment:

ocamlfind install nom_del_paquet fitxer_META

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

Distribució GODI[modifica | modifica el codi]

GODI[36] é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 interfase de menús en una consola de comandes, per a la selecció, instal·lació i actualització dels paquets[53]

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 de 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 | modifica el codi]

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.[54]
Relligat dinàmic amb biblioteques
Vegeu ref.[55] ocamlmklib genera biblioteques estàtiques i dinàmiques partint d'objectes i biblioteques de codi OCaml i codi C.[56]

Portabilitat[modifica | modifica el codi]

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í.[57]

JoCaml[modifica | modifica el codi]

JoCaml[58] és una extensió del llenguatge que implementa l'àlgebra de processos CSP anomenada "càlcul Join"[59] 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 | modifica el codi]

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

export OCAMLJAVA=carpeta_distrib_ocamljava/bin

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

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[62] 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 | modifica el codi]

(-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 | modifica el codi]

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

OCaml per a l'iPhone / iPad[modifica | modifica el codi]

OCaml a l'Android[modifica | modifica el codi]

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

OCaml sobre Xen[modifica | modifica el codi]

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

Exemples[modifica | modifica el codi]

Ent./Sortida[modifica | modifica el codi]

(** 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 linia i l'escriu
   @param inp canal d'entrada
   @raise End_of_file fi de fitxer
   *)
let volca_linia (inp:in_channel) : unit =   
         let linia = 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 | modifica el codi]

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 | modifica el codi]

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[71] 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) [72] 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[73] 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 | modifica el codi]

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.[42]

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 | modifica el codi]

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

open Event
 
type info = Info of int | Plega
 
exception EFinal of string
 
let obtenir_hora : unit -> string =                       (* obtenir hora de 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)[75] (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

dóna:

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 | modifica el codi]

Amb la biblioteca coThreads[76]

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 | modifica el codi]

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

Vegeu també[modifica | modifica el codi]

  • 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 | modifica el codi]