2010-02-09 3 views
4

Poursuite de la quête pour donner un sens à ContT et ses amis. Veuillez considérer le code (absurde mais illustratif) ci-dessous:Haskell confusion avec ContT, callCC, quand

v :: IO (Either String [String]) 
v = return $ Left "Error message" 

doit :: IO (Either String()) 
doit = (flip runContT return) $ callCC $ \k -> do 
    x <- liftIO $ v 
    x2 <- either (k . Left) return x 
    when True $ k (Left "Error message 2") 
    -- k (Left "Error message 3") 
    return $ Right() -- success 

Ce code ne compile pas. Toutefois, si le remplace le when avec l'appel k commenté ci-dessous, il compile. Que se passe-t-il?

Sinon, si je commente la ligne x2, il compile également. ???

De toute évidence, il s'agit d'une version distillée du code original et donc tous les éléments ont un but. Appréciez l'aide explicative sur ce qui se passe et comment y remédier. Merci.

+4

"Poursuite de la quête pour donner un sens à ContT et à ses amis"? Une course de fou! La continuation qui peut être comprise n'est pas la vraie continuation. –

Répondre

6

Le problème a à voir avec les types de when et either, rien de particulier à ContT:

when :: forall (m :: * -> *). (Monad m) => Bool -> m() -> m() 
either :: forall a c b. (a -> c) -> (b -> c) -> Either a b -> c 

Le second argument doit être de type m() pour une monade m. La ligne when de votre code pourrait donc être modifié comme ceci:

when True $ k (Left "Error message 2") >> return() 

pour faire la compilation de code. Ce n'est probablement pas ce que vous voulez faire, mais il nous donne un indice quant à ce qui pourrait être faux: le type de k a été déduit pour être quelque chose de désagréable à when.

Maintenant, pour la signature either: notez que les deux arguments à either doivent être des fonctions qui produisent des résultats du même type. Le type de return ici est déterminé par le type de x, qui est à son tour fixé par la signature explicite sur v. Ainsi, le bit (k . Left) doit avoir le même type; à son tour, fixe le type de k à (GHC déterminé)

k :: Either String() -> ContT (Either String()) IO [String] 

Ceci est incompatible avec les attentes de when.

Lorsque vous commentez la ligne x2, cependant, son effet sur le point de vue du contrôleur de type du code est supprimé, si k n'est plus forcé dans un type pratique et est libre d'assumer le type

k :: Either [Char]() -> ContT (Either [Char]()) IO() 

ce qui est bien dans when livre. Ainsi, le code compile. En guise de note finale, j'ai utilisé la fonction breakpoints de GHCi pour obtenir les types exacts de k dans les deux scénarios - je ne suis pas assez expert pour les écrire à la main et être certain de leur exactitude. :-) Utilisez :break ModuleName line-number column-number pour l'essayer.

+0

Merci. Cela a du sens mais ne résout pas tout à fait mon problème. Je pense que l'intention de mon code est claire. Vous cherchez un moyen d'obtenir des données de IO, et dans diverses conditions, échapper à la fonction. Quelle serait la bonne façon d'écrire cela avec des continuations? – me2

+0

Eh bien, en fait, vous pouvez utiliser le truc que j'ai mentionné dans la réponse et utiliser 'k (...) >> return() 'dans le bit' when' (et les expressions similaires où les différences de type surviennent), car la partie '>> return()' n'a pas d'importance - si 'k' est appelé à ce stade, vous sortons de ce calcul quand même. J'ai suggéré dans la réponse que ce n'est probablement pas ce que vous voulez, mais maintenant je crois que le contraire est vrai. Pourriez-vous essayer et me dire si cela peut vous aider? Si c'est le cas, je l'éditerai dans la réponse appropriée. –

+0

Bien sûr, votre code tel qu'il est maintenant est garanti de retourner 'Left 'Message d'erreur" 'quelle que soit la petite correction que vous appliquez pour le compiler, parce que' k' est déjà appelé sur la ligne 'uns'. Si vous changez 'v' en' return $ Right ["Foo"] 'et ajoutez' >> return() ''a la ligne' when', alors il retournera 'Left 'Message d'erreur 2" '. HTH. –