2012-10-23 18 views
6

Co chciałbym zrobić, to złożyć aplikacyjnych funktora z monady Reader, który robi coś takiego:Tworzenie kombinacji Reader i może Monad (aplikacyjnych funktora)

data MyData = Int Int 

get2Sum :: Reader [Int] Int 
get2Sum = do 
    myData <- ask 
    let fst2 = take 2 myData 
    case length fst2 of 
     2 -> return $ sum fst2 
     _ -> return 0 

myDataFromApplicative = MyData <$> get2Sum <*> get2Sum 

main = print $ runReader myDataFromApplicative [1,2] 

Jednakże, jeśli uruchomić coś jak

runReader myDataFromApplicative [1] 

Zamiast dać mi MyData 0 0

chcę dać mi Error

Grałem z tworzeniem własnego Reader Monad, aby to osiągnąć, ale nie mogłem tego rozgryźć.

Co ja sobie wyobrazić jest coś takiego (oczywiście jest to tylko zarys

data SuccessReader r a = Interm {runSuccessReader :: r -> SuccessReader a} | Success a | Error 
throwError :: SuccessReader() 


get2Sum :: Reader [Int] Int 
get2Sum = do 
    myData <- ask 
    let fst2 = take 2 myData 
    case length fst2 of 
     2 -> return $ sum fst2 
     _ -> throwError 

myDataFromApplicative = MyData <$> get2Sum <*> get2Sum 

main = do 
    print $ runSuccessReader myDataFromApplicative [1,2] 
    print $ runSuccessReader myDataFromApplicative [1] 

które wyjście

Success MyData 3 3 
Error 

Odpowiedz

8

Nie trzeba pisać własne monady, jak to jest dokładnie Problem polegający na tym, że monadowe transformatory i monadowe stosy rozwiązują się, ponieważ chcesz kombinację Reader i Maybe, możesz użyć transformatora ReaderT z monadą Maybe. Np.

get2Sum :: ReaderT [Int] Maybe Int 
get2Sum = do 
    myData <- ask 
    let fst2 = take 2 myData 
    case length fst2 of 
     2 -> return $ sum fst2 
     _ -> lift Nothing 

Typ get2Sum oznacza, że ​​ma zewnętrzną monadę Reader [Int] która zawiera wewnętrzną monadę Maybe. W implementacji get2Sum, lift służy do uruchamiania operacji w wewnętrznej monadzie (w tym przypadku po prostu błąd sygnalizacji z Nothing). Teraz po uruchomieniu (zauważ T w runReaderT)

main = do 
    print $ runReaderT myDataFromApplicative [1,2] 
    print $ runReaderT myDataFromApplicative [1] 

masz

Just (MyData 3 3) 
Nothing 

Można również ukryć stos monada wewnątrz zwyczaju newtype

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

import Control.Applicative 
import Control.Monad.Reader 

data MyData = MyData Int Int deriving Show 

newtype MyMonad a = MyMonad (ReaderT [Int] Maybe a) 
    deriving (Functor, Applicative, Monad, MonadReader [Int]) 

runMyMonad :: MyMonad a -> [Int] -> Maybe a 
runMyMonad (MyMonad m) = runReaderT m 

myError :: MyMonad a 
myError = MyMonad $ lift Nothing 

get2Sum :: MyMonad Int 
get2Sum = do 
    myData <- ask 
    let fst2 = take 2 myData 
    case length fst2 of 
     2 -> return $ sum fst2 
     _ -> myError 

myDataFromApplicative = MyData <$> get2Sum <*> get2Sum 

main = do 
    print $ runMyMonad myDataFromApplicative [1,2] 
    print $ runMyMonad myDataFromApplicative [1]