创建一个读取器和 Maybe Monad(Applicative Functor)组合

6
我想做的是将Reader Monad构建为Applicative Functor,实现以下功能:
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]

然而,如果要运行类似以下的内容:

runReader myDataFromApplicative [1]

而不是给我MyData 0 0

我希望它给我错误

我正在尝试创建自己的Reader Monad来实现这一点,但还没有完全弄清楚。

我想象的是这样的(显然这只是一个概述)

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]

会输出什么。
Success MyData 3 3
Error
1个回答

8

您不需要编写自己的单子,因为这正是单子变换器和单子堆栈解决的问题。由于您想要一个ReaderMaybe的组合,因此可以使用ReaderT变换器和Maybe单子。例如:

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

get2Sum 的类型意味着我们有一个外部单子 Reader [Int],其中包含内部单子 Maybe。在 get2Sum 的实现中,使用 lift 运行内部单子中的操作(在本例中,只需使用 Nothing 表示错误)。现在当您运行时(注意 T 中的 runReaderT

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

你获得

Just (MyData 3 3)
Nothing

您还可以将单子栈隐藏在自定义的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]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接