1

我是 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 -> String函数

  3. 将结果作为响应返回

我读过什么并尝试过什么

我已经阅读了 Scotty文档和一些代码示例

我读到paramis of typeParsable a => Text -> ActionM a并且ActionMis of type ActionT Text IO

我试过name :: T.Text <- param "name", T.unpack,liftIO等,但没有运气。我想我不完全理解这些类型。

问题

这些类型的用途paramActionM实际含义是什么?

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

谢谢你。

4

1 回答 1

2

首先,一些工作代码:

{-# 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不在ActionMmonad 中,let因此可以使用绑定来代替<-语法。

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

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

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

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

所需的其他更改是替换concatmconcat和。这些函数与和完成相同的事情,但是更通用并且适用于所有幺半群而不仅仅是列表。++<>concat++

你最后一个关于类型ActionM意味着什么的问题。

在引擎盖下,ActionM是 的一种特殊形式ActionTActionM = ActionT Text IO

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

newtype ActionT e m a = ActionT 
  { runAM :: ExceptT (ActionError e) (ReaderT ActionEnv (StateT ScottyResponse m)) a }
于 2018-08-25T20:55:18.163 回答