La scelta tra le alternative in un tipo di dati algebrica Haskell
-
13-09-2019 - |
Domanda
Quando il tipo X
è definito come:
data X =
X { sVal :: String } |
I { iVal :: Int } |
B { bVal :: Bool }
e voglio il Int
all'interno di un valore di X
, se ce n'è uno, altrimenti zero.
returnInt :: X -> Int
Come è possibile determinare quale tipo di X
l'argomento per returnInt
è?
Soluzione
Giusto per chiarire un punto qui, mi permetta di riscrivere il tipo di dati per evitare ambiguità nel significato di X:
data SomeType = X { myString :: String} | I {myInt :: Int} | B {myBool :: Bool}
In questa definizione non ci sono X, I e tipo B. X, I e B sono costruttori che creano un valore di tipo Sometype
. Nota cosa succede quando si chiede ghci qual è il tipo di qualsiasi valore costruito con quei costruttori di tipo:
*Main> :t (I 5)
(I 5) :: Sometype
*Main> :t (B False)
(B False) :: Sometype
Appartengono allo stesso tipo !!
Così come è possibile usare X, I e B per la costruzione di tipi, è possibile utilizzare il pattern matching per decostruire il tipo, come fatto nelle altre risposte di cui sopra:
returnInt :: SomeType -> Int
returnInt (I x) = x -- if the pattern matches (I x) then return x
returnInt _ = error "I need an integer value, you moron" -- throw an error otherwise
Basta ricordare che il pattern matching si verifica in ordine:. Se il valore corrisponde al modello in qualche linea, non saranno eseguiti i modelli a righe sotto che
Si noti che quando si definisce il tipo di come avete fatto, utilizzando quello che viene chiamato Record di sintassi (solo guardare qui: http://en.wikibooks.org/wiki/Haskell/More_on_datatypes ), hai le funzioni del genere gratis !!
Prova a guardare il tipo di myInt, ad esempio:
*Main> :t myInt
myInt :: SomeType -> Int
E guarda che questa funzione fare:
*Main> myInt (I 5)
5
*Main> myInt (B False)
*** Exception: No match in record selector Main.myInt
Questo è esattamente il comportamento di returnInt
sopra definito. Il messaggio di errore strano solo ti dice che la funzione non sanno come trattare con un membro del SomeType tipo che non corrisponde (I x)
.
Se si definisce il tipo utilizzando la sintassi più comuni:
data SomeType2 = X String | I Int | B Bool
allora perdi quelle belle funzioni di registrazione.
I messaggi di errore terminano l'esecuzione del programma. Questo è fastidioso a volte. Se avete bisogno di un comportamento più sicuro per le funzioni risposta di GBacon è solo il modo per farlo. Conoscere il tipo Maybe a
e utilizzarlo per far fronte a questo tipo di calcolo, che hanno bisogno di tornare un valore o restituire nulla (provate questo: http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Maybe ).
Altri suggerimenti
Usa pattern matching.
returnInt :: X -> Int
returnInt (I x) = x
returnInt _ = 0
Con una definizione più flessibile per tutti i possibili valori X
:
returnInt :: X -> Maybe Int
returnInt (I i) = Just i
returnInt _ = Nothing
Quindi è possibile utilizzare maybe
per l'inadempiente particolare che si desidera-0 potrebbe essere un valore valido (questo è noto come il semipredicate problema ):
*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> maybe (-1) id (returnInt $ X "yo")
-1
Al contrario, funzioni parziali rischio runtime eccezioni:
*Main> let returnInt (I i) = i
*Main> :t returnInt
returnInt :: X -> Int
*Main> returnInt (B True)
*** Exception: <interactive>:1:4-22: Non-exhaustive patterns in function returnInt
Se ti senti veramente Froggy, si potrebbe usare MonadPlus
returnInt :: (MonadPlus m) => X -> m Int
returnInt (I i) = return i
returnInt _ = mzero
per ottenere una flessibilità ancora maggiore:
*Main> maybe 0 id (returnInt $ X "")
0
*Main> maybe 0 id (returnInt $ I 123)
123
*Main> returnInt (I 123) `mplus` returnInt (I 456) :: [Int]
[123,456]
Data una funzione come questa:
returnInt :: X -> Int
returnInt x = {- some integer -}
... il tipo di x
è sempre X
. Quello che interessa è se x
utilizza il X
, I
o B
tipo di costruzione.
Usa pattern matching a dire la differenza:
returnInt :: X -> Int
returnInt (X _) = error "needed an Int, got a String"
returnInt (I { iVal = n }) = n
returnInt (B _) = error "needed an Int, got a Bool"