6

我有以下我经常做的样板,并且想消除。它看起来像这样:

type Configured = ReaderT Config

doSomething :: Configured IO Data
doSomething = do
   getMeta <- asks getMetaData
   meta <- liftIO getMeta

我想把它减少到这样的事情:

doSomething = do
    meta <- find getMetaData

不幸的是,我还没有完全把注意力集中在 monad 转换器上。是什么类型的find?是(Config -> IO Result) -> Result吗?我该怎么写?

非常感谢任何帮助我 grok monad 转换器的提示/解释。

谢谢!

4

1 回答 1

11

这可以以相当机械的方式完成。让我们从您的原始代码开始:

doSomething = do
    getMeta <- asks getMetaData
    meta <- liftIO getMeta
    ...

使用第三个单子定律,我们可以将我们想要提取的部分移动到它自己的 do-block 中:

doSomething = do
    meta <- do getMeta <- asks getMetaData
               liftIO getMeta
    ...

接下来,我们可以提取该子表达式并为其命名:

findMetaData = do getMeta <- asks getMetaData
                  liftIO getMeta

doSomething = do
    meta <- findMetaData
    ...

最后,让我们通过将显式引用替换为getMetaData参数来概括它:

find something = do x <- asks something
                    liftIO x

doSomething = do
     meta <- find getMetaData
     ...

现在,我们可以在 GHCi 中加载它并要求它为我们推断类型:

*Main> :t find 
find :: (MonadReader r m, MonadIO m) => (r -> IO b) -> m b

或者,我们可能希望对其进行一些清理并删除虚拟名称x

find something = ask >>= liftIO . something

为此,我使用了do-notation的定义asks和脱糖规则。

于 2011-11-16T22:33:47.587 回答