【问题标题】:How to stack correctly monads to finally render to html?如何正确堆叠单子以最终呈现为 html?
【发布时间】:2019-05-30 15:06:02
【问题描述】:

我的目标是使用HtmlT m 类型(最好是Html ())正确地提供html 函数(如Spock-core 中定义的那样)。在这之间,我正在执行一些 Network.Http.Simple 请求。显然我对Haskell的了解不够,我没有找到强制正确单子的方法。据我所知(并且理解 monad 的全部含义),没有办法像 (Monad M, Monad N => M a -> (a -> N b) -> N b) 这样组合不同的 monad。

我设法实现的最好的方法是设置一个HtmlT IO () 类型,但后来我陷入了转换函数lucid :: HtmlT IO () -> SpockAction dtb sess state ()

这是我的连接函数(Auth 是一个 FromJSON 数据结构,用于托管授权密钥和令牌)

     connect :: IO Auth
     connect = do  
                ...building a http request.. 
                response <- httpJSON request
                return (getResponseBody response :: Auth)

接下来,它被连接到 String -&gt; HtmlT IO () 类型的 getRequest 函数中

    getRequest :: RequestPath -> HtmlT IO ()
    getRequest rpath = do
                    atoken <- liftIO connect
                    request' <- liftIO parseRequest "http://......"
                    let request = { series of set methods
                                    to build the request }
                    response <- httpLBS request
                    liftIO (L8.putStrnLn $ (getResponseBody response))

这里我们来到了 lucid 函数,Lucid 可以处理转换Html () -&gt; SpockAction ...Html () 只不过是 HtmlT Identity (),所以我的第一次尝试是用 HtmlT IO () 喂 Lucid。

   lucid :: HtmlT IO () -> SpockAction database session state ()
   lucid document = do 
                        txt <- renderTextT document       --> inside IO (?)
                        return html (TL.toStrict txt)     <-- naive attempt to 
                                      return to "somewhere" of course stupid..

也许IO 不是这里的好单子?当我尝试 Identity monad(拥有HtmlT Identity ())时,如果我将 connect 定义为 connect :: Identity Auth,那么自然会要求我提供 Identity 的 FromJSON 实例(源于使用 httpJSON),也许这曾经是一个潜在的解决方案我在 Identity monad 中,我能够将事情连接起来,并可能以干净的 Html () 类型结束,然后我的 lucid 函数将顺利执行。

感谢您提供任何线索或提示,也许我的方法完全错误我正在做的整个事情是查询一个 restAPI 网站并在使用 Spock 运行的网络服务器上查看结果。

【问题讨论】:

  • 请包含具体的编译器错误,准确复制。另外,这是您的确切实际来源吗? getRequest 应该无法编译,因为 liftIO parseRequest "http://......"liftIO 传递了太多参数,但您似乎在编写该函数是正确/完整的。
  • 在我的实际代码中 lucid is undefined ``` lucid= undefined ``` 其他一切编译正确。 getRequest compile 不要在这里阅读我的定义,我跳过了负责构建请求的部分,该函数的最后一次调用是来自 putStrLn 的 IO 提升结果(即 IO () )
  • 我刚刚修复了你的标签。我猜你不想要 spock 因为它指的是用 Groovy 编写的测试框架。我用 haskell-spock 替换了它,希望它指的是您正在谈论的 Web 开发框架。

标签: html haskell haskell-spock haskell-lucid


【解决方案1】:

相关类型:

document :: HtmlT IO ()
renderTextT :: Monad m => HtmlT m a -> m Text
renderTextT document :: IO Text
<the value you need> :: SpockAction database session state Text

幸运的是,我们有这个:liftIO :: MonadIO m =&gt; IO a -&gt; m a

lucid 函数运行的两个步骤:

  1. txt &lt;- renderTextT document 更改为txt &lt;- liftIO (renderTextT document)
  2. 尝试编译您的代码以确保liftIO 已导入(可能已经导入)。如果没有:将import Control.Monad.IO.Class 添加到您的导入列表中。
  3. return html (TL.toStrict txt) 中删除return

以下代码在我的机器上运行:

lucid :: HtmlT IO () -> SpockAction database session state ()
lucid document = do
  txt <- liftIO (renderTextT document)
  html (TL.toStrict txt)

【讨论】:

  • html 是一个从 Text 到 ActionCtxt ctx m a 的“转换器”,它实际上被包裹在 SpockAction 类型下面的某个地方( SpockAction 是一个 SpockActionCTxt 包装器,它本身就是一个 ActionCTxt 类型的包装器)。我认为,如果我按照您建议 Jeremy List 的方式进行操作,我需要将一层 ActionCTxt 展平。 LiftIO 工作正常,我得到的错误是:Could't match type `ActionCtxT ctx0 m0 a0' with () Expected type: ActionCtxT () (WebStateM database session state) () Actual type: ActionCtxT () (WebStateM database session state) (ActionCtxT ctx0 m0 a0)
  • 剩下的问题是你使用return;这是通过instance Monad ((-&gt;) r) 声明实例化的。
  • 完美。我以前一定做错了什么,你建议实际上编译正确。非常感谢杰里米。尽管如此,在错误中徘徊这么久是一个很好的练习,我终于对单子变换器有了更深入的了解。我仍然对我的代码中的 IO monad 不是很满意,我觉得还有另一种方法。正如我所见,一切都来自使用 parseRequest 将我拉入一个单子(字符串 - > m 请求) - 我让 m 成为一个 IO,一旦你在 IO 单子中,你就无法离开......这是“IO Monad的诅咒”(“runIO”是主要功能)
猜你喜欢
  • 2018-04-18
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 2014-02-12
  • 2012-03-05
  • 2011-09-01
  • 2023-04-08
相关资源
最近更新 更多