2010-03-20 61 views
0

爲什麼我能做到以下幾點:Haskell Applicative和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 

,但我不能這樣做:

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

,我如何修改F2,這樣它會工作,我打算?

回答

3

<$>僅要求ErrorT String GetFunctor的實例。 <*>要求它是Applicative的實例。我認爲這個實例聲明應該工作:

{-# LANGUAGE FlexibleInstances #-} 

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

使我疑惑:爲什麼不應用定義實例(Monad m)=> Applicative m?它只對「WrappedMonad m」這樣做。 – MtnViewMark 2010-03-20 07:02:11

+1

啊,我明白了,它需要UndecidableInstances才能這樣做。 – MtnViewMark 2010-03-20 07:05:17

+0

當然,不幸的是,添加這個實例聲明會給你一個孤兒實例。 – Steve 2010-03-23 09:22:24

0

要做錯誤處理,你不需要Either(T)單子。您可以通過組合在Applicative住得很好。示例(使用AccValidation累積所有錯誤的樂趣):

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