Тип соответствия в Haskell
Если SomeType определен как:
data SomeType = X {myBool :: Bool}
| Y {myString :: String}
| Z {myString :: String}
и я обновлю произвольный X, в зависимости от его типа, следующим образом:
changeST :: SomeType -> SomeType
changeST (X b) = (X True)
changeST (Y s) = (Y "newString")
changeST (Z s) = (Z "newString")
Третья и четвертая строка делают то же самое, они обновляют строку в заданном типе. Есть ли способ заменить эти две строки одной, например. присваивая тип переменной?
4 ответа
Не путем присвоения типа переменной, а путем замены поля:
changeST :: SomeType -> SomeType
changeST (X b) = (X True)
changeST st = st { myString = "newString" }
Это возвращает тот же st, что и его аргумент, но со значением myString
поле заменено. Это одна из приятных особенностей полей: вы можете делать это, не заботясь о том, какой это конструктор данных, если это один из конструкторов данных, который использует myString
,
Для этого вы можете использовать Scrap-Your-Boilerplate.
{-# 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"))
это changeST
меняет каждый внутренний String
в вашей структуре, чтобы "newString"
и каждый Bool
в True
,
Я предпочитаю решение Дэна, но шаблонные охранники в GHC (стандарт в Haskell 2010) - отличная альтернатива предложению Майкла:
{-# LANGUAGE PatternGuards #-}
changeST :: SomeType -> SomeType
changeST x | X _ <- x = X True
| Y _ <- x = Y newString
| Z _ <- x = Z newString
where newString = "newString"
Ваши три определения changeST
отделены друг от друга, поэтому краткий ответ "нет". Однако есть как минимум два способа сделать это.
Шаблон соответствует как Y
а также Z
Конструкторы сразу:
Вы можете объединить 2-е и 3-е определение, сделав соответствие вашего шаблона более общему:
changeST x = x { myString = "newString"}
Это создает новую версию x
будь то Y
или Z
, заменяя строковый член. Вы должны быть осторожны при этом, хотя. Если позже вы переименуете строковое поле Z
Например, при вызове changeST с Z
аргумент.
Используйте выражение case:
Если вы объединяете свои три определения в одно, вы можете обмениваться данными между ними.
changeST :: SomeType -> SomeType
changeST x = case x of
X _ -> X True
Y _ -> Y newString
Z _ -> Z newString
where
newString = "newString"