学习Hapstack和Monad变形金刚



所以我有一个项目,我认为它足够简单,可以学习,但又足够复杂,非常有趣,我想使用Happstack库来编写。在最基本的层面上,这个项目只是一个花哨的文件服务器,它有一些特定于域的REST方法(或者其他什么,我真的不在乎它是否真的是RESTful)来搜索和获取所述文件和元数据。由于我现在也在尝试真正学习monad transformers,我决定这将是一个完美的学习项目。然而,我在开始时遇到了一些困难,尤其是在如何构建变压器堆栈方面。

现在,我只担心几件事:配置、错误报告、状态和日志记录,所以我从开始

newtype MyApp a = MyApp {
    runMyApp :: ReaderT Config (ErrorT String (StateT AppState IO)) a
} deriving (...)

由于我总是在IO中,我可以很容易地使用hslogger来处理我的日志记录。但我也知道我需要使用ServerPartT才能与Hapstack交互,因此

runMyApp :: ReaderT Config (ErrorT String (StateT AppState (ServerPartT IO))) a

我可以运行它,查看请求等,但我遇到的问题是,这需要为它实现FilterMonad,以便使用dirpathok等方法,但我不知道如何为这种类型实现它。我只需要它将过滤器传递到底层的monad。有人能给我一些关于如何实现这个显然至关重要的类型类的建议吗?或者,如果我只是做错了什么,就引导我朝着正确的方向前进。我在Happstack上才看了几天,变压器对我来说还是很新的。我想我已经足够了解它们的危险性,但我对它们的了解还不够,无法独自实现。非常感谢您能提供的任何帮助!

完整代码

(X-posted from/r/haskell)

对您来说,最简单的事情就是从堆栈中删除ErrorT。如果你看一下这里,你可以看到Happstack为StateT和ReaderT定义了FilterMonad的内置直通实例,但没有为ErrorT定义。如果你真的想在堆栈中保留ErrorT,那么你需要为它写一个passthrough实例。它可能看起来很像ReaderT。

instance (FilterMonad res m) => FilterMonad res (ReaderT r m) where
    setFilter f   = lift $ setFilter f
    composeFilter = lift . composeFilter
    getFilter     = mapReaderT getFilter

我倾向于认为你不应该在你的应用程序monad中加入ErrorT,因为你不会总是处理可能失败的计算。如果您确实需要处理代码的某个部分中的失败,那么只需将runErrorT包装在该部分上,然后根据需要使用ErrorTliftreturn,就可以轻松地处理它。此外,转换器堆栈中的额外层会在每次绑定时增加性能税。因此,虽然monad转换器的可组合性非常好,但当性能是一个重要的考虑因素时,您通常希望谨慎使用它们。

此外,我建议使用EitherT而不是ErrorT。这样你就可以利用奇妙的错误包。它有很多非常常见的便利功能,比如静音、just等。

此外,如果您想看到您正在尝试做什么的真实示例,请查看Snap的Handler monad。您的MyApp monad正是snaplets设计用来解决的问题。处理程序有一些额外的复杂性,因为它是为了以通用的方式解决问题而设计的,这样Snap用户就不需要自己构建这个通用的转换器堆栈。但是,如果您查看底层实现,您可以看到它的核心实际上只是Reader和State monad浓缩为一个。

相关内容

  • 没有找到相关文章

最新更新