Tipo de Correspondência em Haskell
-
18-09-2019 - |
Pergunta
Se SomeType é definido como:
data SomeType = X {myBool :: Bool}
| Y {myString :: String}
| Z {myString :: String}
e vou atualizar um arbitrário X, dependente de seu tipo, como segue:
changeST :: SomeType -> SomeType
changeST (X b) = (X True)
changeST (Y s) = (Y "newString")
changeST (Z s) = (Z "newString")
A terceira e a quarta linha de fazer o mesmo, eles atualizam a seqüência de caracteres de um dado tipo.Existe alguma maneira de substituir essas duas linhas em um único arquivo, por exemplo.atribuindo-se o tipo de uma variável?
Solução
Não, atribuindo-se o tipo de uma variável, mas fazendo a substituição de campo:
changeST :: SomeType -> SomeType
changeST (X b) = (X True)
changeST st = st { myString = "newString" }
Isso retorna o mesmo ponto como seu argumento, mas com o valor da myString
campo substituído.É um dos bons recursos de campos que você pode fazer isso sem se importar com que os dados construtor é, como um dos dados de construtores que usa myString
.
Outras dicas
Você pode usar Scrap Your-Boilerplate por esta.
{-# 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"))
este changeST
muda cada interno String
em sua estrutura para "newString"
e todo Bool
para True
.
Eu prefiro a solução de Dan, mas os guardas de padrões no GHC (padrão em Haskell 2010) são uma alternativa legal à proposta de Michael:
{-# LANGUAGE PatternGuards #-}
changeST :: SomeType -> SomeType
changeST x | X _ <- x = X True
| Y _ <- x = Y newString
| Z _ <- x = Z newString
where newString = "newString"
Suas três definições de changeST
são separados um do outro, então a resposta curta é "não". No entanto, existem pelo menos duas maneiras de fazer isso.
Padrão corresponde a ambos Y
e Z
Construtores de uma só vez:
Você pode combinar a 2ª e a 3ª definição, tornando seu padrão correspondente mais geral:
changeST x = x { myString = "newString"}
Isso cria uma nova versão de x
, seja um Y
ou a Z
, substituindo o membro da string. Você tem que ter cuidado ao fazer isso, no entanto. Se você renomear mais tarde o campo de string de Z
, por exemplo, você receberá falhas de correspondência de padrões de tempo de execução ao chamar Changest com um Z
argumento.
Use uma expressão de caso:
Se você combinar suas três definições em uma, poderá compartilhar dados entre eles.
changeST :: SomeType -> SomeType
changeST x = case x of
X _ -> X True
Y _ -> Y newString
Z _ -> Z newString
where
newString = "newString"