close
Sari la conținut

OCaml

De la Wikipedia, enciclopedia liberă
OCaml
BERJAYA
Logo-ul limbajului
OCaml
Extensii fișiere.ml, .mli
ParadigmăMulti-paradigmă: funcțională, imperativă,[1] orientată pe obiecte, modulară[2]
Apărut în1996; acum 30 ani (1996)[3]
Proiectat deXavier Leroy, Jérôme Vouillon, Damien Doligez, Didier Rémy, Ascánder Suárez
DezvoltatorInstitut national de recherche en informatique et en automatique (Inria)
Ultima versiune5.4.0[4]  Modificați la Wikidata
Tiparecu inferență, statică, puternică
Influențat deC, Caml, Pascal, ML
InfluențeElm, Gleam, F#, Rust, Scala
PlatformăIA-32, x86-64, PowerPC, SPARC, ARM 32-64, RISC-V
Sistem de operareCross-platform
LicențăLGPLv2.1
Prezență onlineocaml.org

OCaml (pronunție în engleză: /ˈkæməl/, anterior Objective Caml) este un limbaj de programare multi-paradigmă, de nivel înalt și de uz general, care extinde dialectul Caml al limbajului ML cu caracteristici orientate pe obiecte. OCaml a fost creat în 1996 de către Xavier Leroy, Jérôme Vouillon,[5] Damien Doligez, Didier Rémy,[6] Ascánder Suárez și alții.

Setul de instrumente OCaml include un interpretor interactiv, un compilator de bytecode, un compilator de cod nativ, un depanator și un manager de pachete (OPAM), împreună cu un sistem de construcție compozabil pentru proiecte mai complexe numit Dune. OCaml a fost dezvoltat inițial în contextul demonstrării automate a teoremelor și este utilizat în analiza statică și în software-ul de metode formale, cum ar fi Frama-C.[7] Dincolo de aceste domenii, și-a găsit utilitatea în programarea de sisteme, dezvoltarea web și sisteme financiare specifice,[8] printre altele.

Acronimul CAML provenea inițial de la Categorical Abstract Machine Language (Limbaj mașină categorizant abstract), dar OCaml omite această mașină abstractă.[9] OCaml este un proiect software liber și cu sursă deschisă, gestionat și întreținut în principal de Institutul Francez de Cercetare în Informatică și Automatică (Inria). La începutul anilor 2000, elemente din OCaml au fost adoptate de multe limbaje, în special F# și Scala.

Limbajele influențate de ML sunt cunoscute în special pentru sistemele lor de tipizare statică și pentru compilatoarele cu inferență a tipurilor. OCaml unește programarea funcțională, imperativă și orientată pe obiecte sub un sistem de tipizare de tip ML. Astfel, programatorii nu trebuie să fie foarte familiarizați cu paradigma limbajelor funcționale pure pentru a utiliza OCaml.

Prin obligarea programatorului să lucreze în limitele sistemului său de tipizare statică, OCaml elimină multe dintre problemele de execuție legate de tipuri, asociate cu limbajele tipizate dinamic, precum Python. De asemenea, compilatorul OCaml reduce considerabil necesitatea adnotărilor manuale de tip, care sunt necesare în majoritatea limbajelor tipizate static. De exemplu, tipurile de date ale variabilelor și semnăturile funcțiilor, de obicei, nu trebuie declarate explicit, așa cum se întâmplă în limbaje precum Java și C#, deoarece acestea pot fi deduse din operatorii folosiți și alte funcții aplicate variabilelor și celorlalte valori din cod. Utilizarea eficientă a sistemului de tipizare OCaml poate necesita o anumită sofisticare din partea programatorului.

OCaml se distinge probabil cel mai mult de alte limbaje cu origini academice prin accentul pus pe performanță. Sistemul său de tipizare statică previne nepotrivirile de tip la momentul execuției și, prin urmare, elimină verificările de tip și de siguranță la execuție care îngreunează performanța limbajelor tipizate dinamic, garantând în același timp siguranța la execuție, cu excepția cazului în care verificarea limitelor vectorilor sau listelor este dezactivată sau când sunt utilizate unele caracteristici nesigure, cum ar fi serializarea. Acestea sunt suficient de rare încât evitarea lor este foarte posibilă în practică.

În afară de costul verificării tipurilor, limbajele de programare funcțională sunt, în general, greu de compilat în cod mașină eficient, din cauza unor probleme precum problema funarg. Pe lângă optimizările standard de bucle, regiștri și instrucțiuni, compilatorul utilizează metode de analiză statică a programelor pentru a optimiza împachetarea valorilor (boxing) și alocarea funcțiilor lambda (closures), ajutând la maximizarea performanței codului rezultat, chiar dacă acesta utilizează intensiv construcții de programare funcțională.

Xavier Leroy a declarat că „OCaml oferă cel puțin 50% din performanța unui compilator C decent”,[10] deși o comparație directă este imposibilă. Unele funcții din biblioteca standard OCaml sunt implementate cu algoritmi mai rapizi decât funcțiile echivalente din bibliotecile standard ale altor limbaje. De exemplu, implementarea reuniunii seturilor din biblioteca standard OCaml este, în teorie, asimptotic mai rapidă decât funcția echivalentă din bibliotecile standard ale limbajelor imperative (de exemplu, C++, Java), deoarece implementarea OCaml poate exploata imutabilitatea seturilor pentru a reutiliza părți din seturile de intrare în rezultatul de ieșire.

BERJAYA
Echipa de dezvoltare OCaml primind un premiu la Symposium on Principles of Programming Languages (POPL) 2024

Dezvoltarea ML (Meta Language)

[modificare | modificare sursă]

Între anii 1970 și 1980, Robin Milner, un informatician britanic și câștigător al premiului Turing, a lucrat la Laboratorul pentru Fundamentele Informaticii al Universității din Edinburgh.[11][12] Milner și alții lucrau la demonstratoare automate de teoreme, care erau dezvoltate istoric în limbaje precum Lisp.[12] Milner s-a lovit în repetate rânduri de problema în care demonstratoarele de teoreme încercau să susțină că o demonstrație este validă prin îmbinarea unor non-demonstrații. Ca urmare, a continuat prin a dezvolta un meta-limbaj pentru sistemul său Logic for Computable Functions, care să permită scriitorului să construiască doar demonstrații valide prin intermediul unui sistem de tipizare polimorfic.[13] ML a fost transformat într-un compilator pentru a simplifica utilizarea LCF pe diferite mașini și, până în anii 1980, a fost transformat într-un sistem complet de sine stătător.[13] ML avea să servească în cele din urmă ca bază pentru crearea OCaml.

La începutul anilor 1980, au existat câteva evoluții care au determinat echipa Formel de la Inria să devină interesată de limbajul ML. Luca Cardelli, profesor de cercetare la Universitatea din Oxford, a folosit „mașina sa abstractă funcțională” pentru a dezvolta o implementare mai rapidă a ML, iar Robin Milner a propus o nouă definiție a ML pentru a evita divergența între diferitele implementări. Simultan, Pierre-Louis Curien, cercetător principal la Universitatea Paris-Diderot, a dezvoltat un calcul al combinatorilor categoriali și l-a legat de calculul lambda, ceea ce a dus la definirea „mașinii abstracte categoriale” (CAM). Guy Cousineau, cercetător la Universitatea Paris-Diderot, a recunoscut că aceasta ar putea fi aplicată ca metodă de compilare pentru ML.[14]

Prima implementare

[modificare | modificare sursă]

Caml a fost proiectat și dezvoltat inițial de echipa Formel de la INRIA, condusă de Gérard Huet. Prima implementare a Caml a fost creată în 1987 și a fost dezvoltată în continuare până în 1992. Deși a fost coordonată de Ascánder Suárez, Pierre Weis și Michel Mauny au continuat dezvoltarea după plecarea acestuia în 1988.[14]

Guy Cousineau este citat amintindu-și de experiența sa în implementarea limbajelor de programare ca fiind inițial foarte limitată și că au existat multiple inadecvări de care este responsabil. În ciuda acestui fapt, el consideră că „Ascander, Pierre și Michel au făcut o treabă destul de bună”.[14]

Objective Caml

[modificare | modificare sursă]

Didier Rémy și Jérôme Vouillon au proiectat un sistem de tipizare expresiv pentru obiecte și clase, care a fost integrat în Caml Special Light. Acest lucru a dus la apariția limbajului Objective Caml, lansat pentru prima dată în 1996 și redenumit ulterior în OCaml în 2011. Acest sistem de obiecte a făcut tehnicile orientate pe obiecte prevalente la vremea respectivă sigure din punct de vedere al tipizării statice, în timp ce aceleași tehnici cauzau instabilitate sau necesitau verificări la execuție în limbaje precum C++ sau Java. În 2000, Jacques Garrigue a extins Objective Caml cu multiple caracteristici noi, cum ar fi metode polimorfice, variantele și argumentele etichetate și opționale.[13][14]

Dezvoltarea în ziua de astăzi

[modificare | modificare sursă]

Îmbunătățirile aduse limbajului au fost adăugate incremental în ultimele două decenii pentru a susține bazele de cod comerciale și academice în creștere ale OCaml.[13] Lansarea OCaml 4.0 în 2012 a adăugat Tipuri de Date Algebrice Generalizate (GADTs) și module de primă clasă pentru a crește flexibilitatea limbajului.[13] Versiunea OCaml 5.0.0 lansată în 2022[15] reprezintă o rescriere completă a runtime-ului limbajului, eliminând blocarea globală a colectorului de gunoi (global GC lock) și adăugând gestionari de efecte (effect handlers) prin continuări delimitate. Aceste schimbări permit suportul pentru paralelism cu memorie partajată și, respectiv, pentru concurența de tip "color-blind".

Dezvoltarea OCaml a continuat în cadrul echipei Cristal de la Inria până în 2005, când a fost urmată de echipa Gallium.[16] Ulterior, Gallium a fost urmată de echipa Cambium în 2019.[17][18] Începând din 2023, există 23 de dezvoltatori de bază ai distribuției compilatorului din diverse organizații[19] și 41 de dezvoltatori pentru ecosistemul mai larg de instrumente și pachete OCaml.[20] În 2023, compilatorul OCaml a fost recunoscut cu premiul Programming Languages Software Award al ACM SIGPLAN.

Exemple de cod

[modificare | modificare sursă]

Exemplele de cod OCaml sunt cel mai ușor studiate când sunt introduse în REPL-ul OCaml. Un REPL (din englezul Read-Eval-Print-Loop) este un interpretor interactiv ce evaluează cod OCaml introdus de la tastatură în timp real. De menționat că deși în REPL expresiile trebuie terminate cu ;;, în codul dinfișierele sursă, acest lucru nu este necesar. De exemplu, într-un shell bash:

$ ocaml
OCaml version 5.4.0
Enter #help;; for help.

#

Codul poate fi introdus după caracterul „#”. De exemplu, pentru a calcula 2 + 3:

$ ocaml
OCaml version 5.4.0
Enter #help;; for help.

# 2 + 3;;
- : int = 5

Interpretorul returnează rezultatul calcului și tipul de date găsit prin inferență. Pentru că am folosit operator + cu două literale de tip întreg, interpretorul inferă că rezultatul va fi de tip int, adică tot un număr întreg. Dacă în schimb am fi folosit operatorul +. și literale de tip număr cu virgulă mobilă, rezultatul ar fi fost altul:

# 2. +. 3.;;
- : float = 5.

Într-adevăr, dacă vrem să verificăm semnătura unei funcții pentru a vedea ce tipuri admite, o putem introduce direct. Operatorii folosiți mai devreme sunt de asemenea funcții:

# (+);;
- : int -> int -> int = <fun>
# (+.);;
- : float -> float -> float = <fun>

Clasicul exemplu „Hello world” se poate implementa așa:

# print_endline "Hello world";;
Hello world
- : unit = ()

unit sau () este un rezultat care semnifică că funcția chemată nu returnează o valoare, ci în schimb are numai efecte laterale. În cazul acesta, scrie un șir de caractere pe ecran.

Ca și în alte limbaje de programare funcțională, executarea unei bucle de cod se face prin recursivitate, adică o funcție care se cheamă pe ea însăși pentru a efectua mai mulți pași într-un calcul. Un mod simplu, dar ineficient, de a calcula al n-lea număr din șirul lui Fibonacci este dat de:

let rec fibonacci n = 
  match n with 
  | 0 -> 0
  | 1 -> 1
  | n -> fibonacci (n - 2) + fibonacci (n - 1)

O variantă mai eficientă este folosirea unei funcții recusive la coadă (tail-recursive). Aceste funcții au proprietatea că ultimul lucru pe care fiecare apel recursiv îl face este să producă rezultatul final al calculului, înainte să se întoarcă la funcția care a apelat-o. Acest lucru permite optimizarea apelulilor recursive într-o buclă imperativă la compilare. În contrast cu funcția de mai sus, care face calculul final (adunarea) după ce cele două apeluri recursive se întorc.

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

Acest artificiu necesită doi parametrii noi, drept care facem o funcție nouă, fob_aux, care este accesibilă doar în interiorul funcției fibonacci pentru a ține spațiul de nume global curat și pentru a ascunde acest detaliu de implementare de utilizatorii funcției. Am fi putut face același lucru folosind parametrii opționali. Acum, rezultatul calculului este în parametrul b, numit acumulatorul funcției. n este folosit ca și contor pentru a știi când să oprim apelul recursiv. Când devine 0, returnăm acumulatorul.

  1. „Imperative Ocaml”. Realworld OCaml. Accesat în .
  2. „Modules · OCaml Documentation” (în engleză). OCaml. Accesat în .
  3. Leroy, Xavier (). „Objective Caml 1.00”. caml-list mailing list.
  4. „OCaml 5.4.0”. Accesat în .
  5. „Jérôme Vouillon”. www.irif.fr. Accesat în .
  6. „Didier Remy”. pauillac.inria.fr. Accesat în .
  7. „Frama-C repository on Gitlab”.
  8. „Jane Street Technology”. Accesat în .
  9. „A History of OCaml”. Accesat în .
  10. Linux Weekly News.
  11. „A J Milner - A.M. Turing Award Laureate”. amturing.acm.org. Accesat în .
  12. 1 2 Clarkson, Michael; et al. „1.2. OCaml: Functional Programming in OCaml”. courses.cs.cornell.edu. Accesat în .
  13. 1 2 3 4 5 „Prologue - Real World OCaml”. dev.realworldocaml.org. Accesat în .
  14. 1 2 3 4 „A History of OCaml – OCaml”. v2.ocaml.org. Accesat în .
  15. „Release of OCaml 5.0.0 OCaml Package”. OCaml (în engleză). Arhivat din original la . Accesat în .
  16. „Projet Cristal”. cristal.inria.fr. Accesat în .
  17. „Gallium team - Home”. gallium.inria.fr. Accesat în .
  18. „Home”. cambium.inria.fr (în engleză). Accesat în .
  19. „OCaml compiler governance and membership”. .
  20. „OCaml governance and projects”. .