学习Happstack和Monad变形金刚

时间:2013-06-27 05:02:03

标签: haskell monad-transformers happstack

所以我有一个项目,我认为这个项目很容易学习,但复杂到足以让我感兴趣的是我想用Happstack库编写。在它最基础的层面上,这个项目只是一个花哨的文件服务器,带有一些特定于域的REST方法(或者其他什么,我真的不在乎它是否真的是RESTful)来搜索和获取所述文件和元数据。既然我现在也想要真正学习monad变形金刚,我认为这将是一个完美的学习项目。但是,我遇到了一些困难,尤其是如何构建变换器堆栈。

现在,我只担心一些事情:配置,错误报告,状态和日志记录,所以我开始使用

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

因为我总是会在IO中,所以我可以很容易地使用hslogger来处理我的日志记录。但我也知道我需要使用ServerPartT来与Happstack进行交互,因此

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

我可以让它运行,查看请求等,但我遇到的问题是,为了使用像FilterMonad,{{1这样的方法,需要dir实现它}和path,但我不知道如何为这种类型实现它。我只需要它将过滤器传递给底层monad。有人可以给我一些关于如何实现这个显然至关重要的类型类的指针吗?或者,如果我只是做了一件非常错误的事情,那就引导我朝着正确的方向前进。我只看了几天哈普斯塔克,变形金刚对我来说还是一个新手。我认为我理解他们足够危险,但我对他们知之甚少,我可以自己实施。非常感谢您提供的任何帮助!

FULL CODE

(X-posted from /r/haskell

1 个答案:

答案 0 :(得分:1)

最简单的方法是从堆栈中删除ErrorT。如果查看here,您可以看到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包裹在该部分周围,然后使用ErrorTlift和{{{}} 1}}根据需要。此外,变换器堆栈中的额外层会对每个绑定添加性能税。因此,尽管monad变换器的可组合性非常好,但是当性能成为重要考虑因素时,您通常会谨慎使用它们。

另外,我建议使用EitherT而不是ErrorT。这样你就可以利用梦幻般的errors package。它有许多非常常见的便利功能,如hushjust等。

此外,如果您想查看您尝试执行的操作的真实示例,请查看Snap的Handler monad。您的MyApp monad正是snaplet旨在解决的问题。 Handler有一些额外的复杂性,因为它旨在以一种通用的方式解决问题,因此Snap用户不需要自己构建这个公共变换器堆栈。但是,如果你看一下underlying implementation,你可以看到它的核心实际上只是读者和状态monad凝聚成一个。