我正在尝试提出一个模块化程序设计,我再次请求您的帮助。
作为以下帖子的后续内容 Monad Transformers vs passing Parameters和 Large Scale Design in Haskell ,我正在尝试构建两个使用 Monad Transformers 但公开 Monad 不可知函数的独立模块,然后将每个模块中的 Monad 不可知函数组合成一个新的 Monad 不可知函数。
我无法运行组合功能,例如如何在下面的示例中使用 runReaderT
调用 mainProgram
?。
次要问题是:是否有更好的方法来实现相同的模块化设计目标?
<小时/>该示例有两个模拟模块(但可以编译),一个执行日志记录,另一个读取用户输入并对其进行操作。组合函数读取用户输入,记录并打印它。
{-# LANGUAGE FlexibleContexts #-}
module Stackoverflow2 where
import Control.Monad.Reader
----
---- From Log Module - Writes the passed message in the log
----
data LogConfig = LC { logFile :: FilePath }
doLog :: (MonadIO m, MonadReader LogConfig m) => String -> m ()
doLog _ = undefined
----
---- From UserProcessing Module - Reads the user Input and changes it to the configured case
----
data MessageCase = LowerCase | UpperCase deriving (Show, Read)
getUserInput :: (MonadReader MessageCase m, MonadIO m) => m String
getUserInput = undefined
----
---- Main program that combines the two
----
mainProgram :: (MonadReader MessageCase m, MonadReader LogConfig m, MonadIO m) => m ()
mainProgram = do input <- getUserInput
doLog input
liftIO $ putStrLn $ "Entry logged: " ++ input
最佳答案
有一种方法可以编写程序的完全模块化版本。解决这个问题的方法是将阅读器配置捆绑到一个数据结构中,然后定义类型类来描述特定函数对该数据结构所需的部分接口(interface)。例如:
class LogConfiguration c where
logFile :: c -> FilePath
doLog :: (MonadIO m, LogConfiguration c, MonadReader c m) => String -> m ()
doLog = do
file <- asks logFile
-- ...
class MessageCaseConfiguration c where
isLowerCase :: c -> Bool
getUserInput :: (MonadIO m, MessageCaseConfiguration c, MonadReader c m) => m String
getUserInput = do
lc <- asks isLowerCase
-- ...
data LogConfig = LC { logConfigFile :: FilePath }
data MessageCase = LowerCase | UpperCase
data Configuration = Configuration { logging :: LogConfig, casing :: MessageCase }
instance LogConfiguration Configuration where
logFile = logConfigFile . logging
instance MessageCaseConfiguration Configuration where
isLowerCase c = case casing c of
LowerCase -> True
UpperCase -> False
mainProgram :: (MonadIO m, MessageCaseConfiguration c, LogConfiguration c, MonadReader c m) => m ()
mainProgram = do
input <- getUserInput
doLog input
liftIO . putStrLn $ "Entry logged: " ++ input
现在,您可以在 ReaderT
monad 中使用 Configuration
调用 mainProgram
,它将按照您的预期工作。
关于haskell - 模块化程序设计 - 将 Monad Transformer 组合到 Monad 不可知函数中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13007123/