2010-03-20 4 views
0

Pourquoi est-ce que je peux faire ce qui suit:Haskell Applicative et ErrorT?

import Data.Word 
import Data.Binary.Get 
import Control.Applicative 
import Control.Monad.Error 

getW1 :: ErrorT String Get Word8 
getW1 = lift getWord8 

f1 = (+1) <$> getW1 

mais je ne peux pas faire:

f2 = (+) <$> getW1 <*> getW1 

et comment je dois-je modifier f2 de sorte qu'il fonctionne comme je compte?

Répondre

3

<$> requiert uniquement que ErrorT String Get soit une instance de Functor. <*> requiert que ce soit une instance de Applicative. Je pense que cette déclaration d'instance devrait fonctionner:

{-# LANGUAGE FlexibleInstances #-} 

instance (Error e, Monad m) => Applicative (ErrorT e m) where 
    pure = return 
    (<*>) = ap 
+0

à se demander: Pourquoi ne pas définir Applicative instance (Monad m) => Applicative m? Il le fait seulement pour "WrappedMonad m". – MtnViewMark

+1

Ah, je vois, il faudrait UndecidableInstances pour le faire. – MtnViewMark

+0

Bien sûr, ajouter cette déclaration d'instance vous laissera malheureusement une instance orpheline. – Steve

0

Pour faire la gestion des erreurs, vous n'avez pas besoin nécessaire, le Either(T) monade. Vous pouvez être parfaitement bien en restant dans Applicative via la composition. Exemple (pour le plaisir en utilisant AccValidation qui accumule toutes les erreurs): me

import Control.Applicative 
import Control.Monad.Error 

import Data.Validation 
import Data.Bifoldable 
import Data.Functor.Compose 

-- Replicating OP example with a Dummy monad (Get is made Applicative in newer libs) 

data Dummy a = D a 
    deriving Show 

instance Monad Dummy where 
    return = D 
    (D x) >>= f = f x 

instance Functor Dummy where 
    fmap f (D x) = D (f x) 

getM1 :: ErrorT String Dummy Int 
getM1 = lift (D 1) 

-- Can do with Applicatives + (Acc)Validation too 

instance Applicative Dummy where 
    pure = return 
    (<*>) = ap 

getA :: Compose Dummy (AccValidation String) Int 
getA = Compose $ D (success 1) 

getE :: Compose Dummy (AccValidation String) Int 
getE = Compose $ D (failure "bad") 

-- Applicative composition can work either way 

getA2 :: Compose (AccValidation String) Dummy Int 
getA2 = Compose $ success (D 1) 

getE2 :: Compose (AccValidation String) Dummy Int 
getE2 = Compose $ failure "bad" 

main = do 
    runMonadic $ (+) <$> getM1 <*> getM1 -- D "2" 
    -- 
    runApplicative $ (+) <$> getA <*> getA -- D "2" 
    runApplicative $ (+) <$> getE <*> getA -- D "bad" 
    runApplicative $ (+) <$> getE <*> getE -- D "badbad" 
    -- 
    runOtherApp $ (+) <$> getA2 <*> getA2 -- "D 2" 
    runOtherApp $ (+) <$> getE2 <*> getE2 -- "badbad" 
    where 
     runMonadic  = print . fmap (either id show) . runErrorT 
     runApplicative = print . fmap (validate id show) . getCompose 
     runOtherApp  = print . validate id show . getCompose 

-- some helper mimicking @[email protected] of @[email protected] 
validate :: (e -> c) -> (a -> c) -> AccValidation e a -> c 
validate f g = bifoldl (const f) (const g) undefined