Η ML είναι μια συναρτησιακήγλώσσα προγραμματισμού γενικής χρήσης, που αναπτύχθηκε από τον Ρόμπιν Μίλνερ και άλλους στο τέλος της δεκαετίας του 1970 στο πανεπιστήμιο του Εδιμβούργου.[1] Ξεκίνησε ως μέτα-γλώσσα (εξού και το όνομα Meta-Language) για διαδραστικές αποδείξεις στο σύστημα Edinburgh LCF (τα αρχικά για "Logic for Computable Functions" - λογική για υπολογίσιμες συναρτήσεις) και εξελίχθηκε σε γενικής χρήσης γλώσσα προγραμματισμού για να καλύψει τις ανάγκες αυτής της εφαρμογής.[2]
Τα συναρτησιακά στοιχεία της γλώσσας είναι εμπνευσμένα από την ISWIM και την GEDANKEN[1]: iii , αλλά διαφέρει στον χειρισμό των τύπων,[3] ενώ άλλα στοιχεία της γλώσσας είναι εμπνευσμένα από τη Lisp and την POP2.[4]:89 Το συγκεκριμένο σύστημα αποδείξεων του LCF ήταν το PPLambda, που είναι συνδυασμός του πρωτοβάθμιου προτασιακού λογισμού (first-order predicate calculus) και του πολυμορφικού λογισμού λάμδα με απλούς τύπους (simply-typed polymorphic lambda-calculus). Αλλά, σύμφωνα με τον Ρόμπιν Μίλνερ, σχεδόν οποιδήποτε επαγωγικό σύστημα θα είχε οδηγήσει στις ίδιες βασικές αρχές της γλώσσας.[2]
Η ML είναι γνωστή για τη χρήση του αλγόριθμου Χίντλεϋ-Μίλνερ[5][6] για την εξαγωγή τύπων (type inference), που μπορεί να συνάγει αυτόματα τους τύπους των περισσοτέρων εκφράσεων της γλώσσας, χωρίς να χρειάζεται σαφείς προσδιορισμούς τύπων από τον προγραμματιστή. Είναι μία από τις λίγες γλώσσες προγραμματισμού με αυστηρή απόδειξη ότι όλα τα προγράμματα που περνάνε τον έλεγχο τύπων, δεν έχουν σφάλματα κατά την εκτέλεση.[6]
Αντίθετα με τη Haskell, η ML χρησιμοποιεί πρόθυμη αποτίμηση (eager evaluation), που σημαίνει ότι όλες οι υπο-εκφράσεις μιας έκφρασης αποτιμώνται. Αποτέλεσμα είναι ότι δεν μπορούν να χρησιμοποιηθούν άπειρες λίστες ως έχει. Παρόλα αυτά, η οκνηρή αποτίμηση (lazy evaluation) και επομένως και οι άπειρες δομές, μπορούν να προσομοιωθούν με τη χρήση ανώνυμων συναρτήσεων.[7]:191-211
Σήμερα υπάρχουν αρκετές γλώσσες στην οικογένεια της ML. Οι δύο κύριες διάλεκτοι είναι η Standard ML και η Caml, αλλά υπάρχουν και άλλες, όπως η F#, η Lazy ML, η οποία υποστηρίζει οκνηρή αποτίμηση[8] και η CakeML, η οποία είναι ένα υποσύνολο της ML με τυπική σημασιολογία επιβεβαιωμένη στο HOL.[9] Ιδέες από την ML έχουν επηρεάσει πολυάριθμες άλλες γλώσσες όπως τη Haskell, τη Cyclone και τη Nemerle.
Τα δυνατά σημεία της ML συγκεντρώνονται συνήθως στο σχεδιασμό και χειρισμό γλωσσών (μεταγλωττιστές, αναλυτές,[10] αποδείκτες θεωρημάτων όπως το HOL και το Isabelle,[11] κλπ), και γι'αυτό έχει χρησιμοποιηθεί αρκετά στην έρευνα γλωσσών προγραμματισμού. Ως συναρτησιακή γλώσσα επιτρέπει την εύκολη υλοποίηση διαχρονικών δομών δεδομένων.[12] Έχει χρησιμοποιηθεί επίσης στη βιοπληροφορική[13], σε οικονομικά συστήματα, και άλλες εφαρμογές, αλλά εκεί πιο διαδεδομένες είναι παραλλαγές της γλώσσας όπως η OCaml.[14]
Παραδείγματα χαρακτηριστικών γλώσσας
Εξαγωγή τύπων
Η ML επιτρέπει την αυτόματη εξαγωγή τύπων.[7]: 63-67 Για παράδειγμα, στην παρακάτω συνάρτηση οι δηλωτές (type specifiers) είναι περιττοί:
funincrement(n:int):int=n+1
Η συνάρτηση μπορεί να γραφτεί ως εξής:
funincrementn=n+1
και η γλώσσα θα εξαγάγει τον τύπο της μεταβλητής ως ακέραιο και της συνάρτησης increment ως συνάρτηση από ακεραίους σε ακεραίους:
valincrement=fn:int->int
Αναδρομή
Η ML υποστηρίζει και ενθαρρύνει την χρήση αναδρομικών συναρτήσεων[15]:54-60 (και αναδρομικών τύπων). Για παράδειγμα η παρακάτω συνάρτηση υπολογίζει το παραγοντικό.
Επίσης υποστηρίζει αμοιβαία αναδρομή (mutual recursion).[15]: 60-62 Για παράδειγμα, οι παρακάτω συναρτήσεις ελέγχουν (με μη αποδοτικό τρόπο) αν ένας μη-αρνητικός ακέραιος είναι ζυγός ή περιττός, καλώντας η μία την άλλη:
Στην ML οι συναρτήσεις είναι τιμές, που σημαίνει ότι μπορούν να δοθούν ως όρισμα μίας συνάρτησης ή να επιστραφούν ως αποτέλεσμα μίας συνάρτησης.[7]: 171-191 Μία συνάρτηση με όρισμα ή αποτέλεσμα μία άλλη συνάρτηση, λέγεται συνάρτηση ανωτέρου βαθμού.
Συνάρτηση ως όρισμα
Για παράδειγμα, η παρακάτω συνάρτηση παίρνει ως όρισμα μία οποιαδήποτε συνάρτηση και επιστρέφει το άθροισμα :
Για παράδειγμα, η παρακάτω συνάρτηση παίρνει ως όρισμα και επιστρέφει την γραμμική συνάρτηση της μορφής :
funlinear_fn(a,b)=letfunnew_fnx=a*x+binnew_fnend;;(* Παρατηρήστε ότι ο τύπος της συνάρτησης επιβεβαιώνει αυτό που θέλουμε όρισμα: int * int (δηλαδή (a,b)) και αποτέλεσμα: int -> int (δηλαδή μία συνάρτηση). *)>vallinear_fn=fn:int*int->int->int
Επομένως, η συνάρτηση , μπορεί να οριστεί ως εξής:
Καθώς η ML χρησιμοποιεί currying (δηλαδή όλες οι συναρτήσεις μετατρέπονται ώστε να έχουν ένα όρισμα), η linear_fn μπορεί να γραφτεί πιο σύντομα ως:
funlinear_fn(a,b)x=a*x+b;;
Ανώνυμες συναρτήσεις
Οι συναρτήσεις μπορούν να οριστούν και ως ανώνυμες.[7]: 172-173 Δηλαδή στο παραπάνω παράδειγμα, στην κλήση στο sum_three:
fungx=2*x+5;;sum_threeg;;
θα μπορούσε να χρησιμοποιηθεί μία ανώνυμη στην θέση της , ως εξής:
sum_three(fnx=>2*x+5)
Με αυτόν τον τρόπο αποφεύγεται η ονομασία απλών συναρτήσεων και μειώνονται οι γραμμές κώδικα.
Παραμετρικός πολυμορφισμός
Η ML επιτρέπει τον παραμετρικό πολυμορφισμό, δηλαδή τον ορισμό γενικών συναρτήσεων και γενικών τύπων, που δουλεύουν για όλους τους δυνατούς τύπους της παραμέτρου.
To 'a είναι η παράμετρος και ο τύπος 'a->'a υποδηλώνει ότι συνάρτηση παίρνει ένα στοιχείο οποιοδήποτε τύπου και επιστρέφει ένα στοιχείο του ίδιου τύπου (στην συγκεκριμένη περίπτωση και το ίδιο στοιχείο). Αυτό μας επιτρέπει να γράψουμε:
Μία λίγο πιο σύνθετη συνάρτηση είναι αυτή που αντιστρέφει τις τιμές ενός ζεύγους, π.χ. για είσοδο (10,"hello") επιστρέφει ("hello",10). Αυτή μπορεί να οριστεί ως εξής:
funswap(x,y)=(y,x);;>valswap=fn:'a*'b->'b*'a
Ο τύπος 'a*'b->'b*'a σημαίνει ότι η συνάρτηση παίρνει ως όρισμα ένα ζεύγος τύπου 'a*'b (π.χ. για το (10,"hello"), ο τύπος είναι string*int) και επιστρέφει ένα ζεύγος τύπου 'b*'a (το ("hello",10) με τύπο int*string).
Παράδειγμα 2ο: Map
Μία πιο σύνθετη συνάρτηση που χρησιμοποιείται αρκετά συχνά είναι η map.[7]: 182-184 Αυτή επιτρέπει την εφαρμογή μία συνάρτησης σε κάθε στοιχείο μίας λίστας. Για παράδειγμα,
(* Προσθέτει +10 σε κάθε αριθμό της λίστας *)map(fnx=>x+10)[1,6,2,7,8];;>valit=[11,16,12,17,18]:intlist(* Επισημαίνει ποιοι από τους αριθμούς είναι ζυγοί *)map(fnx=>xmod2=0)[1,5,2,7,8];;>valit=[false,false,true,false,true]:boollistmap(fnx=>ifxthen"Even"else"Odd")[false,false,true,false,true];;>valit=["Odd","Odd","Even","Odd","Even"]:stringlist
Ο τύπος της δηλώνει ότι δέχεται μία συνάρτηση τύπου 'a->'b (για παράδειγμα fnx=>xmod2=0 που ελέγχει αν ο είναι ζυγός) και μία λίστα τύπου 'alist (π.χ. μία λίστα ακεραίων) και επιστρέφει μία λίστα τύπου 'blist (π.χ. μία λίστα από booleans).
Παρόμοιες συναρτήσεις είναι η filter, exists, foldr.[7]: 182-186
Στην λίγο πιο σύνθετη μορφή, κάποιοι από τους τύπους μπορούν να έχουν παραπάνω πληροφορίες. Άμα θέλαμε για τα οχήματα να ξέρουμε και πόσες ρόδες έχουν, τότε θα αλλάζαμε τα φορτηγά ώστε:
(* Θεωρούμε ότι το ποδήλατο έχει 2 και το αυτοκίνητο 4 ρόδες. Τα φορτηγά μπορεί να έχουν 6, 8, 10 ή και παραπάνω ρόδες, επομένως πρέπει να επισημανθεί ο αριθμός όταν το ορίζουμε. *)datatypeVehicleWithWheels=Bicycle|Car|Truckofint;;funvehicle_to_strBicycle="bicycle has 2 wheels"|vehicle_to_strCar="car has 4 wheels"|vehicle_to_str(Truckn)="truck has "^Int.toStringn^" wheels";;>valvehicle_to_str=fn:VehicleWithWheels->stringvehicle_to_str(Truck10);;>valit="truck has 10 wheels":string
Οι τύποι αυτοί μπορεί να είναι και αναδρομικοί. Για παράδειγμα ο παρακάτω τύπος επιτρέπει τον ορισμό αριθμητικών παραστάσεων:
Οι τύποι μπορεί να είναι και πολυμορφικοί.[7]: 128-130 Για παράδειγμα, ένας τύπος για λίστες μπορεί να οριστεί ως εξής:
datatype'aMyList=Empty|Itemof'a*'aMyList
και συναρτήσεις όπως η length, για την εύρεση του μήκους μίας λίστας, μπορούν να οριστούν ως εξής:
(* Συνάρτηση που επιστρέφει το μήκος της λίστας. *)funlengthEmpty=0|length(Cons(x,t))=1+lengtht;;>vallength=fn:'aMyList->int(* Ορισμός μίας λίστας με τα στοιχεία 10, 20 και 30. *)valls=Cons(10,Cons(20,Cons(30,Empty)));;lengthls;;>valit=3:int
Αντίστοιχα με τις λίστες, μπορούν να οριστούν και άλλες δομές, όπως τα δυαδικά δένδρα:
Ο ορισμός αναδρομικών δομών και η δυνατότητα της αναπαράστασης των συναρτήσεων ως τιμές, επιτρέπει την αναπαράσταση λιστών με άπειρο μέγεθος.[7]: 191-211 Ένας συνηθισμένος τρόπος είναι ο εξής:
(* Κάθε ένα κομμάτι της λίστας έχει ένα στοιχείο (τύπου 'a) και μία συνάρτηση που όταν την καλέσουμε επιστρέφει αναδρομικά την υπόλοιπη λίστα. *)datatype'aLazyList=Consof'a*(unit->'aLazyList)
Για παράδειγμα, η άπειρη ακολουθία ακεραίων ίσων ή μεγαλύτερων του , ορίζεται ως εξής:
Τα πρώτα στοιχεία της λίστας μπορούν να ανακτηθούν ως εξής:
funget_first_0=[]|get_first(Cons(x,t))n=x::(get_first(t())(n-1));;>valget_first=fn:'aLazyList->int->'alist(* Επιστρέφει τους ακεραίους 1, 2, ... , 10. *)get_first(integers_from1)10;;>valit=[1,2,3,4,5,6,7,8,9,10]:intlist
Χρησιμοποιώντας αντίστοιχες συναρτήσεις με αυτές που υπάρχουν για λίστες (όπως η map), μπορούμε να ορίσουμε πιο σύνθετες ακολουθίες όπως τους ζυγούς αριθμούς ή τα τετράγωνα αριθμών:
(* Map για άπειρες λίστες. *)funlazy_list_mapf(Cons(x,t))=Cons(fx,fn()=>lazy_list_mapf(t()));;(* Επιστρέφει μία ακολουθία με τους ζυγούς φυσικούς αριθμούς. *)valeven_naturals=lazy_list_map(fnx=>2*x)(integers_from0);;(* Επιστρέφει τους πρώτους 10 ζυγούς: 0, 2, ... , 18. *)get_firsteven_naturals10;;>valit=[0,2,4,6,8,10,12,14,16,18]:intlist(* Επιστρέφει μία ακολουθία με τα τετράγωνα αριθμών. *)valsquares=lazy_list_map(fnx=>2*x)(integers_from0);;(* Επιστρέφει τα 4 πρώτα τετράγωνα: 0, 1, 4, 9. *)get_firstsquares4;;>valit=[0,2,4,6]:intlist
Άπειρα δυαδικά δένδρα
Αντίστοιχα με τις άπειρες λίστες μπορούν να οριστούν άπειρα δυαδικά δένδρα, π.χ. ως εξής:
Η ML επιτρέπει το ταίριασμα προτύπων.[1]: 65-74 Για παράδειγμα, την συνάρτηση factorial, μπορούμε να την γράψουμε ως εξής:
(* 1η Εκδοχή με την χρήση case ... of *)funfactorialn=casenof0=>1|n=>n*factorial(n-1)(* 2η Εκδοχή *)funfactorial0=1|factorialn=n*factorial(n-1)
Ο έλεγχος των προτύπων γίνεται με την σειρά που δίνονται οι περιπτώσεις. Επομένως, αν στην factorial αλλάξουμε την σειρά των περιπτώσεων, τότε το θα ταιριάζει όλους τους ακεραίους και η δεύτερη περίπτωση δεν εκτελείται ποτέ. Αυτό οδηγεί στο σφάλμα Error: match redundant. Αντίστοιχα αν δεν καλύπτονται όλες οι περιπτώσεις, εμφανίζεται η προειδοποίηση Warning: match nonexhaustive.
Η ML υποστηρίζει εξαιρέσεις.[7] Με την σύνταξη raise <exception> εγείρεται μία εξαίρεση και με το handle <exception pattern> => <expression> διαχειρίζεται μία εξαίρεση. Για την συνάρτηση factorial που ορίσαμε παραπάνω, για αρνητικούς αριθμούς μπορούμε να εγείρουμε μία εξαίρεση ως εξής:
funfactorialn=ifn=0then1elseifn>0thenn*factorial(n-1)elseraiseFail"Factorial only defined for non-negative integers."(* Παρατηρήστε ότι ο τύπος της συνάρτησης δεν αλλάζει. *)>valfactorial=fn:int->int
Επομένως, άμα την καλέσουμε με αρνητικό ακέραιο, παίρνουμε:
factorial (~2) > uncaught exception Fail [Fail: Factorial only defined for non-negative integers.]
Εκτός από τις εξαιρέσεις της γλώσσας (Overflow, Div, Domain, Chr, Subscript), επιπλέον εξαιρέσεις μπορούν να οριστούν από τον χρήστη:
(* Εξαίρεση ειδικά ορισμένη για την συνάρτηση factorial. *)exceptionFactorialExceptionofstring;;funfactorialn=ifn=0then1elseifn>0thenn*factorial(n-1)elseraiseFactorialException"Given a negative integer.";;factorial(~2);;>uncaughtexceptionFactorialException
Ο παρακάτω κώδικας είναι ένα παράδειγμα διαχείρισης μίας εξαίρεσης:
(* Συνάρτηση που δέχεται μία λίστα και επιστρέφει 0 αν η λίστα είναι άδεια, διαφορετικά το παραγοντικό του πρώτου στοιχείου (και 1 αν είναι αρνητικός αριθμός). *)funhd_factorialls=factorial(hdls)handleEmpty=>0|_=>1;;hd_factorial[3,1,2];;>valit=6:inthd_factorial[~2,1,3];;>valit=1:inthd_factorial[];;>valit=0:int
Οι εξαιρέσεις μπορούν να χρησιμοποιηθούν και για την υλοποίηση της οπισθοδρόμησης.[7]: 139
Πράξεις με παρενέργειες
Η ML έχει πράξεις με παρενέργειες,[7] όπως είναι η δήλωση ανάθεσης :=,
↑Gordon, M.; Milner, R.; Morris, L.; Newey, M.; Wadsworth, C. (1978). «A Metalanguage for interactive proof in LCF». Proceedings of the 5th ACM SIGACT-SIGPLAN symposium on Principles of programming languages: 119–130. doi:https://doi.org/10.1145/512760.512773.
↑Hindley, R. (1969). «The Principal Type-Scheme of an Object in Combinatory Logic». Transactions of the American Mathematical Society146: 29. doi:doi:10.2307/1995158.
↑ 7,007,017,027,037,047,057,067,077,087,097,107,117,12Paulson, Lawrence C. (1996). ML for the working programmer (2η έκδοση). Cambridge: Cambridge University Press. ISBN978-0-521-57050-3.Σφάλμα αναφοράς: Μη έγκυρη ετικέτα <ref> • όνομα " P96 " ορίζεται πολλές φορές με διαφορετικό περιεχόμενο
↑Nipkow, Tobias· Wenzel, Markus· Paulson, Lawrence C. (2002). Isabelle/HOL : a proof assistant for higher-order logic. Berlin: Springer. ISBN978-3-540-43376-7.
↑ 12,012,1Okasaki, Chris (1996). Purely functional data structures. Cambridge, U.K.: Cambridge University Press. ISBN9780521663502.