I tipi opachi sono una tecnica di incapsulamento in Elm.
Alcuni la chiamano anche occultamento delle informazioni, o occultamento dei dettagli di implementazione.
Vengono anche usati comunemente per imporre la validazione, usando il pattern Analizza, non validare.
Per esempio, potresti avere un tipo opaco EmailAddress, che può contenere solo indirizzi email validi (al contrario di una String, che può contenere qualsiasi cosa).
I tipi personalizzati hanno una o più varianti, e il compilatore genera automaticamente una funzione per creare ogni variante, usando il nome della variante. Questi sono l'unico modo in cui si possono creare i tipi personalizzati. Il compilatore consente anche la destrutturazione di queste varianti.
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 _'
Per impostazione predefinita, tutto questo è interno al file / modulo, e usiamo la parola chiave exposing per scegliere cosa esportare.
Per creare un tipo opaco, ti basta scegliere di non esportare le varianti.
Quando lo fai, quasi sempre devi fornire un altro modo per creare e ottenere le varianti, esponendo funzioni personalizzate. Queste funzioni personalizzate ti permettono di applicare una certa logica quando crei e ottieni le varianti. Questo nasconde i dettagli di implementazione interni del modulo e significa che puoi cambiare i dettagli di implementazione quanto vuoi, purché le funzioni esposte rimangano le stesse.
Probabilmente l'uso più comune dei tipi opachi è imporre la validazione, usando il pattern Analizza, non validare.
In questo pattern, esponi una funzione che riceve un input con meno struttura / sicurezza di tipo di quanta ne vorresti, come una String, e poi lo analizzi per ottenere un tipo più strutturato, come EmailAddress, restituendo il tipo oppure un errore (di solito Maybe o Result).
Per esempio, nel codice qui sotto, per i moduli esterni è possibile creare un tipo EmailAddress solo usando la funzione parseEmailAddress, quindi quando lavori con il tipo EmailAddress hai la certezza che sia valido.
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
-- ...
In questo esercizio scriverai un SecureTreasureChest generico (e magico!), per custodire un tesoro.
Gli studenti più attenti avranno notato che il precedente TreasureChest non era poi così sicuro!
Se usavi la funzione getTreasure, dovevi fornire la password, ma potevi comunque destrutturare semplicemente il tipo TreasureChest per ottenere il tesoro, senza dover conoscere la password.
Peggio ancora, potevi recuperare la password nello stesso modo!
Risolviamo questo problema usando un tipo opaco.
Definisci un tipo personalizzato Password con una sola variante.
La variante deve avere un valore String associato, per la password.
Devi esporre il tipo Password, ma non la variante.
Questo rende Password un tipo opaco e significa che non puoi più usare la destrutturazione per ottenere la password.
Non è possibile verificare se un tipo è opaco oppure no; nel codice di produzione useresti elm-review (uno strumento di analisi statica) per imporlo.
Definisci ed esponi una funzione createPassword, con un'annotazione di tipo, che prende una String e restituisce un Maybe Password.
Owasp raccomanda che le password siano lunghe almeno 8 caratteri, quindi usiamo il pattern Parse, don't validate per imporre questo invariante.
Se la password è più corta di 8 caratteri, createPassword deve restituire Nothing, altrimenti deve restituire Just Password.
Devi esporre il tipo SecureTreasureChest, ma non la variante.
Questo rende SecureTreasureChest un tipo opaco e significa che non puoi più usare la destrutturazione per ottenere la password o il tesoro.
Questa funzione deve prendere due parametri e avere un'annotazione di tipo.
Password (per la password)La funzione deve restituire un SecureTreasureChest.
Esponi questa funzione, in modo che altri moduli possano creare istanze di SecureTreasureChest.
Questa funzione deve prendere due parametri e avere un'annotazione di tipo.
String (per la password)SecureTreasureChest
Questa funzione deve confrontare il tentativo di password fornito con la Password contenuta nel SecureTreasureChest.
La funzione deve restituire un Maybe.
Se le password corrispondono, restituisci Just con il valore generico contenuto nel SecureTreasureChest (il tesoro!).
Se le password non corrispondono, restituisci Nothing.
Esponi questa funzione, in modo che altri moduli possano ottenere il tesoro da un SecureTreasureChest, a patto che forniscano la password corretta.
Iscriviti a Exercism per imparare e padroneggiare Elm con 28 concetti110 esercizi e il mentoring di persone reali, tutto gratis.