OCaml
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.
Tipus | llenguatge de programació orientat a objectes, llenguatge de programació funcional, llenguatge de programació multiparadigma, llenguatge de programació imperatiu, dialect (en) i programari lliure i de codi obert |
---|---|
Data de creació | 1996 |
Disseny | Xavier Leroy i Damien Doligez |
Desenvolupador | INRIA |
Epònim | Caml |
Paradigma de programació | programació funcional, llenguatge imperatiu, programació orientada a objectes i programació modular |
Darrera versió estable | 5.2.0 () |
Dialecte de | ML |
Llenguatge de programació | OCaml i C |
Influenciat per | ML Estàndard |
Sistema operatiu | Unix-like |
Extensió dels fitxers | ml i mli |
Llicència | Q Public License (en) i GNU LGPL 2.1 |
Etiqueta d'Stack Exchange | Etiqueta i Etiqueta |
Pàgina web | ocaml.org |
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
modificaOCaml 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
modificaEl 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
modificaOCaml 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, ...
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
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
modificaDe 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
modificatipus bàsics
modificatype 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
modificaenumerats
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
modificaEl 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
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
modificaDe l'anglès "Phantom types". Tipus sense definició o amb paràmetres de tipus que no intervenen a la definició.
expressions
modificalligant 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
modificaif ''expr'' then ''expr'' else ''expr'' ;; match ''expr'' with ''patró'' when ''expr'' -> ''expr'' | ''patró'' | ''patró'' | ''patró'' when ''expr'' -> ''expr'' ... ;;
declaracions locals
modificaamb à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
modificaPermet 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
modificaLa que modifica l'estat.
registres amb camps mudables
modificatype 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
modificalet _ = 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
modificaexpressió_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)
modificaVegeu[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)
modificalet 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
modificaIncorporant 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
modificaLa 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
modificaEls 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
modificaLes 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
modificaA 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
modificaPer 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
modificaUnió 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)
modificaVegeu[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
modificaclass 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
modificaUn 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ó
modificaEn 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
modificaper 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
modificaOcamlMkTop[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
modificaSeqüència d'especificacions de tipus dels identificadors exportats per un mòdul.
Generació partint del codi font
modificaEn 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
modificamodule 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
modificaA 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
modificaVegeu 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) ;;
Amb el mòdul Marshal.[43]
Autodocumentació
modificaEls 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
modificaVegeu[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:
- Alain Frish Getopt Arxivat 2009-04-05 a Wayback Machine. a l'estil de GNU GetOpt.
API Estàndard
modificaLes 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
modificade 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
modificaFindlib
modificaEl 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
modificaGODI[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
modificaEn 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
modificaEl 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
modificaJoCaml[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
modificaAmb el programari OCamlJava[59][60] Requereix JVM >= 1.6
export OCAMLJAVA=carpeta_distrib_ocamljava/bin
Execució de codi intermedi OCaml (projecte Cadmium)
modificaocamlc 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 Toplevel:[68] intèrpret (de Keigo Imai) descarregable des del Google Market
- OpenMirage[69] és una implementació per desenvolupar micronuclis servidors d'aplicacions Núvol d'internet sobre l'hipervisor Xen (sense sistema operatiu).
Exemples
modificaEnt./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
modificaEstalvien 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))
;;
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
modificaA 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
modificaMé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
modificaAmb la biblioteca coThreads[75]
A l'Ubuntu:
sudo apt-get install libcothreads-ocaml-dev
Vegeu també:
- MVars al Haskell
- STM al GHC
(* 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,0 1,1 Canvi de nom de Objective Caml a OCaml
- ↑ El llenguatge Caml i extensions(anglès)
- ↑ Microsoft Research - F sostingut -- "El nucli del llenguatge és compartit amb OCaml"(anglès)
- ↑ Linux Weekly News (anglès)
- ↑ The Computer Language Benchmarks Game - GNU GCC respecte de OCaml[Enllaç no actiu](anglès)
- ↑ Plataformes suportades(anglès)
- ↑ Intèrpret ocaml o toplevel Arxivat 2010-08-09 a Wayback Machine.(anglès) Avaluador d'expressions de nivell global (toplevel)
- ↑ Quin sistema és més eficient, Haskell o bé OCaml?(anglès)
- ↑ Garbage Collection(anglès) Recol·lecció de memòria brossa
- ↑ Ocaml-tutorial - Garbage collection(anglès)
- ↑ Ubuntu - modalitats per a temps-real(anglès)
- ↑ IDEone - Interfícies amb l'entorn d'execució remota
- ↑ És possible escriure tires de text literal als fonts en UTF-8 en OCaml? Arxivat 2009-01-07 a Wayback Machine. (anglès)
- ↑ nova sintaxi opcional(anglès)
- ↑ Guia sobre la sintaxi revisada(anglès)
- ↑ «Literals int32, int64, nativeint». Arxivat de l'original el 2010-08-13. [Consulta: 13 setembre 2010].
- ↑ mòdul String Tipus String(anglès)
- ↑ Vectors - mòdul Array(anglès)
- ↑ Variant types (anglès)
- ↑ 20,0 20,1 Biblioteca Camomile de tipus Unicode
- ↑ Control d'accés estàtic fent servir Phantom types(anglès)
- ↑ Tipus fantasmes (Phantom types) a Haskell(anglès)
- ↑ Expressions(anglès)
- ↑ «Operador de composició (-|)». Arxivat de l'original el 2014-10-21. [Consulta: 13 febrer 2013].
- ↑ «Opcions de compilació». Arxivat de l'original el 2009-12-04. [Consulta: 6 desembre 2009].
- ↑ Optional arguments
- ↑ operadors infix(anglès)
- ↑ taula de precedències i associativitat dels operadors(anglès)
- ↑ Objectes a OCaml Arxivat 2009-11-29 a Wayback Machine. (anglès)
- ↑ finalitzadors al recollidor de brossa(anglès)
- ↑ Objectes funcionals Arxivat 2009-11-29 a Wayback Machine.(anglès)
- ↑ Tipus i Genericitat (anglès)
- ↑ Clonant objectes Arxivat 2009-11-29 a Wayback Machine.(anglès)
- ↑ 34,0 34,1 El gestor de paquets Findlib (anglès)
- ↑ 35,0 35,1 GODI distribució en codi font(anglès)
- ↑ 36,0 36,1 Ocamlfind, interfície del gestor de paquets(anglès)
- ↑ Linker options(anglès) Vegeu opcions als fitxers META dels paquets.
- ↑ Opcions del compilador ocamlc Arxivat 2009-12-04 a Wayback Machine. (anglès) Vegeu opció -pack
- ↑ Makefile del paquet Camomile (anglès) Genera mòdul CamomileLibrary per empaquetament (-pack) dels altres fitxers objecte
- ↑ OcamlMkTop(anglès)
- ↑ 41,0 41,1 Substitució del tipus en signatures Arxivat 2010-08-13 a Wayback Machine.(anglès)
- ↑ Mòduls parametritzats(anglès)
- ↑ Serialització: mòdul Marshal
- ↑ El generador d'autodocumentació Arxivat 2009-02-20 a Wayback Machine. (anglès)
- ↑ Biblio estàndard Arg (anglès)
- ↑ «Operacions predefinides». Arxivat de l'original el 2009-04-17. [Consulta: 27 abril 2009].
- ↑ «Tipus i excepcions predefinits». Arxivat de l'original el 2009-04-27. [Consulta: 27 abril 2009].
- ↑ Biblioteques estàndards
- ↑ 49,0 49,1 Lexer and parser generators (ocamllex, ocamlyacc) Arxivat 2019-08-16 a Wayback Machine. (anglès)
- ↑ Generació de biblioteques de relligat dinàmic (opció -shared) Arxivat 2010-08-26 a Wayback Machine.(anglès) "Només a la plataforma amd64"
- ↑ Fitxer META(anglès)
- ↑ Paquets del rebost del GODI(anglès)
- ↑ El programa ocamlrun Arxivat 2010-08-09 a Wayback Machine.(anglès)
- ↑ biblioteques de relligat dinàmic (DLL) Arxivat 2010-08-09 a Wayback Machine. (anglès)
- ↑ El programa ocamlmklib Arxivat 2010-07-24 a Wayback Machine.(anglès)
- ↑ Portabilitat - Plataformes suportades(anglès)
- ↑ Jocaml
- ↑ Join calculus tutorial
- ↑ OcamlJava(anglès)
- ↑ Presentació OcamlJava Paris 2008(anglès)
- ↑ Cadmium per a l'Android(anglès)
- ↑ 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
- ↑ ocamljs al viver GitHub(anglès)
- ↑ js_of_ocaml - traductor a javascript(anglès)
- ↑ Compilador Ocaml creuat per a l'iphone (anglès)
- ↑ Compilador creuat Ocaml per a l'iPhone treballant dins l'entorn Xcode
- ↑ OCaml a l'iPad(anglès)
- ↑ Keigo Imai - OCaml Toplevel a l'Android Arxivat 2011-07-15 a Wayback Machine.(japonès)
- ↑ OpenMirage Arxivat 2015-05-10 a Wayback Machine.(anglès) Desenvolupament sobre Xen
- ↑ biblioteca lablGTK de connectors a GTK i Glade(anglès)
- ↑ format libGlade: GladeXML v.2.0 Arxivat 2010-07-02 a Wayback Machine.(anglès)
- ↑ Traductor obsolet ml-glade (glade v.1 a ocaml)(anglès)
- ↑ Unix system programming in Objective Caml(anglès)
- ↑ Biblioteca threads Arxivat 2010-08-27 a Wayback Machine.(anglès)
- ↑ 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- Web oficial de OCaml (anglès)