Miranda (γλώσσα προγραμματισμού)

Γενικά

Η Miranda είναι μια αμιγώς συναρτησιακή, οκνηρή γλώσσα προγραμματισμού: δεν έχει παρενέργειες και χαρακτηριστικά προστακτικού προγραμματισμού. Ένα πρόγραμμα σε Miranda (σενάριο ή script) είναι ένα σύνολο από ισότητες που ορίζουν διάφορες μαθηματικές συναρτήσεις και αλγεβρικούς τύπους δεδομένων (algebraic data types). Η λέξη σύνολο έχει σημασία: η σειρά των ισοτήτων δεν έχει συνήθως σημασία και δε χρειάζεται να οριστεί κάτι πριν χρησιμοποιηθεί.
Επειδή ο αλγόριθμος συντακτικής ανάλυσης χρησιμοποιεί έξυπνα τη στοίχιση του κώδικα, σπάνια χρειάζονται αγκύλες ανάμεσα στις εντολές και δε χρειάζονται σημεία τερματισμού των εντολών. Το χαρακτηριστικό αυτό, εμπνευσμένο αρχικά από την ISWIM χρησιμοποιείται επίσης στην occam και τη Haskell και έγινε αργότερα γνωστό από την Python.
Τα σχόλια εισάγονται στα κανονικά σενάρια με τη χρήση των χαρακτήρων || και συνεχίζουν μέχρι το τέλος της γραμμής. Μια εναλλακτική σύμβαση για σχόλια, που επηρεάζει όλο το αρχείο πηγαίου κώδικα και ονομάζεται "literate script", υποθέτει ότι κάθε γραμμή είναι σχόλιο εκτός από αυτές που αρχίζουν με το σύμβολο >.
Οι βασικοί τύποι δεδομένων της Miranda είναι οι εξής: char, num και bool. Μια συμβολοσειρά είναι απλά μια λίστα από char, ενώ μια μεταβλητή τύπου num αθόρυβα μετατρέπεται ανάμεσα σε δύο μορφές υλοποίησης: αρχικά σε ακεραίους άπειρης ακρίβειας (bignums), και σε τιμές κινητής υποδιαστολής όταν αυτό απαιτείται.
Οι ακολουθίες σταθερού μήκους (tuples) μπορούν να περιέχουν στοιχεία διάφορων μεταξύ τους τύπων, όμοια με τις εγγραφές (records) στις γλώσσες τύπου Pascal, και γράφεται ανάμεσα σε παρενθέσεις:

 this_employee = ("Folland, Mary", 10560, False, 35)
Η λίστα είναι η πιο συχνά χρησιμοποιούμενη δομή δεδομένων στη Miranda. Γράφεται ανάμεσα σε τετράγωνες αγκύλες και τα στοιχεία της, που πρέπει όλα να έχουν τον ίδιο τύπο, χωρίζονται με κόμμα:

 week_days = ["Mon","Tue","Wed","Thur","Fri"]
Η παράθεση λιστών (concatenation) είναι ++, η αφαίρεση είναι --, η προσθήκη είναι :, ο τελεστής μήκους είναι # και η πρόσβαση σε αριθμημένο στοιχείο (indexing) είναι !, επομένως:

 days = week_days ++ ["Sat","Sun"]
 days = "Nil":days
 days!0
→ "Nil"
 days = days -- ["Nil"]
 #days
→ 7
Υπάρχουν πολλές συντομεύσεις για την κατασκευή λιστών: το .. χρησιμοποιείται για λίστες με στοιχεία από μια σειρά αριθμών, με το βήμα αύξησης να μη χρειάζεται να είναι 1:

 fac n   = product [1..n]
 odd_sum = sum [1,3..100]
Πιο γενικές και ισχυρές δυνατότητες κατασκευής λιστών παρέχουν οι συμπεριλήψεις λιστών ("list comprehensions", παλιότερα γνωστές και ως "ZF expressions"), που έχουν δύο μορφές: μια έκφραση που εφαρμόζεται σε μια σειρά όρους, π.χ.:

 squares = [ n * n | n <- [1..] ]
(που σημαίνει: λίστα των τετραγώνων του n με το n να προέρχεται από τη λίστα όλως των θετικών ακεραίων) και μια σειρά, στην οποία κάθε όρος προέρχεται από τον προηγούμενο, π.χ.:

 powers_of_2 = [ n | n <- 1, 2*n .. ]
Όπως δείχνουν αυτά τα δύο παραδείγματα, η Miranda επιτρέπει λίστες με άπειρο αριθμό στοιχείων, με την πιο απλή να είναι η λίστα όλων των θετικών ακεραίων: [1..]
Η σύνταξη για την εφαρμογή συνάρτησης είναι απλά η παράθεση: sin x.
Στη Miranda, όπως και στις περισσότερες αμιγώς συναρτησιακές γλώσσες προγραμματισμού, οι συναρτήσεις είναι μέλη πρώτης τάξης, δηλαδή μπορούν να περαστούν σαν παράμετροι σε άλλες συναρτήσεις, να επιστραφούν σαν αποτελέσματα, ή να περιλαμβάνονται σαν μέλη σε δομές δεδομένων. Επιπλέον, μια συνάρτηση με δύο ή περισσότερες παραμέτρους μπορεί να παραμετροποιηθεί κατά ένα μέρος (currying), δίνοντάς της λιγότερες από όλες τις παραμέτρους. Αυτό επιστρέφει μια συνάρτηση, η οποία, αν της δοθούν οι υπόλοιπες παράμετροι, θα επιστρέψει αποτέλεσμα. Για παράδειγμα το πρόγραμμα:

 add a b = a + b
 increment = add 1
είναι ένας έμμεσος τρόπος να δημιουργηθεί μια συνάρτηση "increment" που προσθέτει ένα στην παράμετρό της. Στην πραγματικότητα, ο κώδικας add 4 7 παίρνει μια συνάρτηση δύο παραμέτρων add, την εφαρμόζει στο 4 με αποτέλεσμα μια συνάρτηση μιας παραμέτρου που προσθέτει τέσσερα στην παράμετρό της, και εφαρμόζει αυτήν στο 7.
Κάθε συνάρτηση που δέχεται δύο παραμέτρους μπορεί να μετατραπεί σε έναν ενθεματικό (infix) τελεστή (για παράδειγμα, δεδομένου του παραπάνω ορισμού της add, ο όρος $add είναι εντελώς ισοδύναμος με τον τελεστή+) και κάθε ενθεματικός τελεστής που παίρνει δύρο παραμέτρους μπορεί να μετατραπεί στην αντίστοιχη συνάρτηση. Επομένως ο κώδικας:

 increment = (+) 1
είναι ο συντομότερος τρόπος να δημιουργηθεί μια συνάρτηση που προσθέτει ένα στην παράμετρό της. Όμοια, στον κώδικα

 half = (/ 2)
 reciprocal = (1 /)
δημιουργούνται δύο συναρτήσεις μιας παραμέτρου. Ο διερμηνέας καταλαβαίνει σε κάθε περίπτωση ποια από τις δύο παραμέτρους τους τελεστή διαίρεσης δίνεται, δίνοντας συναρτήσεις που αντίστοιχα διαιρούν έναν αριθμό με το δύο και δίνουν τον αντίστροφό του.
Αν και η Miranda έχει ισχυρούς τύπους, δεν υποχρεώνει σε ρητές δηλώσεις τύπων. Αν ο τύπος μιας συνάρτησης δε δηλωθεί ρητά, ο διερμηνέας εξάγει τον τύπο της από τον τύπο των παραμέτρων της και το πώς χρησιμοποιούνται στο εσωτερικό της. Επιπλέον των βασικών τύπων (char, num, bool), υπάρχει και ο τύπος "anything" που σημαίνει ότι ο τύπος μιας παραμέτρου δε χρειάζεται, όπως στη συνάρτηση αντιστροφής λίστας:

 rev [] = []
 rev (a:x) = rev x ++ [a]
που μπορεί να εφαρμοστεί σε μια λίστα κάθε τύπου δεδομένων, και για την οποία η ρητή δήλωση του τύπου της θα ήταν:

 rev :: [*] -> [*]
Τέλος, η Miranda διαθέτει μηχανισμούς για τη δημιουργία και τη διαχείριση μονάδων κώδικα (modules), των οποίων οι εσωτερικές συναρτήσεις είναι αόρατες στα προγράμματα που κάνουν κλήση των μονάδων.

Παραδείγματα κώδικα

Το ακόλουθο σενάριο σε Miranda βρίσκει το σύνολο όλων των υποσυνόλων ενός συνόλου αριθμών:

  subsets []     = [[]]
  subsets (x:xs) = [[x] ++ y | y <- ys] ++ ys
                   where ys = subsets xs
και αυτό είναι ένα literate script για τη συνάρτηση primes που επιστρέφει τη λίστα όλων των πρώτων αριθμών

  > || Η άπειρη λίστα όλων των πρώτων αριθμών, από το κόσκινο του Ερατοσθένη.
  
  The list of potential prime numbers starts as all integers from 2 onwards;
  as each prime is returned, all the following numbers that can exactly be
  divided by it are filtered out of the list of candidates.
  
  > primes = sieve [2..]
  > sieve (p:x) = p : sieve [n | n <- x; n mod p ~= 0]