【问题标题】:How to convert Scotty parameter as String如何将 Scotty 参数转换为字符串
【发布时间】:2023-09-10 08:52:01
【问题描述】:

我是 Haskell 的新手,正在使用 Scotty 网络库测试一些概念。

但是,我无法让简单的 hello world 页面正常工作。 我坚持将参数转换为字符串并应用于另一个函数。

这是尚未运行的高级代码。

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Web.Scotty

main :: IO ()
main = scotty 3000 $
  get "/" $ do
    name <- param "name" `rescue` (\_ -> return "haskell")
    greeting <- hello name
    html $ concat ["<h1>hello ", greeting, "</h1>"]

hello :: String -> String
hello s = "hello " ++ s

错误信息

app/Main.hs:11:17: error:
    • Couldn't match type ‘[]’
                     with ‘Web.Scotty.Internal.Types.ActionT
                             Data.Text.Internal.Lazy.Text IO’
      Expected type: Web.Scotty.Internal.Types.ActionT
                       Data.Text.Internal.Lazy.Text IO Char
        Actual type: String
<Omitted>
   |
11 |     greeting <- hello name
   |                 ^^^^^^^^^^

app/Main.hs:12:12: error:
    • Couldn't match expected type ‘Data.Text.Internal.Lazy.Text’
                  with actual type ‘[Char]’
<Omitted>
   |
12 |     html $ concat ["<h1>hello ", greeting, "</h1>"]
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

app/Main.hs:12:34: error:
    • Couldn't match expected type ‘[Char]’ with actual type ‘Char’
<Omitted>
   |
12 |     html $ concat ["<h1>hello ", greeting, "</h1>"]
   |                                  ^^^^^^^^

目标

hello 函数是一个存根。我想证明以下机制有效。

  1. 将参数提取为字符串

  2. 应用于String -&gt; String函数

  3. 将结果作为响应返回

我阅读和尝试了什么

我读过 Scotty doc 和一些 code examples

我读到paramParsable a =&gt; Text -&gt; ActionM a 类型,ActionMActionT Text IO 类型。

我尝试过name :: T.Text &lt;- param "name"T.unpackliftIO 等,但没有运气。我想我不完全了解这些类型。

问题

paramActionM 的类型实际上是什么意思?

如何将参数提取为字符串以与其他函数一起使用?

谢谢。

【问题讨论】:

  • 您的错误信息是什么?专注于这些是达成解决方案的好方法。
  • 另外,当您说“这些类型实际上是什么意思?”时,您想解释哪些类型?我已经确定了几个。
  • @MichaelLitchard 嗨,迈克尔,我已经更新了错误消息。我的意思是paramActionM 的类型。谢谢!

标签: haskell scotty


【解决方案1】:

首先,一些工作代码:

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Data.Text.Lazy (Text)
import Web.Scotty

main :: IO ()
main = scotty 3000 $
  get "/" $ do
    name <- param "name" `rescue` (\_ -> return "haskell")
    let greeting = hello name
    html $ mconcat ["<h1>hello ", greeting, "</h1>"]

hello :: Text -> Text
hello s = "hello " <> s

由于hello 不在ActionM monad 中,所以可以使用let 绑定来代替&lt;- 语法。

param 可用于解析任何属于Parseable 类型类的查询参数。

param :: Parsable a =&gt; Text -&gt; ActionM a 表示给定参数的文本名称,param 可以返回您需要的任何类型,只要它是Parseable。检查the docs 以获取可用类型的列表。请注意,String 不在该列表中,但 Text 在。这就是为什么在上面的代码中我将hello 函数更改为使用Text 而不是String。如果你更喜欢使用String,你可以像这样解压解析的参数:

name <- T.unpack <$> param "name" `rescue` (\_ -> return "haskell")
let greeting = hello name -- where `hello :: String -> String`

(但是在使用html函数之前,您需要将结果重新打包成文本)

所需的其他更改是将concat 替换为mconcat++ 替换为&lt;&gt;。这些函数与concat++ 完成相同的事情,但是更通用,并且适用于所有幺半群而不仅仅是列表。

关于ActionM 的类型是什么的最后一个问题。

在底层,ActionMActionT 的一种特殊形式:ActionM = ActionT Text IO

ActionT 表示发生在环境中的计算(http 请求),可以修改内部状态(http 响应),并可能导致错误。它是使用一堆 monad 转换器制成的,如下所示:

newtype ActionT e m a = ActionT 
  { runAM :: ExceptT (ActionError e) (ReaderT ActionEnv (StateT ScottyResponse m)) a }

【讨论】: