2010-11-15 13 views
3

J'essaie les codes de < Real World Haskell >.Quelle est la différence entre 'err @ (Left _)' et 'Left err'

sur la version 6.10.4 GHC:

data ParseState = ParseState { 
    string :: String 
} deriving (Show) 

newtype Parse a = Parse { 
    runParse :: ParseState -> Either String (a, ParseState) 
} 

parse :: Parse a -> String -> Either String a 
parse parser initState = 
    case runParse parser (ParseState initState) of 
    Left err   -> Left err 
    Right (result, _) -> Right result 

Tout allait bien jusqu'à ce que je l'ai changé 'err gauche' à 'err @ (gauche _)':

-- [email protected](Left _)  -> err 
{- 
- Occurs check: cannot construct the infinite type: 
- a = (a, ParseState) 
- When generalising the type(s) for `parse' 
-} 

Toutes les idées?

Répondre

8

C'est subtil. Le case scrute une valeur de type Either String (a, ParseState), donc quand vous nommez le modèle dans

[email protected](Left _) -> err 

err a ce même type. Toutefois, le type de retour de la fonction indique qu'il doit être Either String a, ce qui ne correspond pas au type de Either String (a, ParseState). En regardant le type de Left:

Left :: x -> Either x y 

Lorsque vous utilisez Left dans la droite dans

Left err -> Left err 

Vous donnez une chance de choisir un y différent, à savoir a au lieu de (a, ParseState).

Ainsi, même si les valeurs sont les mêmes, les types ne sont pas, et ils ne peuvent pas être remplacés.

Par ailleurs, il y a quelques fonctions très pratiques pour votre cas Control.Arrow (spécialisée à (->) pour simplifier):

left :: (a -> a') -> Either a b -> Either a' b 
right :: (b -> b') -> Either a b -> Either a b' 
(+++) :: (a -> a') -> (b -> b') -> Either a b -> Either a' b' 

dont la sémantique sont fixées par les théorèmes libres (lire: ils ont seulement un mise en œuvre raisonnable, de sorte qu'ils font ce que vous attendez de leurs types). Donc, vous pouvez écrire votre code comme:

parse parser = right fst . runParse parser . ParseState 
+0

Merci, je suis juste le livre. Je vais faire une note mentale pour Control.Arrow et revenir plus tard. En ce qui concerne votre réponse à ma question, cela a du sens pour moi. Mais pensez-vous que le compilateur devrait être rendu plus intelligent pour savoir que err dans 'err @ (Left _)' a été apparié à un constructeur de données de gauche? – aXqd

+1

Non, je ne pense pas que ça devrait être plus intelligent. Ce n'est qu'une des subtilités du système de types, mais elle est cohérente. Un système de type capable de reconnaître de telles choses existe probablement (des GADT sont en route), mais il est plus compliqué à implémenter et a probablement plus de subtilités en usage ailleurs pour le compenser. Hindley-Milner est un bon point d'équilibre. – luqui

+0

Y at-il une erreur dans le type de (+++)? Ne devrait-il pas être (+++) :: (a-> a ') -> (b-> b') -> Soit un b -> Soit un 'b' –