Тип соответствия в 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"
Другие вопросы по тегам