Type Correspondance en Haskell
-
18-09-2019 - |
Question
Si UnType est défini comme suit:
data SomeType = X {myBool :: Bool}
| Y {myString :: String}
| Z {myString :: String}
et je vais mettre à jour un arbitraire X, en fonction de son genre comme suit:
changeST :: SomeType -> SomeType
changeST (X b) = (X True)
changeST (Y s) = (Y "newString")
changeST (Z s) = (Z "newString")
La troisième et la quatrième ligne font la même, ils mettent à jour la chaîne dans le type donné. Est-il possible de remplacer ces deux lignes par un seul, par exemple. en affectant le type d'une variable?
La solution
Non en affectant le type à une variable, mais en faisant le remplacement du terrain:
changeST :: SomeType -> SomeType
changeST (X b) = (X True)
changeST st = st { myString = "newString" }
retourne la même st comme argument, mais avec la valeur du champ de myString
remplacé. Il est l'une des fonctionnalités intéressantes de champs que vous pouvez le faire sans se soucier que constructeur de données est, tant qu'il est l'un des constructeurs de données qui utilise myString
.
Autres conseils
Vous pouvez utiliser Scrap-votre-boilerplate pour cela.
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Generics
data SomeType
= X { myBool :: Bool }
| Y { myString :: String }
| Z { myString :: String }
deriving (Data, Typeable)
changeST :: SomeType -> SomeType
changeST = everywhere (mkT (const True)) . everywhere (mkT (const "newString"))
Ce changeST
change tous les String
internes dans votre structure "newString"
et chaque Bool
à True
.
Je préfère la solution de Dan, mais les gardes de motif dans GHC (standard Haskell 2010) sont une alternative propre à la proposition de Michael:
{-# LANGUAGE PatternGuards #-}
changeST :: SomeType -> SomeType
changeST x | X _ <- x = X True
| Y _ <- x = Y newString
| Z _ <- x = Z newString
where newString = "newString"
Vos trois définitions de changeST
sont séparées les unes des autres, de sorte que la réponse est « non ». Il y a, cependant, au moins deux façons dont vous pouvez le faire.
correspondance de motif à la fois les constructeurs de Y
et Z
à la fois:
Vous pouvez combiner les 2e et 3e définition en rendant votre modèle correspondant plus général:
changeST x = x { myString = "newString"}
Cela crée une nouvelle version de x
, que ce soit un Y
ou un Z
, en remplacement de l'élément de chaîne. Vous devez être prudent lorsque vous faites cela, cependant. Si vous renommez plus tard, le champ de chaîne de Z
, par exemple, vous obtiendrez des échecs d'exécution match de modèle lors de l'appel changeST avec un argument Z
.
Utiliser une expression de cas:
Si vous combinez vos trois définitions en un seul, vous pouvez partager des données entre eux.
changeST :: SomeType -> SomeType
changeST x = case x of
X _ -> X True
Y _ -> Y newString
Z _ -> Z newString
where
newString = "newString"