Οι αδιαφανείς τύποι είναι μια τεχνική ενθυλάκωσης στην Elm.
Μερικοί την αποκαλούν και απόκρυψη πληροφορίας, ή απόκρυψη λεπτομερειών υλοποίησης.
Χρησιμοποιούνται επίσης συχνά για την επιβολή επικύρωσης, με το μοτίβο Parse, dont validate.
Για παράδειγμα, θα μπορούσες να έχεις έναν αδιαφανή τύπο EmailAddress, που μπορεί να περιέχει μόνο έγκυρες διευθύνσεις email (σε αντίθεση με μια String, που μπορεί να περιέχει οτιδήποτε).
Οι προσαρμοσμένοι τύποι έχουν μία ή περισσότερες παραλλαγές, και ο μεταγλωττιστής δημιουργεί αυτόματα μια συνάρτηση για τη δημιουργία κάθε παραλλαγής, χρησιμοποιώντας το όνομα της παραλλαγής. Αυτός είναι ο μόνος τρόπος με τον οποίο μπορούν να δημιουργηθούν προσαρμοσμένοι τύποι. Ο μεταγλωττιστής επιτρέπει επίσης την αποδόμηση αυτών των παραλλαγών.
type Maybe a
= Nothing --> Variant implies a 'Nothing' function, with type 'Maybe a', and destructuring of 'Nothing'
| Just a --> Variant implies a 'Just' function, with type 'a -> Maybe a', and destructuring of 'Just _'
Από προεπιλογή, όλα αυτά είναι εσωτερικά στο αρχείο / στο module, και χρησιμοποιούμε τη λέξη-κλειδί exposing για να επιλέξουμε τι θα εξάγουμε.
Για να δημιουργήσεις έναν αδιαφανή τύπο, απλώς επιλέγεις να μην εξάγεις τις παραλλαγές.
Όταν το κάνεις αυτό, σχεδόν πάντα χρειάζεται να παρέχεις έναν άλλο τρόπο για να δημιουργείς και να παίρνεις τις παραλλαγές, εκθέτοντας δικές σου συναρτήσεις. Αυτές οι συναρτήσεις σου επιτρέπουν να εφαρμόζεις κάποια λογική όταν δημιουργείς και παίρνεις τις παραλλαγές. Έτσι κρύβονται οι εσωτερικές λεπτομέρειες υλοποίησης του module, και σημαίνει ότι μπορείς να αλλάζεις τις λεπτομέρειες υλοποίησης όσο θέλεις, αρκεί οι εκτεθειμένες συναρτήσεις να παραμένουν ίδιες.
Ίσως η πιο συνηθισμένη χρήση των αδιαφανών τύπων είναι η επιβολή επικύρωσης, με το μοτίβο Parse, dont validate.
Σε αυτό το μοτίβο, εκθέτεις μια συνάρτηση που δέχεται μια είσοδο με λιγότερη δομή / ασφάλεια τύπων από ό,τι θέλεις, όπως μια String, και μετά κάνεις parse αυτή την είσοδο σε έναν πιο δομημένο τύπο, όπως EmailAddress, είτε επιστρέφοντας τον τύπο, είτε επιστρέφοντας ένα σφάλμα (συνήθως Maybe ή Result).
Για παράδειγμα, στον παρακάτω κώδικα, τα εξωτερικά module μπορούν να δημιουργήσουν έναν τύπο EmailAddress μόνο μέσω της συνάρτησης parseEmailAddress, οπότε όταν δουλεύεις με τον τύπο EmailAddress, είσαι σίγουρος ότι είναι έγκυρος.
module EmailAddress exposing ( EmailAddress, parseEmailAddress, getEmailAddress )
-- The `EmailAddress` custom type has one variant, also called `EmailAddress`
-- When a custom type has one variant, it is idiomatic to use the same name for both.
type EmailAddress =
EmailAddress String
-- create an EmailAddress variant
parseEmailAddress: String -> Maybe EmailAddress
parseEmailAddress candidateEmailAddress =
if isValidEmailAddress candidateEmailAddress then
Just (EmailAddress candidateEmailAddress)
else
Nothing
-- get an EmailAddress variant
getEmailAddress: EmailAddress -> String
getEmailAddress ( EmailAddress emailAdress ) =
emailAdress
isValidEmailAddress: String -> bool
-- ...
Σε αυτή την άσκηση θα γράψεις ένα γενικό (ή μαγικό!) SecureTreasureChest, για να αποθηκεύεις κάποιον θησαυρό.
Οι πιο παρατηρητικοί θα έχουν προσέξει ότι το προηγούμενο TreasureChest δεν ήταν και τόσο ασφαλές!
Αν χρησιμοποιούσες τη συνάρτηση getTreasure, έπρεπε να δώσεις τον κωδικό πρόσβασης, αλλά μπορούσες ακόμα απλώς να αποδομήσεις τον τύπο TreasureChest για να πάρεις τον θησαυρό, χωρίς να χρειάζεται να ξέρεις τον κωδικό πρόσβασης.
Ακόμα χειρότερα, μπορούσες να ανακτήσεις με τον ίδιο τρόπο ακόμα και τον κωδικό πρόσβασης!
Ας το διορθώσουμε αυτό χρησιμοποιώντας έναν αδιαφανή τύπο.
Όρισε έναν προσαρμοσμένο τύπο Password με μία μόνο παραλλαγή.
Η παραλλαγή θα πρέπει να έχει μια συσχετισμένη τιμή String, για τον κωδικό πρόσβασης.
Θα πρέπει να εκθέσεις τον τύπο Password, αλλά όχι την παραλλαγή.
Αυτό κάνει το Password έναν αδιαφανή τύπο και σημαίνει ότι δεν μπορείς πλέον να χρησιμοποιήσεις αποδόμηση για να πάρεις τον κωδικό πρόσβασης.
Δεν είναι δυνατό να ελέγξεις αν ένας τύπος είναι αδιαφανής ή όχι· σε κώδικα παραγωγής, θα χρησιμοποιούσες το elm-review (ένα εργαλείο στατικής ανάλυσης) για να το επιβάλεις.
Όρισε και έκθεσε μια συνάρτηση createPassword, με ορισμό τύπου, που δέχεται ένα String και επιστρέφει ένα Maybe Password.
Το Owasp συνιστά οι κωδικοί πρόσβασης να έχουν τουλάχιστον 8 χαρακτήρες, οπότε ας χρησιμοποιήσουμε το μοτίβο Parse, don't validate για να επιβάλουμε αυτή την αναλλοίωτη συνθήκη.
Αν ο κωδικός πρόσβασης έχει λιγότερους από 8 χαρακτήρες, η createPassword θα πρέπει να επιστρέφει Nothing, αλλιώς θα πρέπει να επιστρέφει Just Password.
Θα πρέπει να εκθέσεις τον τύπο SecureTreasureChest, αλλά όχι την παραλλαγή.
Αυτό κάνει το SecureTreasureChest έναν αδιαφανή τύπο και σημαίνει ότι δεν μπορείς πλέον να χρησιμοποιήσεις αποδόμηση για να πάρεις τον κωδικό πρόσβασης ή τον θησαυρό.
Αυτή η συνάρτηση θα πρέπει να δέχεται δύο παραμέτρους και να έχει ορισμό τύπου.
Password (για τον κωδικό πρόσβασης)Η συνάρτηση θα πρέπει να επιστρέφει ένα SecureTreasureChest.
Έκθεσε αυτή τη συνάρτηση, ώστε άλλα modules να μπορούν να δημιουργούν στιγμιότυπα του SecureTreasureChest.
Αυτή η συνάρτηση θα πρέπει να δέχεται δύο παραμέτρους και να έχει ορισμό τύπου.
String (για τον κωδικό πρόσβασης)SecureTreasureChest
Αυτή η συνάρτηση θα πρέπει να συγκρίνει τον κωδικό πρόσβασης που δίνεται με το Password του SecureTreasureChest.
Η συνάρτηση θα πρέπει να επιστρέφει ένα Maybe.
Αν οι κωδικοί πρόσβασης ταιριάζουν, τότε επέστρεψε Just με τη γενική τιμή από το SecureTreasureChest (τον θησαυρό!)
Αν οι κωδικοί πρόσβασης δεν ταιριάζουν, τότε επέστρεψε Nothing.
Έκθεσε αυτή τη συνάρτηση, ώστε άλλα modules να μπορούν να πάρουν τον θησαυρό από ένα SecureTreasureChest, αρκεί να δίνουν τον σωστό κωδικό πρόσβασης.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Elm με 28 έννοιες110 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.