Az ML programozási nyelv

Bevezetés

Leírás

Az ML egy klasszikus funkcionális programozási nyelv ennek minden előnyével és hátrányával. Ezen tulajdonságok dióhéjban: nincsenek a nyelvben változók (csak ún. kötések vannak), nem szerepelhetnek a programokban értékadások, semelyik objektum értéke nem változhat meg a program futása során (amit átlátszóságnak hívnak) és a megszokott szekvencia / elágazás / ciklus helyett a függvény kompozíció / mintaillesztés / rekurzió áll a rendelkezésünkre. Ezen tulajdonságaik könnyen analizálhatóvá teszik a funkcionális programozási nyelveket. A funkcionális programozási nyelveket deklaratívnak tekintik szemben a harmadik generációs ún. imperatív programozási nyelvekkel.

Az ebben a leírásban szereplő összes példaprogram Standard ML alatti futtatásra lett tervezve. A példák használhatóak a "New Jersey ML" -el és a "Edinburgh ML" -el is, és működniük kellene az összes többi ML verzióval is (azonban például a Moscow ML -el akadnak problémák). Az ML promptja "-". A beírt kifejezések azonnal kiértékelődnek és a legtöbbször az eredmény típusával együtt kerülnek kiírásra. A kifejezések ";" -vel vannak terminálva. Például "New Jersey ML" -t használva a következő dialógus történik:

- "Hello World";

val it = "Hello World" : string

Normál módban az ML kifejezéseket fogad el és kiértékeli őket. Az eredményt a képernyőre írja a kifejezés típusával együtt. Az utolsó kiszámolt eredményre az "it" kulcsszóval lehet hivatkozni, amely nem egy változó, hanem egy ún. kötés az ML -ben. A fenti példában az értelmezőnek nem kell csinálnia semmit, hogy a kifejezés értékét kiszámolja, mivel a kifejezés már a legegyszerűbb formájában van, amit normálformának is hívnak. Egy sokkal trükkösebb ;-) példa a "3 + 4" amit az ML értelmező 7 -re értékel.

- 3+4;

it = 7 : int

Minden kiértékelésre szánt kifejezésnek ";" -vel kell végződnie. Az értelmező megengedi, hogy egy kifejezés több mint egy sorból álljon. Ha ez történik a prompt átvált "=" -re, például:

- 4 + 4 +

= 4;

val it = 12 : int

A funkcionális programozási paradigma

A funkcionális programozás két fő jellemzője az érték (a függvényérték is érték) és a függvényalkalmazás. A funkcionális programozás nevét a függvények kitüntetett szerepének köszönheti. A tisztán funkcionális programozási nyelvek a matematikában megszokott függvényfogalmat valósítják meg: a függvény egyértelmű leképezés a függvény argumentuma és eredménye között, a függvény alkalmazásának nincs semmilyen más hatása. Tisztán funkcionális programozás esetén tehát nincs állapot, nincs (mellék)hatás, nincs értékadás.

Funkcionális program pl. az e e1 kifejezés, ahol az e-nek függvényértéket eredményező kifejezésnek kell lennie, az e1 pedig tetszőleges kifejezés lehet. A matematikában megszokott módon azt mondjuk, hogy az e függvényt (vagy kissé körülményesebben: az e függvényértéket adó kifejezést) alkalmazzuk az e1 argumentumra. Függvények alkalmazásáról lévén szó, funkcionális helyett szinonimaként gyakran applikatív programozásról beszélünk.

Az applikatív programozás elmélete a λ-kalkulus, az a függvényelmélet, amelyet Alonzo Church az 1930-as években dolgozott ki, majd Moses Schönfinkel és Haskell Curry fejlesztett tovább. A λ-kalkuluson alapuló első funkcionális programozási nyelvet, a liSP-et (liSt Programming) John McCarthy dolgozta ki az 1950-es évek közepén, az 1960-es évek elején. A sokféle változat közül a professzionális célokra alkalmazható Common liSP a legismertebb. A liSP-dialektusok és modernebb utódjuk, a Scheme is típus nélküli nyelvek.

Az első típusos funkcionális nyelv az ML (Meta Language) egyik korai változata volt a 70-es évek közepén, amelyben R. Milner megvalósította típuselméleti eredményeit. Eredetileg logikai állítások igazolásra, tételbizonyításra tervezték, erre utal a nem túl ötletes Meta Language elnevezés is. A HOPE-pal és más funkcionális nyelvekkel szerzett tapasztalatok alapján dolgozták ki a Standard ML (SML) nyelvet a 80-as évek közepétől kezdve. Számos megvalósítása készült el különféle számítógépekre, és természetesen megjelentek különféle dialektusai is, pl. a Caml. Az SML-családba tartozó nyelvek, kevés kivétellel, ún. mohó kiértékelést, azaz érték szerinti paraméterátadást alkalmaznak. Ez azt jelenti, hogy amikor egy függvénykifejezést alkalmazunk egy argumentumra, akkor az SML-értelmező először az argumentumot értékeli ki, és csak ezután lát hozzá a függvénykifejezés kiértékeléséhez.

A Miranda, az 1990-ben megjelent Haskell és a még újabb Clean nyelv ezzel szemben lusta kiértékelést használ. A lusta kiértékelés az 1960-as években az Algol nyelvben alkalmazott név szerinti paraméterátadás modern leszármazottja; nem tévesztendő össze a Pascalban, a C-ben és más nyelvekben használt cím szerinti paraméterátadással. Megjegyzendő, hogy Lazy SML néven az SML-nek is van lusta kiértékelésű változata.

Az SML – akárcsak a körülményes szintaxisú, típus nélküli Common liSP – gyakorlati programozási feladatok megoldására készült, ezért nemcsak a tisztán funkcionális, hanem az imperatív stílusú programozáshoz szükséges nyelvi elemek is megtalálhatók benne: frissíthető változók, tömbök, mellékhatással járó függvények, stb., továbbá a nagybani programozást segítő fejlett modulrendszere van.

Az SML-t, más deklaratív nyelvekhez hasonlóan, rendszerint értelmezőprogrammal (interpreterrel) valósítják meg: az értelmezőprogram a kifejezéseket beolvassa és kiértékeli, majd kiírja az eredményt, és azután ismét a beolvasással folytatja (ezt nevezik read-eval-print ciklusnak).

Az ún. Hivatkozási átlátszóság (referential transparency) megléte vagy hiánya fontos jellemzője a programázási nyelveknek. Ha egy programnyelv, mint pl. az SML, rendelkezik ezzel a tulajdonsággal, akkor ez azt jelenti, hogy egyenlők helyettesíthetők egyenlőkkel, pl. egy kifejezés az értékével, az E1 + E2 kifejezés az E2 + E1 kifejezéssel (ahol a + jel a kommutatív aritmetikai összeadást jelent).

A hivatkozási átlátszóság megléte esetén egy kifejezés értelme, jelentése egyszerűen a kiértékelésének az eredménye, és ezért egyes részkifejezéseit egymástól függetlenül lehet kiértékelni. Ezzel szemben egy parancs végrehajtása azt jelenti, hogy a program állapota megváltozik, vagyis a parancs megértéséhez meg kell érteni a parancs hatását a program teljes állapotterére.

A funkcionális program: mennyiségek közötti kapcsolatokat leíró egyenletek halmaza.

Pl. a square(x) = x * x megfelelő alakú egyenlet, ún. kiszámítási szabály. Ezzel szemben a sqrt(x) * sqrt(x) = x alakú egyenlet csak deklarálja a kívánt tulajdonságokat, kiszámításra nem, csupán ellenőrzésre alkalmas.

SML-értelmezők és fordítók

Az SML-nyelvnek két szintje van. A nyelv magját az alapnyelv (Core Language) képezi, a nagyobb programok írását a modulnyelv (Module Language) támogatja. A nyelvbe beépített elemeket gazdag és egyre bővülő alapkönyvtár (Basis Library) egészíti ki. Az SML-nyelv és az alapkönyvtár definícióját legutóbb 1996-ban vizsgálták felül. Az első ML-értelmezőt 1977-ben írták az edinborough-i egyetemen. Az évek során számos értelmező és fordítóprogram készült el, egy részük licencköteles, más részük szabadon használható. Az utóbbiak közül kettőt ajánlunk (mindkettő már az 1997-es, módosított definíciót követi):

A funkcionális nyelvek általában interaktívak, megvalósításukra értelmezőprogramot (interpertert) írnak. Az SML-értelmezők és a programozók többek között a készenléti jel, a folytató-jel, a kiértékelő-jel és a válaszjel révén társalognak egymással. Az alábbi táblázatban a baloldali oszlopban a mosml, ill. smlnj által használt jeleket adjuk meg: Az SML-ből kilépni a készenléti jelre adott többféle válasszal lehet.

Az ML - Meta-Language - programozási nyelvcsalád

 

family tree of ML based languages
Amber Infer LML - Lazy ML JoCaml Objective-Caml OLABL PFL

Általánosítja az ML típus-rendszerét, valamint kiegészíti CSP (Communicating Sequential Processes)-vel, ami magában foglalja többek közt a többszörös és ismétlődő öröklődést. Mind a statikus, mind a dinamikus típust megvalósítja.

Az ML és a SCHEME nyelv legjobb tulajdonságait ötvözi.

Lusta, funkcionális ML2 változat
G-gépen lett implementálva, és Haskell B fordítókat hoztak létre vele.

Logikai ML
Ez egy új adattípussal egészíti ki az ML-t, mellyel könnyű reprezentálni a logikai problémákat

Kísérleti kiterjesztése az Objective-Caml nyelvnek, egy osztott kapcsolat-számítási programozási modellel. Ez a modell tartalmaz egy magas-szintű kommunikációs és szinkronizációs csatornát, mobil közvetítőt, hibakeresőt és automatikus irányítást

A Caml egy biztonságos nyelv. A fordító számos "józan" ellenőrzést elvégez a programon, még a fordítás előtt. Éppen ezért, rengeteg programozási hiba egyáltalán nem fordul elő a Camlban: Adat-típus tévesztés, lehetetlen hibás hozzáférés egy összetett típushoz. A Caml statikus típus-ellenőrzést végez, és nincs szükség megadni a típus-információkat, mint az Adában, a Pascalban, vagy a C-ben. A Caml Light egy alap-rendszer, az Objective Caml pedig egy hatékony modul-rendszerrel rendelkezik, támogatja az objektum-orientált programozást és optimalizáló fordítót alkalmaz

Az Objectiv-Caml egy kiterjesztettje nyílt összeg típussal, nevezett és operatív funkcionállal.

Persistent Functional Language
Egy funkcionális adatbáziskezelő nyelv

Parallel Functional Language
Az első párhuzamos kiterjesztése az ML-nek
Ennél újabb a Poly/ML és a Concurrent ML



ML:
Meta Language Haladó nyelvek egy családja, általában funkcionális kontroll szerkezettel, szigorú szemantikával, szigorú polimorfikus típus-rendszerrel és paraméterezhető modulokkal. A család tagjai: Standard ML (1984), Lazy ML, Caml, Caml Light és további változatos "kutatási" nyelvek. Az implementációi a legtöbb platformon elérhetőek, úgymint PC-n, mainframe-n, a legtöbb munkaállomáson, multiprocessoros és szuperszámítógépeken. Az ML-t rengetegen használják, a legtöbb egyetemen oktatják - néhányon ezzel kezdenek.
Manipulator Language Az IBM nyelv robotjai irányításában használja.



Az ELTE programtervező matematikus szakának programozási nyelvek sávján elvégezhető Programozási nyelvek 3, illetve Programozási nyelvek 4 című tárgyak keretében már feldolgozott nyelvek: