haskell - 模块化程序设计 - 将 Monad Transformer 组合到 Monad 不可知函数中

标签 haskell monad-transformers

我正在尝试提出一个模块化程序设计,我再次请求您的帮助。

作为以下帖子的后续内容 Monad Transformers vs passing ParametersLarge 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/

相关文章:

haskell - 真实世界#在哪里定义的?

c - 通过 FFI 在 C 和 Haskell 之间传递类结构数据

scala - 在 Haskell 中实现 List#flatten

haskell - 如何在 Elm 中结合结果和状态?

Scalaz:过滤器在|@|中扮演什么角色?

haskell - 具有MaybeT和RandT的Monad变压器堆栈

haskell - 使用 getChar 重新实现 getContents

performance - 为什么 `filterM + mapM_`比 `mapM_ + when`慢得多,且列表很大?

monads - 为什么在编写新的 Monad Transformers 时使用样板

haskell - MonadWriter 类中的冗余