ML (γλώσσα προγραμματισμού): Διαφορά μεταξύ των αναθεωρήσεων

Από τη Βικιπαίδεια, την ελεύθερη εγκυκλοπαίδεια
Περιεχόμενο που διαγράφηκε Περιεχόμενο που προστέθηκε
Προσθήκη αναφορών; Προσθήκη παραδειγμάτων για τα χαρακτηριστικά της γλώσσας
Γραμμή 1: Γραμμή 1:
Η '''ML''' είναι μια [[συναρτησιακός προγραμματισμός|συναρτησιακή]] [[γλώσσα προγραμματισμού]] γενικής χρήσης, που αναπτύχθηκε από τον Ρόμπιν Μίλνερ και άλλους στο τέλος της δεκαετίας του 1970 στο [[πανεπιστήμιο του Εδιμβούργου]].<ref name="LCF79">{{cite book |last=Gordon |first=Michael J. C. |title=Edinburgh LCF : a mechanised logic of computation |year=1979 |publisher=Springer-Verlag |location=Berlin |isbn=978-3-540-09724-2 |url=https://doi.org/10.1007/3-540-09724-4}}</ref> Ξεκίνησε ως μέτα-γλώσσα (εξού και το όνομα Meta-Language) για διαδραστικές αποδείξεις στο σύστημα Edinburgh LCF (τα αρχικά για "Logic for Computable Functions" - λογική για υπολογίσιμες συναρτήσεις) και εξελίχθηκε σε γενικής χρήσης γλώσσα προγραμματισμού για να καλύψει τις ανάγκες αυτής της εφαρμογής.<ref name="M82">{{cite journal |last=Milner |first=R. |title=How ML evlolved |journal=ML/Hope/LCF Newsletter |year=1982 |volume=1 |issue=1 |pages=25-34 |url=https://www.research.ed.ac.uk/en/publications/how-ml-evlolved}}</ref>
{{χωρίς παραπομπές}}


Η ML είναι μια [[συναρτησιακός προγραμματισμός|συναρτησιακή]] [[γλώσσα προγραμματισμού]] γενικής χρήσης, που αναπτύχθηκε από τον Ρόμπερτ Μίλνερ και άλλους στο τέλος της δεκαετίας του 1970 στο [[πανεπιστήμιο του Εδιμβούργου]]. Η [[Σύνταξη_(γλώσσα_προγραμματισμού)|σύνταξη]] της ML είναι εμπνευσμένη από την ISWIM. Τα αρχικά ML σημαίνουν Meta-Language (μετα-γλώσσα), μιας και ο σκοπός της ήταν η ανάπτυξη τακτικών για [[απόδειξη|αποδείξεις]] (proof tactics) στο σύστημα αποδείξεων LCF. Η γλώσσα pplambda του LCF είναι συνδυασμός του [[προτασιακός λογισμός πρώτου βαθμού|πρωτοβάθμιου προτασιακού λογισμού]] (first-order predicate calculus) και του πολυμορφικού [[λογισμός λάμδα|λογισμού λάμδα]] με απλούς τύπους (simply-typed polymorphic lambda-calculus), και χρησιμοποιούσε την ML ως μετα-γλώσσα. Η ML είναι γνωστή για τη χρήση του αλγόριθμου Χίντλεϋ-Μίλνερ για την [[εξαγωγή τύπων]] (type inference), που μπορεί να συνάγει αυτόματα τους τύπους των περισσοτέρων εκφράσεων της γλώσσας, χωρίς να χρειάζεται σαφείς προσδιορισμούς τύπων από τον προγραμματιστή.
Τα συναρτησιακά στοιχεία της γλώσσας είναι εμπνευσμένα από την ISWIM και την GEDANKEN{{r|LCF79|p=iii}}, αλλά διαφέρει στον χειρισμό των τύπων,<ref>{{cite journal |last=Gordon |first=M. |coauthors=Milner, R.; Morris, L.; Newey, M.; Wadsworth, C. |title=A Metalanguage for interactive proof in LCF |journal=Proceedings of the 5th ACM SIGACT-SIGPLAN symposium on Principles of programming languages |date=1978 |pages=119–130 |doi=https://doi.org/10.1145/512760.512773}}</ref> ενώ άλλα στοιχεία της γλώσσας είναι εμπνευσμένα από τη [[Lisp]] and την POP2.<ref name="M97">{{cite book |last1=Milner |first1=Robin |last2=Tofte |first2=Mads |last3=Harper |first3=Robert |last4=MacQueen |first4=David |title=The definition of standard ML : revised |year=1997 |publisher=MIT Press |location=Cambridge, Mass. |isbn=9780262631815 |url=https://smlfamily.github.io/sml97-defn.pdf}}</ref>{{rp|89}} Το συγκεκριμένο σύστημα αποδείξεων του LCF ήταν το PPLambda, που είναι συνδυασμός του [[προτασιακός λογισμός πρώτου βαθμού|πρωτοβάθμιου προτασιακού λογισμού]] (first-order predicate calculus) και του πολυμορφικού [[λογισμός λάμδα|λογισμού λάμδα]] με απλούς τύπους (simply-typed polymorphic lambda-calculus). Αλλά, σύμφωνα με τον Ρόμπιν Μίλνερ, σχεδόν οποιδήποτε επαγωγικό σύστημα θα είχε οδηγήσει στις ίδιες βασικές αρχές της γλώσσας.{{r|M82}}

Η ML είναι γνωστή για τη χρήση του αλγόριθμου Χίντλεϋ-Μίλνερ<ref>{{cite journal |last=Hindley |first=R. |title=The Principal Type-Scheme of an Object in Combinatory Logic |journal=Transactions of the American Mathematical Society |year=1969 |volume=146 |pages=29 |doi=doi:10.2307/1995158}}</ref><ref name="M78">{{cite journal |last=Milner |first=Robin |title=A theory of type polymorphism in programming |journal=Journal of Computer and System Sciences |year=1978 |volume=17 |issue=3 |pages=348-375 |doi=https://doi.org/10.1016/0022-0000(78)90014-4}}</ref> για την [[εξαγωγή τύπων]] (type inference), που μπορεί να συνάγει αυτόματα τους τύπους των περισσοτέρων εκφράσεων της γλώσσας, χωρίς να χρειάζεται σαφείς προσδιορισμούς τύπων από τον προγραμματιστή. Είναι μία από τις λίγες γλώσσες προγραμματισμού με αυστηρή απόδειξη ότι όλα τα προγράμματα που περνάνε τον [[Σύστημα τύπων#Έλεγχος τύπων|έλεγχο τύπων]], δεν έχουν σφάλματα κατά την εκτέλεση.{{r|M78}}


== Περιγραφή ==
== Περιγραφή ==
Η ML συχνά αναφέρεται ως '''ακάθαρτη''' συναρτησιακή γλώσσα, επειδή επιτρέπει [[παρενέργεια (υπολογιστές)|παρενέργειες]], και επομένως [[διαδικαστικός προγραμματισμός|διαδικαστικό προγραμματισμό]], σε αντίθεση με [[αμιγώς συναρτησιακός|αμιγώς συναρτησιακές]] γλώσσες όπως η [[Haskell]]. Για αυτό το λόγο λέγεται και γλώσσα προγραμματισμού μικτού προτύπου.
Η ML συχνά αναφέρεται ως '''ακάθαρτη''' συναρτησιακή γλώσσα, επειδή επιτρέπει [[παρενέργεια (υπολογιστές)|παρενέργειες]], και επομένως [[διαδικαστικός προγραμματισμός|διαδικαστικό προγραμματισμό]],{{r|M97|p=89}} σε αντίθεση με [[αμιγώς συναρτησιακός|αμιγώς συναρτησιακές]] γλώσσες όπως η [[Haskell]]. Για αυτό το λόγο λέγεται και γλώσσα προγραμματισμού μικτού προτύπου.


Χαρακτηριστικά της ML περιλαμβάνουν την [[στρατηγική υπολογισμού]] της κλήσης κατά τιμή (call-by-value), [[συνάρτηση (υπολογιστές)|συναρτήσεις]] πρώτης τάξης, αυτόματη διαχείριση μνήμης με [[συλλέκτης απορριμάτων|συλλογή απορριμάτων]], παραμετρικό [[πολυμορφισμός|πολυμορφισμό]], [[στατικός τύπος|στατικούς]] [[σύστημα τύπων|τύπους]], [[εξαγωγή τύπων]] (type inference), [[Αλγεβρικός τύπος δεδομένων|αλγεβρικούς τύπους δεδομένων]] (algebraic datatypes), ταίριασμα προτύπων (pattern matching) και [[εξαίρεση|εξαιρέσεις]].
Χαρακτηριστικά της ML περιλαμβάνουν την [[στρατηγική υπολογισμού]] της κλήσης κατά τιμή (call-by-value), [[συνάρτηση (υπολογιστές)|συναρτήσεις]] πρώτης τάξης, αυτόματη διαχείριση μνήμης με [[συλλέκτης απορριμάτων|συλλογή απορριμάτων]], παραμετρικό [[πολυμορφισμός|πολυμορφισμό]], [[στατικός τύπος|στατικούς]] [[σύστημα τύπων|τύπους]], [[εξαγωγή τύπων]] (type inference), [[Αλγεβρικός τύπος δεδομένων|αλγεβρικούς τύπους δεδομένων]] (algebraic datatypes), ταίριασμα προτύπων (pattern matching) και [[εξαίρεση|εξαιρέσεις]]. Παραδείγματα αυτών των χαρακτηριστικών δίνονται [[#Παραδείγματα χαρακτηριστικών γλώσσας|παρακάτω]].


Αντίθετα με τη Haskell, η ML χρησιμοποιεί [[πρόθυμη αποτίμηση]] (eager evaluation), που σημαίνει ότι όλες οι υπο-εκφράσεις μιας έκφρασης αποτιμώνται. Αποτέλεσμα είναι ότι δεν μπορούν να χρησιμοποιηθούν άπειρες λίστες ''ως έχει''. Παρόλα αυτά, η [[οκνηρή αποτίμηση]] (lazy evaluation) και επομένως και οι άπειρες δομές, μπορούν να προσομοιωθούν με τη χρήση ανώνυμων συναρτήσεων.
Αντίθετα με τη Haskell, η ML χρησιμοποιεί [[πρόθυμη αποτίμηση]] (eager evaluation), που σημαίνει ότι όλες οι υπο-εκφράσεις μιας έκφρασης αποτιμώνται. Αποτέλεσμα είναι ότι δεν μπορούν να χρησιμοποιηθούν άπειρες λίστες ''ως έχει''. Παρόλα αυτά, η [[οκνηρή αποτίμηση]] (lazy evaluation) και επομένως και οι άπειρες δομές, μπορούν να προσομοιωθούν με τη χρήση ανώνυμων συναρτήσεων.<ref name="P96">{{cite book |last=Paulson |first=Lawrence C. |title=ML for the working programmer |year=1996 |publisher=Cambridge University Press |location=Cambridge |isbn=978-0-521-57050-3 |edition=2η}}</ref>{{rp|191-211}}


Σήμερα υπάρχουν αρκετές γλώσσες στην οικογένεια της ML. Οι δύο κύριες διάλεκτοι είναι η [[Standard ML]] και η [[Caml]], αλλά υπάρχουν και άλλες, όπως η [[F Sharp|F#]]. Ιδέες από την ML έχουν επηρεάσει πολυάριθμες άλλες γλώσσες όπως τη [[Haskell]], τη [[Cyclone]] και τη [[Nemerle]].
Σήμερα υπάρχουν αρκετές γλώσσες στην οικογένεια της ML. Οι δύο κύριες διάλεκτοι είναι η [[Standard ML]] και η [[Caml]], αλλά υπάρχουν και άλλες, όπως η [[F Sharp|F#]], η Lazy ML, η οποία υποστηρίζει οκνηρή αποτίμηση<ref>{{cite journal |last=Augustsson |first=L. |coauthors=Johnsson Τ. |title=The Chalmers Lazy-ML Compiler |journal=The Computer Journal |year=1989 |volume=32 |issue=2 |pages=127–141 |doi=https://doi.org/10.1093/comjnl/32.2.127}}</ref> και η CakeML, η οποία είναι ένα υποσύνολο της ML με [[Τυπική σημασιολογία των γλωσσών προγραμματισμού|τυπική σημασιολογία]] επιβεβαιωμένη στο HOL.<ref>{{cite journal |last=Kumar |first=Ramana |coauthors=Myreen, Magnus O.; Norrish, Michael; Owens, Scott |title=CakeML: a verified implementation of ML |journal=ACM SIGPLAN Notices |year=2014 |volume=49 |issue=1 |pages=179–191 |doi=https://doi.org/10.1145/2578855.2535841}}</ref> Ιδέες από την ML έχουν επηρεάσει πολυάριθμες άλλες γλώσσες όπως τη [[Haskell]], τη [[Cyclone]] και τη [[Nemerle]].


Τα δυνατά σημεία της ML συγκεντρώνονται συνήθως στο σχεδιασμό και χειρισμό γλωσσών (μεταγλωττιστές, αναλυτές, αποδείκτες θεωρημάτων, κλπ), αλλά είναι γλώσσα γενικής χρήσης, και χρησιμοποιείται επίσης στη [[βιοπληροφορική]] (bioinformatics), σε οικονομικά συστήματα, και άλλες εφαρμογές.
Τα δυνατά σημεία της ML συγκεντρώνονται συνήθως στο σχεδιασμό και χειρισμό γλωσσών (μεταγλωττιστές, αναλυτές,<ref>{{cite book |last=Appel |first=Andrew W. |title=Modern compiler implementation in ML |year=1998 |publisher=Cambridge University Press |location=Cambridge |isbn=0-521-60764-7 |url=https://www.cs.princeton.edu/~appel/modern/ml/}}</ref> αποδείκτες θεωρημάτων όπως το HOL και το Isabelle,<ref>{{cite book |last1=Nipkow |first1=Tobias |last2=Wenzel |first2=Markus |last3=Paulson |first3=Lawrence C. |title=Isabelle/HOL : a proof assistant for higher-order logic |year=2002 |publisher=Springer |location=Berlin |isbn=978-3-540-43376-7}}</ref> κλπ), και γι'αυτό έχει χρησιμοποιηθεί αρκετά στην έρευνα γλωσσών προγραμματισμού. Ως συναρτησιακή γλώσσα επιτρέπει την εύκολη υλοποίηση διαχρονικών δομών δεδομένων.<ref name="O96">{{cite book |last=Okasaki |first=Chris |title=Purely functional data structures |year=1996 |publisher=Cambridge University Press |location=Cambridge, U.K. |isbn=9780521663502}}</ref> Έχει χρησιμοποιηθεί επίσης στη [[βιοπληροφορική]]<ref>{{cite journal |last=Wong |first=Limsoon |title=The functional guts of the Kleisli query system |journal=ACM SIGPLAN Notices |year=2000 |volume=35 |issue=9 |pages=1–10 |doi=https://doi.org/10.1145/357766.351241}}</ref>, σε οικονομικά συστήματα, και άλλες εφαρμογές, αλλά εκεί πιο διαδεδομένες είναι παραλλαγές της γλώσσας όπως η [[OCaml]].<ref>{{cite web |title=OCaml in Industry |url=https://ocaml.org/industrial-users |publisher=OCaml |accessdate=17 Ιουλίου 2022}}</ref>

== Παραδείγματα χαρακτηριστικών γλώσσας ==

=== Εξαγωγή τύπων ===

Η ML επιτρέπει την αυτόματη εξαγωγή τύπων.{{r|P96|p=63-67}} Για παράδειγμα, στην παρακάτω συνάρτηση οι δηλωτές (type specifiers) είναι περιττοί:
<syntaxhighlight lang="sml">
fun increment (n:int) :int = n + 1
</syntaxhighlight>

Η συνάρτηση μπορεί να γραφτεί ως εξής:
<syntaxhighlight lang="sml">
fun increment n = n + 1
</syntaxhighlight>
και η γλώσσα θα εξαγάγει τον τύπο της μεταβλητής <math>n</math> ως ακέραιο και της συνάρτησης <syntaxhighlight lang="sml" inline>increment</syntaxhighlight> ως συνάρτηση από ακεραίους σε ακεραίους:
<syntaxhighlight lang="sml">
val increment = fn : int -> int
</syntaxhighlight>

=== Αναδρομή ===

Η ML υποστηρίζει και ενθαρρύνει την χρήση αναδρομικών συναρτήσεων<ref name="U94">{{cite book |last=Ullman |first=Jeffrey D. |title=Elements of ML programming |year=1994 |publisher=Prentice Hall |location=Upper Saddle River, NJ |isbn=9780137903870 |edition=ML97}}</ref>{{rp|54-60}} (και αναδρομικών τύπων). Για παράδειγμα η παρακάτω συνάρτηση υπολογίζει το <math>n</math> [[Παραγοντικό|παραγοντικό]].

<syntaxhighlight lang="sml">
fun factorial n =
if n = 0 then 1
else n * factorial (n-1);;

factorial 4;; (* Υπολογίζει 4 * 3 * 2 * 1 = 24 *)
> val it = 24 : int
</syntaxhighlight>

Επίσης υποστηρίζει αμοιβαία αναδρομή (mutual recursion).{{r|U94|p=60-62}} Για παράδειγμα, οι παρακάτω συναρτήσεις ελέγχουν (με μη αποδοτικό τρόπο) αν ένας μη-αρνητικός ακέραιος είναι ζυγός ή περιττός, καλώντας η μία την άλλη:
<syntaxhighlight lang="sml">
fun is_odd n =
if n = 0 then false
else is_even (n-1)
and is_even n =
if n = 0 then true
else is_odd (n-1)

> val is_odd = fn : int -> bool
> val is_even = fn : int -> bool
</syntaxhighlight>

=== Συναρτήσεις ως τιμές ===

Στην ML οι συναρτήσεις είναι τιμές, που σημαίνει ότι μπορούν να δοθούν ως ''όρισμα'' μίας συνάρτησης ή να επιστραφούν ως ''αποτέλεσμα'' μίας συνάρτησης.{{r|P96|p=171-191}} Μία συνάρτηση με όρισμα ή αποτέλεσμα μία άλλη συνάρτηση, λέγεται [[Συναρτήσεις ανώτερου βαθμού|συνάρτηση ανωτέρου βαθμού]].

==== Συνάρτηση ως όρισμα ====

Για παράδειγμα, η παρακάτω συνάρτηση παίρνει ως όρισμα μία οποιαδήποτε συνάρτηση <math>f: \mathrm{int} \to \mathrm{int}</math> και επιστρέφει το άθροισμα <math>f(1) + f(2) + f(3)</math>:
<syntaxhighlight lang="sml">
fun sum_three f = (f 1) + (f 2) + (f 3)
</syntaxhighlight>
Άρα, όταν την καλέσουμε για <math>g(x) = 2x + 5</math> η απάντηση είναι <math>(2 \cdot 1 + 5) + (2 \cdot 2 + 5) + (2 \cdot 3 + 5) = 27</math>:
<syntaxhighlight lang="sml">
fun g x = 2 * x + 5;;
sum_three g;;

> val g = fn : int -> int
> val it = 27 : int
</syntaxhighlight>

==== Συνάρτηση ως αποτέλεσμα ====

Για παράδειγμα, η παρακάτω συνάρτηση παίρνει ως όρισμα <math>a, b</math> και επιστρέφει την γραμμική συνάρτηση της μορφής <math>f(x) = a \cdot x + b</math>:
<syntaxhighlight lang="sml">
fun linear_fn (a, b) =
let fun new_fn x = a * x + b in
new_fn
end;;

(* Παρατηρήστε ότι ο τύπος της συνάρτησης επιβεβαιώνει
αυτό που θέλουμε όρισμα: int * int (δηλαδή (a,b)) και
αποτέλεσμα: int -> int (δηλαδή μία συνάρτηση). *)
> val linear_fn = fn : int * int -> int -> int
</syntaxhighlight>
Επομένως, η συνάρτηση <math>f(x) = 3x + 1</math>, μπορεί να οριστεί ως εξής:
<syntaxhighlight lang="sml">
val threeXplusOne = linear_fn (3, 1);;
threeXplusOne 10;;
> val threeXplusOne = fn : int -> int
> val it = 31 : int
</syntaxhighlight>
Καθώς η ML χρησιμοποιεί currying (δηλαδή όλες οι συναρτήσεις μετατρέπονται ώστε να έχουν ένα όρισμα), η <syntaxhighlight lang="sml" inline>linear_fn</syntaxhighlight> μπορεί να γραφτεί πιο σύντομα ως:
<syntaxhighlight lang="sml">
fun linear_fn (a, b) x = a * x + b;;
</syntaxhighlight>

=== Ανώνυμες συναρτήσεις ===

Οι συναρτήσεις μπορούν να οριστούν και ως ανώνυμες.{{r|P96|p=172-173}} Δηλαδή στο [[#Συνάρτηση ως όρισμα|παραπάνω παράδειγμα]], στην κλήση στο <syntaxhighlight lang="sml" inline>sum_three</syntaxhighlight>:
<syntaxhighlight lang="sml">
fun g x = 2 * x + 5;;
sum_three g;;
</syntaxhighlight>
θα μπορούσε να χρησιμοποιηθεί μία ανώνυμη στην θέση της <math>g</math>, ως εξής:
<syntaxhighlight lang="sml">
sum_three (fn x => 2 * x + 5)
</syntaxhighlight>
Με αυτόν τον τρόπο αποφεύγεται η ονομασία απλών συναρτήσεων και μειώνονται οι γραμμές κώδικα.

=== Παραμετρικός πολυμορφισμός ===

Η ML επιτρέπει τον [[Παραμετρικός πολυμορφισμός|παραμετρικό πολυμορφισμό]], δηλαδή τον ορισμό γενικών συναρτήσεων και γενικών τύπων, που δουλεύουν για όλους τους δυνατούς τύπους της παραμέτρου.

==== Παράδειγμα 1ο: Απλές συναρτήσεις ====

Η πιο απλή τέτοια συνάρτηση είναι η [[Συνάρτηση#Ταυτοτική συνάρτηση|ταυτοτική συνάρτηση]]
<syntaxhighlight lang="sml">
fun id x = x
</syntaxhighlight>
η οποία έχει τύπο
<syntaxhighlight lang="sml">
val id = fn : 'a -> 'a
</syntaxhighlight>
To <syntaxhighlight lang="sml" inline>'a</syntaxhighlight> είναι η παράμετρος και ο τύπος <syntaxhighlight lang="sml" inline>'a -> 'a</syntaxhighlight> υποδηλώνει ότι συνάρτηση παίρνει ένα στοιχείο οποιοδήποτε τύπου και επιστρέφει ένα στοιχείο του ίδιου τύπου (στην συγκεκριμένη περίπτωση και το ίδιο στοιχείο). Αυτό μας επιτρέπει να γράψουμε:
<syntaxhighlight lang="sml">
id 3;;
> val it = 3 : int
id 3.0;;
> val it = 3.0 : real
id "Hello";;
> val it = "Hello" : string
</syntaxhighlight>

Μία λίγο πιο σύνθετη συνάρτηση είναι αυτή που αντιστρέφει τις τιμές ενός ζεύγους, π.χ. για είσοδο <syntaxhighlight lang="sml" inline>(10, "hello")</syntaxhighlight> επιστρέφει <syntaxhighlight lang="sml" inline>("hello", 10)</syntaxhighlight>. Αυτή μπορεί να οριστεί ως εξής:
<syntaxhighlight lang="sml">
fun swap (x, y) = (y, x);;
> val swap = fn : 'a * 'b -> 'b * 'a
</syntaxhighlight>
Ο τύπος <syntaxhighlight lang="sml" inline>'a * 'b -> 'b * 'a</syntaxhighlight> σημαίνει ότι η συνάρτηση παίρνει ως όρισμα ένα ζεύγος τύπου <syntaxhighlight lang="sml" inline>'a * 'b</syntaxhighlight> (π.χ. για το <syntaxhighlight lang="sml" inline>(10, "hello")</syntaxhighlight>, ο τύπος είναι <syntaxhighlight lang="sml" inline>string * int</syntaxhighlight>) και επιστρέφει ένα ζεύγος τύπου <syntaxhighlight lang="sml" inline>'b * 'a</syntaxhighlight> (το <syntaxhighlight lang="sml" inline>("hello", 10)</syntaxhighlight> με τύπο <syntaxhighlight lang="sml" inline>int * string</syntaxhighlight>).

==== Παράδειγμα 2ο: Map ====

Μία πιο σύνθετη συνάρτηση που χρησιμοποιείται αρκετά συχνά είναι η <syntaxhighlight lang="sml" inline>map</syntaxhighlight>.{{r|P96|p=182-184}} Αυτή επιτρέπει την εφαρμογή μία συνάρτησης σε κάθε στοιχείο μίας λίστας. Για παράδειγμα,
<syntaxhighlight lang="sml">
(* Προσθέτει +10 σε κάθε αριθμό της λίστας *)
map (fn x => x + 10) [1, 6, 2, 7, 8];;
> val it = [11,16,12,17,18] : int list
(* Επισημαίνει ποιοι από τους αριθμούς είναι ζυγοί *)
map (fn x => x mod 2 = 0) [1, 5, 2, 7, 8];;
> val it = [false,false,true,false,true] : bool list
map (fn x => if x then "Even" else "Odd") [false,false,true,false,true];;
> val it = ["Odd","Odd","Even","Odd","Even"] : string list
</syntaxhighlight>

Η συνάρτηση <syntaxhighlight lang="sml" inline>map</syntaxhighlight> μπορεί να οριστεί ως εξής:
<syntaxhighlight lang="sml">
fun map f [] = []
| map f (x::xs) = (f x)::map f xs;;
> val map = fn : ('a -> 'b) -> 'a list -> 'b list
</syntaxhighlight>
Ο τύπος της δηλώνει ότι δέχεται μία συνάρτηση τύπου <syntaxhighlight lang="sml" inline>'a -> 'b</syntaxhighlight> (για παράδειγμα <syntaxhighlight lang="sml" inline>fn x => x mod 2 = 0</syntaxhighlight> που ελέγχει αν ο <math>x</math> είναι ζυγός) και μία λίστα τύπου <syntaxhighlight lang="sml" inline>'a list</syntaxhighlight> (π.χ. μία λίστα ακεραίων) και επιστρέφει μία λίστα τύπου <syntaxhighlight lang="sml" inline>'b list</syntaxhighlight> (π.χ. μία λίστα από booleans).

Παρόμοιες συναρτήσεις είναι η <syntaxhighlight lang="sml" inline>filter</syntaxhighlight>, <syntaxhighlight lang="sml" inline>exists</syntaxhighlight>, <syntaxhighlight lang="sml" inline>foldr</syntaxhighlight>.{{r|P96|p=182-186}}

==== Παράδειγμα 3ο: Σύνθεση συναρτήσεων ====

Μπορούμε να ορίσουμε μία συνάρτηση που [[Σύνθεση συνάρτησης|συνθέτει δύο συναρτήσεις]] ως εξής:
<syntaxhighlight lang="sml">
fun compose f g x = f ( g x )
> val compose = fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
</syntaxhighlight>

Άρα για την σύνθεση της <math>f(x) = 2x + 1</math> και της <math>g(x) = x^2 + 2</math> (που έχει ως αποτέλεσμα την <math>h(x) = f(g(x)) = 2 \cdot (x^2 + 2) + 1 = 2x^2 + 5</math>), γράφουμε:
<syntaxhighlight lang="sml">
val h = compose (fn x => 2 * x + 1) (fn x => x * x + 2)
h 10 (* Επιστρέφει 2 * 10^2 + 5 *)
</syntaxhighlight>

Άλλη μία εφαρμογή της <syntaxhighlight lang="sml" inline>compose</syntaxhighlight> είναι η συγχώνευση δύο map. Στο [[#Παράδειγμα 2ο: Map|προηγούμενο παράδειγμα]] τα δύο τελευταία map μπορούν να αντικατασταθούν με ένα:
<syntaxhighlight lang="sml">
map (compose (fn x => if x then "Even" else "Odd") (fn x => x mod 2 = 0)) [1, 5, 2, 7, 8]
> val it = ["Odd","Odd","Even","Odd","Even"] : string list
</syntaxhighlight>

=== Αλγεβρικός τύπος δεδομένων ===

Η ML υποστηρίζει τον ορισμό καινούργιων [[Αλγεβρικός τύπος δεδομένων|αλγεβρικών τύπων δεδομένων]].{{r|P96|p=124-128}}

Στην πιο απλή περίπτωση, αυτοί μπορεί να είναι [[Τύπος δεδομένων#Απαριθμήσεις|απαριθμητικοί τύποι]], π.χ. οχήματα:
<syntaxhighlight lang="sml">
datatype Vehicle = Bicycle | Car | Truck;;
fun vehicle_to_str Bicycle = "bicycle"
| vehicle_to_str Car = "car"
| vehicle_to_str Truck = "truck";;
> val vehicle_to_str = fn : Vehicle -> string
vehicle_to_str Truck;;
> val it = "truck" : string
</syntaxhighlight>

Στην λίγο πιο σύνθετη μορφή, κάποιοι από τους τύπους μπορούν να έχουν παραπάνω πληροφορίες. Άμα θέλαμε για τα οχήματα να ξέρουμε και πόσες ρόδες έχουν, τότε θα αλλάζαμε τα φορτηγά ώστε:
<syntaxhighlight lang="sml">
(* Θεωρούμε ότι το ποδήλατο έχει 2 και το αυτοκίνητο 4 ρόδες.
Τα φορτηγά μπορεί να έχουν 6, 8, 10 ή και παραπάνω ρόδες, επομένως
πρέπει να επισημανθεί ο αριθμός όταν το ορίζουμε. *)
datatype VehicleWithWheels = Bicycle | Car | Truck of int;;

fun vehicle_to_str Bicycle = "bicycle has 2 wheels"
| vehicle_to_str Car = "car has 4 wheels"
| vehicle_to_str (Truck n) = "truck has " ^ Int.toString n ^ " wheels";;
> val vehicle_to_str = fn : VehicleWithWheels -> string
vehicle_to_str (Truck 10);;
> val it = "truck has 10 wheels" : string
</syntaxhighlight>
Οι τύποι αυτοί μπορεί να είναι και αναδρομικοί. Για παράδειγμα ο παρακάτω τύπος επιτρέπει τον ορισμό αριθμητικών παραστάσεων:
<syntaxhighlight lang="sml">
datatype Expression =
Num of int
| Neg of Expression
| Plus of Expression * Expression
| Mult of Expression * Expression;;
</syntaxhighlight>
και η παρακάτω συνάρτηση επιτρέπει τον υπολογισμό τους.
<syntaxhighlight lang="sml">
fun evaluate (Num n) = n
| evaluate (Neg e) = ~(evaluate e)
| evaluate (Plus (e1, e2)) = (evaluate e1) + (evaluate e2)
| evaluate (Mult (e1, e2)) = (evaluate e1) * (evaluate e2);;
> val evaluate = fn : Expression -> int
(* Υπολογισμός της παράστασης: 10 + 5 * (7 - 3) = 30. *)
evaluate (Plus(Num 10, Mult(Num 5, Plus(Num 7, Neg(Num 3)))));;
> val it = 30 : int
</syntaxhighlight>

Οι τύποι μπορεί να είναι και πολυμορφικοί.{{r|P96|p=128-130}} Για παράδειγμα, ένας τύπος για λίστες μπορεί να οριστεί ως εξής:
<syntaxhighlight lang="sml">
datatype 'a MyList = Empty | Item of 'a * 'a MyList
</syntaxhighlight>
και συναρτήσεις όπως η <syntaxhighlight lang="sml" inline>length</syntaxhighlight>, για την εύρεση του μήκους μίας λίστας, μπορούν να οριστούν ως εξής:
<syntaxhighlight lang="sml">
(* Συνάρτηση που επιστρέφει το μήκος της λίστας. *)
fun length Empty = 0
| length (Cons(x, t)) = 1 + length t;;
> val length = fn : 'a MyList -> int

(* Ορισμός μίας λίστας με τα στοιχεία 10, 20 και 30. *)
val ls = Cons(10, Cons(20, Cons(30, Empty)));;
length ls;;
> val it = 3 : int
</syntaxhighlight>
Αντίστοιχα με τις λίστες, μπορούν να οριστούν και άλλες δομές, όπως τα [[Δυαδικό δέντρο|δυαδικά δένδρα]]:
<syntaxhighlight lang="sml">
datatype 'a BTree = Leaf | Branch of 'a * ('a BTree) * ('a BTree)
</syntaxhighlight>

=== Άπειρες δομές ===

==== Άπειρες λίστες ====

Ο ορισμός αναδρομικών δομών και η δυνατότητα της αναπαράστασης των συναρτήσεων ως τιμές, επιτρέπει την αναπαράσταση λιστών με άπειρο μέγεθος.{{r|P96|p=191-211}} Ένας συνηθισμένος τρόπος είναι ο εξής:
<syntaxhighlight lang="sml">
(* Κάθε ένα κομμάτι της λίστας έχει ένα στοιχείο (τύπου 'a)
και μία συνάρτηση που όταν την καλέσουμε επιστρέφει
αναδρομικά την υπόλοιπη λίστα. *)
datatype 'a LazyList = Cons of 'a * (unit -> 'a LazyList)
</syntaxhighlight>
Για παράδειγμα, η άπειρη ακολουθία ακεραίων ίσων ή μεγαλύτερων του <math>n</math>, ορίζεται ως εξής:
<syntaxhighlight lang="sml">
fun integers_from n =
Cons(n, fn () => integers_from (n+1));;
</syntaxhighlight>
Τα πρώτα <math>n</math> στοιχεία της λίστας μπορούν να ανακτηθούν ως εξής:
<syntaxhighlight lang="sml">
fun get_first _ 0 = []
| get_first (Cons(x, t)) n = x::(get_first (t()) (n-1));;
> val get_first = fn : 'a LazyList -> int -> 'a list
(* Επιστρέφει τους ακεραίους 1, 2, ... , 10. *)
get_first (integers_from 1) 10;;
> val it = [1,2,3,4,5,6,7,8,9,10] : int list
</syntaxhighlight>

Χρησιμοποιώντας αντίστοιχες συναρτήσεις με αυτές που υπάρχουν για λίστες (όπως η <syntaxhighlight lang="sml" inline>map</syntaxhighlight>), μπορούμε να ορίσουμε πιο σύνθετες ακολουθίες όπως τους ζυγούς αριθμούς ή τα τετράγωνα αριθμών:
<syntaxhighlight lang="sml">
(* Map για άπειρες λίστες. *)
fun lazy_list_map f (Cons(x, t)) =
Cons(f x, fn () => lazy_list_map f (t()));;

(* Επιστρέφει μία ακολουθία με τους ζυγούς φυσικούς αριθμούς. *)
val even_naturals = lazy_list_map (fn x => 2 * x) (integers_from 0);;
(* Επιστρέφει τους πρώτους 10 ζυγούς: 0, 2, ... , 18. *)
get_first even_naturals 10;;
> val it = [0,2,4,6,8,10,12,14,16,18] : int list

(* Επιστρέφει μία ακολουθία με τα τετράγωνα αριθμών. *)
val squares = lazy_list_map (fn x => 2 * x) (integers_from 0);;
(* Επιστρέφει τα 4 πρώτα τετράγωνα: 0, 1, 4, 9. *)
get_first squares 4;;
> val it = [0,2,4,6] : int list
</syntaxhighlight>

==== Άπειρα δυαδικά δένδρα ====

Αντίστοιχα με τις άπειρες λίστες μπορούν να οριστούν άπειρα δυαδικά δένδρα, π.χ. ως εξής:
<syntaxhighlight lang="sml">
datatype 'a LazyBTree = Branch of 'a * (unit -> 'a LazyBTree) * (unit -> 'a LazyBTree)
</syntaxhighlight>

=== Ταίριασμα προτύπων ===

Η ML επιτρέπει το ταίριασμα προτύπων.{{r|LCF79|p=65-74}} Για παράδειγμα, την συνάρτηση <syntaxhighlight lang="sml" inline>factorial</syntaxhighlight>, μπορούμε να την γράψουμε ως εξής:
<syntaxhighlight lang="sml">
(* 1η Εκδοχή με την χρήση case ... of *)
fun factorial n = case n of
0 => 1
| n => n * factorial (n-1)

(* 2η Εκδοχή *)
fun factorial 0 = 1
| factorial n = n * factorial (n-1)
</syntaxhighlight>
Ο έλεγχος των προτύπων γίνεται με την σειρά που δίνονται οι περιπτώσεις. Επομένως, αν στην <syntaxhighlight lang="sml" inline>factorial</syntaxhighlight> αλλάξουμε την σειρά των περιπτώσεων, τότε το <math>n</math> θα ταιριάζει όλους τους ακεραίους και η δεύτερη περίπτωση δεν εκτελείται ποτέ. Αυτό οδηγεί στο σφάλμα <syntaxhighlight lang="console" inline>Error: match redundant</syntaxhighlight>. Αντίστοιχα αν δεν καλύπτονται όλες οι περιπτώσεις, εμφανίζεται η προειδοποίηση <syntaxhighlight lang="console" inline>Warning: match nonexhaustive</syntaxhighlight>.
<syntaxhighlight lang="sml">
fun factorial n = n * factorial (n-1)
| factorial 0 = 1;;
> stdIn: Error: match redundant
n => ...
--> 0 => ...

fun factorial 0 = 1;;
> stdIn: Warning: match nonexhaustive
0 => ...
</syntaxhighlight>

=== Εξαιρέσεις ===

Η ML υποστηρίζει εξαιρέσεις.{{r|P96|r=134-141}} Με την σύνταξη <syntaxhighlight lang="console" inline>raise <exception></syntaxhighlight> εγείρεται μία εξαίρεση και με το <syntaxhighlight lang="console" inline>handle <exception pattern> => <expression></syntaxhighlight> διαχειρίζεται μία εξαίρεση. Για την συνάρτηση <syntaxhighlight lang="sml" inline>factorial</syntaxhighlight> που ορίσαμε [[#Αναδρομή|παραπάνω]], για αρνητικούς αριθμούς μπορούμε να εγείρουμε μία εξαίρεση ως εξής:

<syntaxhighlight lang="sml">
fun factorial n =
if n = 0 then 1
else if n > 0 then n * factorial (n-1)
else raise Fail "Factorial only defined for non-negative integers."
(* Παρατηρήστε ότι ο τύπος της συνάρτησης δεν αλλάζει. *)
> val factorial = fn : int -> int
</syntaxhighlight>
Επομένως, άμα την καλέσουμε με αρνητικό ακέραιο, παίρνουμε:
<syntaxhighlight lang="console">
factorial (~2)
> uncaught exception Fail [Fail: Factorial only defined for non-negative integers.]
</syntaxhighlight>
Εκτός από τις εξαιρέσεις της γλώσσας (Overflow, Div, Domain, Chr, Subscript), επιπλέον εξαιρέσεις μπορούν να οριστούν από τον χρήστη:
<syntaxhighlight lang="sml">
(* Εξαίρεση ειδικά ορισμένη για την συνάρτηση factorial. *)
exception FactorialException of string;;

fun factorial n =
if n = 0 then 1
else if n > 0 then n * factorial (n-1)
else raise FactorialException "Given a negative integer.";;
factorial (~2);;
> uncaught exception FactorialException
</syntaxhighlight>

Ο παρακάτω κώδικας είναι ένα παράδειγμα διαχείρισης μίας εξαίρεσης:
<syntaxhighlight lang="sml">
(* Συνάρτηση που δέχεται μία λίστα και επιστρέφει 0 αν η λίστα είναι άδεια,
διαφορετικά το παραγοντικό του πρώτου στοιχείου (και 1 αν είναι αρνητικός αριθμός). *)
fun hd_factorial ls = factorial (hd ls)
handle Empty => 0
| _ => 1;;

hd_factorial [3, 1, 2];;
> val it = 6 : int
hd_factorial [~2, 1, 3];;
> val it = 1 : int
hd_factorial [];;
> val it = 0 : int
</syntaxhighlight>

Οι εξαιρέσεις μπορούν να χρησιμοποιηθούν και για την υλοποίηση της οπισθοδρόμησης.{{r|P96|p=139}}

=== Πράξεις με παρενέργειες ===

Η ML έχει πράξεις με [[παρενέργεια (υπολογιστές)|παρενέργειες]],{{r|P96|r=1313-1356}} όπως είναι η δήλωση ανάθεσης <syntaxhighlight lang="sml" inline>:=</syntaxhighlight>,
<syntaxhighlight lang="sml">
val a = ref 10;;
> val a = ref 10 : int ref
a := !a + 5;;
> val it = () : unit
a;;
> val it = ref 15 : int ref
</syntaxhighlight>


==Δείτε επίσης==
==Δείτε επίσης==
* [[OCaml]]
* [[OCaml]]


== Εξωτερικοί σύνδεσμοι ==

'''Σελίδες υποστήριξης των εκδόσεων της ML:'''
* [https://smlnj.org/ Standard ML of New Jersey]
* [https://smlfamily.github.io/ Standard ML Family GitHub Project]
* [https://www.polyml.org/ Poly/ML]
* [https://mosml.org/ Moscow ML]
* [http://mlton.org/ MLton]
'''Βιβλία:'''
* [https://www.cl.cam.ac.uk/~lp15/MLbook/ ML for the Working Programmer]{{r|P96}}
* [http://infolab.stanford.edu/~ullman/emlp.html Elements of ML Programming]{{r|U94}}
* [https://www.cambridge.org/core/books/purely-functional-data-structures/0409255DA1B48FA731859AC72E34D494 Purely Functional Data Structures]{{r|O96}}, Βιβλίο για την υλοποίηση δομών δεδομένων στην ML (και γενικά σε συναρτησιακές γλώσσες).
* [http://www0.cs.ucl.ac.uk/staff/C.Clack/papers/Books/progsml.html Programming With Standard ML]<ref>{{cite book |last1=Myers |first1=Colin |last2=Clark |first2=Chris |last3=Poon |first3=Ellen |title=Programming with Standard ML |year=1993 |publisher=Prentice Hall |location=New York |isbn=0-13-722075-8 }}</ref>
* [https://mitpress.mit.edu/books/little-mler The Little MLer]<ref>{{cite book |last=Felleisen |first=Matthias |title=The little MLer |year=1998 |publisher=MIT Press |location=Cambridge, Mass. |isbn=9780262561143}}</ref>
* [https://mitpress.mit.edu/books/commentary-standard-ml Commentary on Standard ML]<ref>{{cite book |last=Milner |first=R. |title=Commentary on Standard ML |year=1990 |publisher=MIT Press |location=Cambridge, MA |isbn=9780262631372}}</ref>
'''Tutorial:'''
* [https://www.cs.tufts.edu/~nr/cs257/archive/mads-tofte/four-lectures.pdf Four Lectures on Standard ML]<ref>{{cite web |last=Tofte |first=Mads |title=Four Lectures on Standard ML |url=https://www.cs.tufts.edu/~nr/cs257/archive/mads-tofte/four-lectures.pdf |accessdate=17 Ιουλίου 2022}}</ref>
* [https://www.cs.nmsu.edu/~rth/cs/cs471/sml.html A gentle introduction to SML]<ref>{{cite web |last=Cumming |first=Andrew |title=A Gentle Introduction to ML |url=https://www.cs.nmsu.edu/~rth/cs/cs471/sml.html |accessdate=17 Ιουλίου 2022}}</ref>
* [http://www.cs.cmu.edu/~rwh/isml/book.pdf Programming in Standard ML]<ref>{{cite web |last=Harper |first=Robert |title=Programming in Standard ML |url=http://www.cs.cmu.edu/~rwh/isml/book.pdf |publisher=Carnegie Mellon University |accessdate=17 Ιουλίου 2022}}</ref>
* [https://www.cs.cornell.edu/riccardo/prog-smlnj/notes-011001.pdf Notes on Programming Standard ML of New Jersey]<ref>{{cite web |last=Pucella |first=Riccardo |title=Notes on Programming Standard ML of New Jersey |url=https://www.cs.cornell.edu/riccardo/prog-smlnj/notes-011001.pdf |publisher=Cornell University |accessdate=17 Ιουλίου 2022}}</ref>
* [https://homepages.inf.ed.ac.uk/stg/NOTES/notes.pdf Programming in Standard ML ’97: A Tutorial Introduction]<ref>{{cite web |last=Gilmore |first=Stephen |title=Programming in Standard ML '97: An On-line Tutorial |url=https://homepages.inf.ed.ac.uk/stg/NOTES/notes.pdf |publisher=University of Edinburgh |accessdate=17 Ιουλίου 2022}}</ref>
'''Πανεπιστημιακά μαθήματα:'''
* [https://courses.softlab.ntua.gr/pl1/2022a/ Γλώσσες Προγραμματισμού Ι], Διαλέξεις 4-5
* [https://ocw.aoc.ntua.gr/courses/ECE137/ Γλώσσες Προγραμματισμού Ι: Βιντεομάθημα] Ενότητες 4-6
* [https://www.cl.cam.ac.uk/teaching/1819/FoundsCS/ Foundations of Computer Science], Εισαγωγή στην πληροφορική με ML.
* [http://www.cs.cmu.edu/~me/212/schedule.html Principles of Programming], Εισαγωγή στον προγραμματισμό με ML.

== Παραπομπές ==
<references />


{{Γλώσσες προγραμματισμού}}
{{Γλώσσες προγραμματισμού}}
Γραμμή 23: Γραμμή 441:


[[Κατηγορία:Συναρτησιακές γλώσσες προγραμματισμού]]
[[Κατηγορία:Συναρτησιακές γλώσσες προγραμματισμού]]

{{Πληροφορική-επέκταση}}

Έκδοση από την 00:48, 18 Ιουλίου 2022

Η 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]

Περιγραφή

Η ML συχνά αναφέρεται ως ακάθαρτη συναρτησιακή γλώσσα, επειδή επιτρέπει παρενέργειες, και επομένως διαδικαστικό προγραμματισμό,[4]: 89  σε αντίθεση με αμιγώς συναρτησιακές γλώσσες όπως η Haskell. Για αυτό το λόγο λέγεται και γλώσσα προγραμματισμού μικτού προτύπου.

Χαρακτηριστικά της ML περιλαμβάνουν την στρατηγική υπολογισμού της κλήσης κατά τιμή (call-by-value), συναρτήσεις πρώτης τάξης, αυτόματη διαχείριση μνήμης με συλλογή απορριμάτων, παραμετρικό πολυμορφισμό, στατικούς τύπους, εξαγωγή τύπων (type inference), αλγεβρικούς τύπους δεδομένων (algebraic datatypes), ταίριασμα προτύπων (pattern matching) και εξαιρέσεις. Παραδείγματα αυτών των χαρακτηριστικών δίνονται παρακάτω.

Αντίθετα με τη 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) είναι περιττοί:

fun increment (n:int) :int = n + 1

Η συνάρτηση μπορεί να γραφτεί ως εξής:

fun increment n = n + 1

και η γλώσσα θα εξαγάγει τον τύπο της μεταβλητής ως ακέραιο και της συνάρτησης increment ως συνάρτηση από ακεραίους σε ακεραίους:

val increment = fn : int -> int

Αναδρομή

Η ML υποστηρίζει και ενθαρρύνει την χρήση αναδρομικών συναρτήσεων[15]:54-60 (και αναδρομικών τύπων). Για παράδειγμα η παρακάτω συνάρτηση υπολογίζει το παραγοντικό.

fun factorial n = 
  if n = 0 then 1
  else n * factorial (n-1);;

factorial 4;; (* Υπολογίζει 4 * 3 * 2 * 1 = 24 *)
 > val it = 24 : int

Επίσης υποστηρίζει αμοιβαία αναδρομή (mutual recursion).[15]: 60-62  Για παράδειγμα, οι παρακάτω συναρτήσεις ελέγχουν (με μη αποδοτικό τρόπο) αν ένας μη-αρνητικός ακέραιος είναι ζυγός ή περιττός, καλώντας η μία την άλλη:

fun is_odd n = 
  if n = 0 then false
  else is_even (n-1)
and is_even n = 
  if n = 0 then true
  else is_odd (n-1)

 > val is_odd = fn : int -> bool
 > val is_even = fn : int -> bool

Συναρτήσεις ως τιμές

Στην ML οι συναρτήσεις είναι τιμές, που σημαίνει ότι μπορούν να δοθούν ως όρισμα μίας συνάρτησης ή να επιστραφούν ως αποτέλεσμα μίας συνάρτησης.[7]: 171-191  Μία συνάρτηση με όρισμα ή αποτέλεσμα μία άλλη συνάρτηση, λέγεται συνάρτηση ανωτέρου βαθμού.

Συνάρτηση ως όρισμα

Για παράδειγμα, η παρακάτω συνάρτηση παίρνει ως όρισμα μία οποιαδήποτε συνάρτηση και επιστρέφει το άθροισμα :

fun sum_three f = (f 1) + (f 2) + (f 3)

Άρα, όταν την καλέσουμε για η απάντηση είναι :

fun g x = 2 * x + 5;;
sum_three g;;

 > val g = fn : int -> int
 > val it = 27 : int

Συνάρτηση ως αποτέλεσμα

Για παράδειγμα, η παρακάτω συνάρτηση παίρνει ως όρισμα και επιστρέφει την γραμμική συνάρτηση της μορφής :

fun linear_fn (a, b) = 
  let fun new_fn x = a * x + b in
     new_fn
  end;;

(* Παρατηρήστε ότι ο τύπος της συνάρτησης επιβεβαιώνει
   αυτό που θέλουμε όρισμα: int * int (δηλαδή (a,b)) και
   αποτέλεσμα: int -> int (δηλαδή μία συνάρτηση). *)
 > val linear_fn = fn : int * int -> int -> int

Επομένως, η συνάρτηση , μπορεί να οριστεί ως εξής:

val threeXplusOne = linear_fn (3, 1);;
threeXplusOne 10;;
 > val threeXplusOne = fn : int -> int
 > val it = 31 : int

Καθώς η ML χρησιμοποιεί currying (δηλαδή όλες οι συναρτήσεις μετατρέπονται ώστε να έχουν ένα όρισμα), η linear_fn μπορεί να γραφτεί πιο σύντομα ως:

fun linear_fn (a, b) x = a * x + b;;

Ανώνυμες συναρτήσεις

Οι συναρτήσεις μπορούν να οριστούν και ως ανώνυμες.[7]: 172-173  Δηλαδή στο παραπάνω παράδειγμα, στην κλήση στο sum_three:

fun g x = 2 * x + 5;;
sum_three g;;

θα μπορούσε να χρησιμοποιηθεί μία ανώνυμη στην θέση της , ως εξής:

sum_three (fn x => 2 * x + 5)

Με αυτόν τον τρόπο αποφεύγεται η ονομασία απλών συναρτήσεων και μειώνονται οι γραμμές κώδικα.

Παραμετρικός πολυμορφισμός

Η ML επιτρέπει τον παραμετρικό πολυμορφισμό, δηλαδή τον ορισμό γενικών συναρτήσεων και γενικών τύπων, που δουλεύουν για όλους τους δυνατούς τύπους της παραμέτρου.

Παράδειγμα 1ο: Απλές συναρτήσεις

Η πιο απλή τέτοια συνάρτηση είναι η ταυτοτική συνάρτηση

fun id x = x

η οποία έχει τύπο

val id = fn : 'a -> 'a

To 'a είναι η παράμετρος και ο τύπος 'a -> 'a υποδηλώνει ότι συνάρτηση παίρνει ένα στοιχείο οποιοδήποτε τύπου και επιστρέφει ένα στοιχείο του ίδιου τύπου (στην συγκεκριμένη περίπτωση και το ίδιο στοιχείο). Αυτό μας επιτρέπει να γράψουμε:

id 3;;
 > val it = 3 : int
id 3.0;;
 > val it = 3.0 : real
id "Hello";;
 > val it = "Hello" : string

Μία λίγο πιο σύνθετη συνάρτηση είναι αυτή που αντιστρέφει τις τιμές ενός ζεύγους, π.χ. για είσοδο (10, "hello") επιστρέφει ("hello", 10). Αυτή μπορεί να οριστεί ως εξής:

fun swap (x, y) = (y, x);;
 > val swap = 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 (fn x => x + 10) [1, 6, 2, 7, 8];;
 > val it = [11,16,12,17,18] : int list
(* Επισημαίνει ποιοι από τους αριθμούς είναι ζυγοί *)
map (fn x => x mod 2 = 0) [1, 5, 2, 7, 8];;
 > val it = [false,false,true,false,true] : bool list
map (fn x => if x then "Even" else "Odd") [false,false,true,false,true];;
 > val it = ["Odd","Odd","Even","Odd","Even"] : string list

Η συνάρτηση map μπορεί να οριστεί ως εξής:

fun map f [] = []
|   map f (x::xs) = (f x)::map f xs;;
 > val map = fn : ('a -> 'b) -> 'a list -> 'b list

Ο τύπος της δηλώνει ότι δέχεται μία συνάρτηση τύπου 'a -> 'b (για παράδειγμα fn x => x mod 2 = 0 που ελέγχει αν ο είναι ζυγός) και μία λίστα τύπου 'a list (π.χ. μία λίστα ακεραίων) και επιστρέφει μία λίστα τύπου 'b list (π.χ. μία λίστα από booleans).

Παρόμοιες συναρτήσεις είναι η filter, exists, foldr.[7]: 182-186 

Παράδειγμα 3ο: Σύνθεση συναρτήσεων

Μπορούμε να ορίσουμε μία συνάρτηση που συνθέτει δύο συναρτήσεις ως εξής:

fun compose f g x = f ( g x )
 > val compose = fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b

Άρα για την σύνθεση της και της (που έχει ως αποτέλεσμα την ), γράφουμε:

val h = compose (fn x => 2 * x + 1) (fn x => x * x + 2)
h 10 (* Επιστρέφει 2 * 10^2 + 5 *)

Άλλη μία εφαρμογή της compose είναι η συγχώνευση δύο map. Στο προηγούμενο παράδειγμα τα δύο τελευταία map μπορούν να αντικατασταθούν με ένα:

map (compose (fn x => if x then "Even" else "Odd") (fn x => x mod 2 = 0)) [1, 5, 2, 7, 8]
 > val it = ["Odd","Odd","Even","Odd","Even"] : string list

Αλγεβρικός τύπος δεδομένων

Η ML υποστηρίζει τον ορισμό καινούργιων αλγεβρικών τύπων δεδομένων.[7]: 124-128 

Στην πιο απλή περίπτωση, αυτοί μπορεί να είναι απαριθμητικοί τύποι, π.χ. οχήματα:

datatype Vehicle = Bicycle | Car | Truck;;
fun vehicle_to_str Bicycle = "bicycle"
|   vehicle_to_str Car = "car"
|   vehicle_to_str Truck = "truck";;
 > val vehicle_to_str = fn : Vehicle -> string
vehicle_to_str Truck;;
 > val it = "truck" : string

Στην λίγο πιο σύνθετη μορφή, κάποιοι από τους τύπους μπορούν να έχουν παραπάνω πληροφορίες. Άμα θέλαμε για τα οχήματα να ξέρουμε και πόσες ρόδες έχουν, τότε θα αλλάζαμε τα φορτηγά ώστε:

(* Θεωρούμε ότι το ποδήλατο έχει 2 και το αυτοκίνητο 4 ρόδες. 
   Τα φορτηγά μπορεί να έχουν 6, 8, 10 ή και παραπάνω ρόδες, επομένως 
   πρέπει να επισημανθεί ο αριθμός όταν το ορίζουμε. *)
datatype VehicleWithWheels = Bicycle | Car | Truck of int;;

fun vehicle_to_str Bicycle = "bicycle has 2 wheels" 
|   vehicle_to_str Car = "car has 4 wheels"
|   vehicle_to_str (Truck n) = "truck has " ^ Int.toString n ^ " wheels";;
 > val vehicle_to_str = fn : VehicleWithWheels -> string
vehicle_to_str (Truck 10);;
 > val it = "truck has 10 wheels" : string

Οι τύποι αυτοί μπορεί να είναι και αναδρομικοί. Για παράδειγμα ο παρακάτω τύπος επιτρέπει τον ορισμό αριθμητικών παραστάσεων:

datatype Expression = 
  Num of int 
| Neg of Expression
| Plus of Expression * Expression 
| Mult of Expression * Expression;;

και η παρακάτω συνάρτηση επιτρέπει τον υπολογισμό τους.

fun evaluate (Num n) = n
|   evaluate (Neg e) = ~(evaluate e)
|   evaluate (Plus (e1, e2)) = (evaluate e1) + (evaluate e2)
|   evaluate (Mult (e1, e2)) = (evaluate e1) * (evaluate e2);;
 > val evaluate = fn : Expression -> int
 
(* Υπολογισμός της παράστασης: 10 + 5 * (7 - 3) = 30. *)
evaluate (Plus(Num 10, Mult(Num 5, Plus(Num 7, Neg(Num 3)))));;
 > val it = 30 : int

Οι τύποι μπορεί να είναι και πολυμορφικοί.[7]: 128-130  Για παράδειγμα, ένας τύπος για λίστες μπορεί να οριστεί ως εξής:

datatype 'a MyList = Empty | Item of 'a * 'a MyList

και συναρτήσεις όπως η length, για την εύρεση του μήκους μίας λίστας, μπορούν να οριστούν ως εξής:

(* Συνάρτηση που επιστρέφει το μήκος της λίστας. *)
fun length Empty = 0
|   length (Cons(x, t)) = 1 + length t;;
 > val length = fn : 'a MyList -> int

(* Ορισμός μίας λίστας με τα στοιχεία 10, 20 και 30. *)
val ls = Cons(10, Cons(20, Cons(30, Empty)));;
length ls;;
 > val it = 3 : int

Αντίστοιχα με τις λίστες, μπορούν να οριστούν και άλλες δομές, όπως τα δυαδικά δένδρα:

datatype 'a BTree = Leaf | Branch of 'a * ('a BTree) * ('a BTree)

Άπειρες δομές

Άπειρες λίστες

Ο ορισμός αναδρομικών δομών και η δυνατότητα της αναπαράστασης των συναρτήσεων ως τιμές, επιτρέπει την αναπαράσταση λιστών με άπειρο μέγεθος.[7]: 191-211  Ένας συνηθισμένος τρόπος είναι ο εξής:

(* Κάθε ένα κομμάτι της λίστας έχει ένα στοιχείο (τύπου 'a) 
   και μία συνάρτηση που όταν την καλέσουμε επιστρέφει 
   αναδρομικά την υπόλοιπη λίστα. *)
datatype 'a LazyList = Cons of 'a * (unit -> 'a LazyList)

Για παράδειγμα, η άπειρη ακολουθία ακεραίων ίσων ή μεγαλύτερων του , ορίζεται ως εξής:

fun integers_from n = 
   Cons(n, fn () => integers_from (n+1));;

Τα πρώτα στοιχεία της λίστας μπορούν να ανακτηθούν ως εξής:

fun get_first _ 0 = []
|   get_first (Cons(x, t)) n = x::(get_first (t()) (n-1));;
 > val get_first = fn : 'a LazyList -> int -> 'a list
(* Επιστρέφει τους ακεραίους 1, 2, ... , 10. *)
get_first (integers_from 1) 10;;
 > val it = [1,2,3,4,5,6,7,8,9,10] : int list

Χρησιμοποιώντας αντίστοιχες συναρτήσεις με αυτές που υπάρχουν για λίστες (όπως η map), μπορούμε να ορίσουμε πιο σύνθετες ακολουθίες όπως τους ζυγούς αριθμούς ή τα τετράγωνα αριθμών:

(* Map για άπειρες λίστες. *)
fun lazy_list_map f (Cons(x, t)) =
   Cons(f x, fn () => lazy_list_map f (t()));;

(* Επιστρέφει μία ακολουθία με τους ζυγούς φυσικούς αριθμούς. *) 
val even_naturals = lazy_list_map (fn x => 2 * x) (integers_from 0);;
(* Επιστρέφει τους πρώτους 10 ζυγούς: 0, 2, ... , 18. *)
get_first even_naturals 10;;
 > val it = [0,2,4,6,8,10,12,14,16,18] : int list

(* Επιστρέφει μία ακολουθία με τα τετράγωνα αριθμών. *) 
val squares = lazy_list_map (fn x => 2 * x) (integers_from 0);;
(* Επιστρέφει τα 4 πρώτα τετράγωνα: 0, 1, 4, 9. *)
get_first squares 4;;
 > val it = [0,2,4,6] : int list

Άπειρα δυαδικά δένδρα

Αντίστοιχα με τις άπειρες λίστες μπορούν να οριστούν άπειρα δυαδικά δένδρα, π.χ. ως εξής:

datatype 'a LazyBTree = Branch of 'a * (unit -> 'a LazyBTree) * (unit -> 'a LazyBTree)

Ταίριασμα προτύπων

Η ML επιτρέπει το ταίριασμα προτύπων.[1]: 65-74  Για παράδειγμα, την συνάρτηση factorial, μπορούμε να την γράψουμε ως εξής:

(* 1η Εκδοχή με την χρήση case ...  of *)
fun factorial n = case n of 
   0 => 1
|  n => n * factorial (n-1)

(* 2η Εκδοχή *)
fun factorial 0 = 1
|   factorial n = n * factorial (n-1)

Ο έλεγχος των προτύπων γίνεται με την σειρά που δίνονται οι περιπτώσεις. Επομένως, αν στην factorial αλλάξουμε την σειρά των περιπτώσεων, τότε το θα ταιριάζει όλους τους ακεραίους και η δεύτερη περίπτωση δεν εκτελείται ποτέ. Αυτό οδηγεί στο σφάλμα Error: match redundant. Αντίστοιχα αν δεν καλύπτονται όλες οι περιπτώσεις, εμφανίζεται η προειδοποίηση Warning: match nonexhaustive.

fun factorial n = n * factorial (n-1)
|   factorial 0 = 1;;
 > stdIn: Error: match redundant
          n => ...
    -->   0 => ...

fun factorial 0 = 1;;
 > stdIn: Warning: match nonexhaustive
          0 => ...

Εξαιρέσεις

Η ML υποστηρίζει εξαιρέσεις.[7] Με την σύνταξη raise <exception> εγείρεται μία εξαίρεση και με το handle <exception pattern> => <expression> διαχειρίζεται μία εξαίρεση. Για την συνάρτηση factorial που ορίσαμε παραπάνω, για αρνητικούς αριθμούς μπορούμε να εγείρουμε μία εξαίρεση ως εξής:

fun factorial n =
  if n = 0 then 1
  else if n > 0 then n * factorial (n-1)
  else raise Fail "Factorial only defined for non-negative integers."
(* Παρατηρήστε ότι ο τύπος της συνάρτησης δεν αλλάζει. *)
 > val factorial = fn : int -> int

Επομένως, άμα την καλέσουμε με αρνητικό ακέραιο, παίρνουμε:

factorial (~2)
 > uncaught exception Fail [Fail: Factorial only defined for non-negative integers.]

Εκτός από τις εξαιρέσεις της γλώσσας (Overflow, Div, Domain, Chr, Subscript), επιπλέον εξαιρέσεις μπορούν να οριστούν από τον χρήστη:

(* Εξαίρεση ειδικά ορισμένη για την συνάρτηση factorial. *)
exception FactorialException of string;;

fun factorial n =
  if n = 0 then 1
  else if n > 0 then n * factorial (n-1)
  else raise FactorialException "Given a negative integer.";;
  
factorial (~2);;
 > uncaught exception FactorialException

Ο παρακάτω κώδικας είναι ένα παράδειγμα διαχείρισης μίας εξαίρεσης:

(* Συνάρτηση που δέχεται μία λίστα και επιστρέφει 0 αν η λίστα είναι άδεια,
   διαφορετικά το παραγοντικό του πρώτου στοιχείου (και 1 αν είναι αρνητικός αριθμός). *)
fun hd_factorial ls = factorial (hd ls)
  handle Empty => 0
  |      _ => 1;;

hd_factorial [3, 1, 2];;
 > val it = 6 : int
hd_factorial [~2, 1, 3];;
 > val it = 1 : int
hd_factorial [];;
 > val it = 0 : int

Οι εξαιρέσεις μπορούν να χρησιμοποιηθούν και για την υλοποίηση της οπισθοδρόμησης.[7]: 139 

Πράξεις με παρενέργειες

Η ML έχει πράξεις με παρενέργειες,[7] όπως είναι η δήλωση ανάθεσης :=,

val a = ref 10;;
 > val a = ref 10 : int ref
a := !a + 5;;
 > val it = () : unit
a;;
 > val it = ref 15 : int ref

Δείτε επίσης

Εξωτερικοί σύνδεσμοι

Σελίδες υποστήριξης των εκδόσεων της ML:

Βιβλία:

Tutorial:

Πανεπιστημιακά μαθήματα:

Παραπομπές

  1. 1,0 1,1 1,2 Gordon, Michael J. C. (1979). Edinburgh LCF : a mechanised logic of computation. Berlin: Springer-Verlag. ISBN 978-3-540-09724-2. 
  2. 2,0 2,1 Milner, R. (1982). «How ML evlolved». ML/Hope/LCF Newsletter 1 (1): 25-34. https://www.research.ed.ac.uk/en/publications/how-ml-evlolved. 
  3. 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. 
  4. 4,0 4,1 Milner, Robin· Tofte, Mads· Harper, Robert· MacQueen, David (1997). The definition of standard ML : revised (PDF). Cambridge, Mass.: MIT Press. ISBN 9780262631815. 
  5. Hindley, R. (1969). «The Principal Type-Scheme of an Object in Combinatory Logic». Transactions of the American Mathematical Society 146: 29. doi:doi:10.2307/1995158. 
  6. 6,0 6,1 Milner, Robin (1978). «A theory of type polymorphism in programming». Journal of Computer and System Sciences 17 (3): 348-375. doi:https://doi.org/10.1016/0022-0000(78)90014-4. 
  7. 7,00 7,01 7,02 7,03 7,04 7,05 7,06 7,07 7,08 7,09 7,10 7,11 7,12 Paulson, Lawrence C. (1996). ML for the working programmer (2η έκδοση). Cambridge: Cambridge University Press. ISBN 978-0-521-57050-3.  Σφάλμα αναφοράς: Μη έγκυρη ετικέτα <ref> • όνομα " P96 " ορίζεται πολλές φορές με διαφορετικό περιεχόμενο
  8. Augustsson, L.; Johnsson Τ. (1989). «The Chalmers Lazy-ML Compiler». The Computer Journal 32 (2): 127–141. doi:https://doi.org/10.1093/comjnl/32.2.127. 
  9. Kumar, Ramana; Myreen, Magnus O.; Norrish, Michael; Owens, Scott (2014). «CakeML: a verified implementation of ML». ACM SIGPLAN Notices 49 (1): 179–191. doi:https://doi.org/10.1145/2578855.2535841. 
  10. Appel, Andrew W. (1998). Modern compiler implementation in ML. Cambridge: Cambridge University Press. ISBN 0-521-60764-7. 
  11. Nipkow, Tobias· Wenzel, Markus· Paulson, Lawrence C. (2002). Isabelle/HOL : a proof assistant for higher-order logic. Berlin: Springer. ISBN 978-3-540-43376-7. 
  12. 12,0 12,1 Okasaki, Chris (1996). Purely functional data structures. Cambridge, U.K.: Cambridge University Press. ISBN 9780521663502. 
  13. Wong, Limsoon (2000). «The functional guts of the Kleisli query system». ACM SIGPLAN Notices 35 (9): 1–10. doi:https://doi.org/10.1145/357766.351241. 
  14. «OCaml in Industry». OCaml. Ανακτήθηκε στις 17 Ιουλίου 2022. 
  15. 15,0 15,1 15,2 Ullman, Jeffrey D. (1994). Elements of ML programming (ML97 έκδοση). Upper Saddle River, NJ: Prentice Hall. ISBN 9780137903870. 
  16. Myers, Colin· Clark, Chris· Poon, Ellen (1993). Programming with Standard ML. New York: Prentice Hall. ISBN 0-13-722075-8. 
  17. Felleisen, Matthias (1998). The little MLer. Cambridge, Mass.: MIT Press. ISBN 9780262561143. 
  18. Milner, R. (1990). Commentary on Standard ML. Cambridge, MA: MIT Press. ISBN 9780262631372. 
  19. Tofte, Mads. «Four Lectures on Standard ML» (PDF). Ανακτήθηκε στις 17 Ιουλίου 2022. 
  20. Cumming, Andrew. «A Gentle Introduction to ML». Ανακτήθηκε στις 17 Ιουλίου 2022. 
  21. Harper, Robert. «Programming in Standard ML» (PDF). Carnegie Mellon University. Ανακτήθηκε στις 17 Ιουλίου 2022. 
  22. Pucella, Riccardo. «Notes on Programming Standard ML of New Jersey» (PDF). Cornell University. Ανακτήθηκε στις 17 Ιουλίου 2022. 
  23. Gilmore, Stephen. «Programming in Standard ML '97: An On-line Tutorial» (PDF). University of Edinburgh. Ανακτήθηκε στις 17 Ιουλίου 2022.