【问题标题】:How to get data from sqlite and response json using scotty?如何使用 scotty 从 sqlite 和响应 json 获取数据?
【发布时间】:2018-06-27 21:46:25
【问题描述】:

我正在尝试使用 Haskell 和 Framework Scotty 构建一个简单的博客。使用 Model.hs 我有:

data Post = Post
    { id :: Int
    , tipo :: String
    , titulo :: String
    , conteudo :: String
    } deriving (Show, Generic)

我已经使用 sqlite 创建了一个架构并填充了一些数据,现在我正在尝试在我的 Storage.hs 中使用此方法获取这些数据

selectPosts :: Sql.Connection -> IO [M.Post]
selectPosts conn =
    Sql.query_ conn "select * from post" :: IO [M.Post]

我的意图是在 Main.hs 中获取 json 格式的数据:

instance ToJSON M.Post
instance FromJSON M.Post

main :: IO ()
main = do
    putStrLn "Starting Server..."
    scotty 3000 $ do
        get "/" $ file "templates/index.html"
        get "/posts" $ do
            json posts where
            posts = withTestConnection $ \conn -> do
                S.selectPosts conn

但是我收到了一个 IO [Model Post],但我不知道如何将它呈现为 json,所以它一直收到这个错误:

No instance for (ToJSON (IO [Post])) arising from a use of ‘json’

我的项目在 github 中运行,只需使用堆栈构建和堆栈 ghci 之后。在建筑物中,我已经收到此错误。

【问题讨论】:

    标签: json haskell functional-programming scotty


    【解决方案1】:

    在 Haskell 中,所有函数都是纯函数——所以像 selectPosts 这样的东西,它需要出去做 IO 来与数据库对话,不能只是这样做并从数据库中返回值。相反,这些类型的函数返回IO a 类型的东西,你可以认为它基本上描述了如何出去执行IO 以获得a 类型的值。这些“IO动作”可以组合在一起,其中一个可以分配给main;在运行时,RTS 会执行这些 IO 动作。

    但是,您不会将从selectPosts 返回的IO a 值组合成更大的IO 值的一部分,最终变为main;您正试图通过将其输入json 来直接使用它。这是行不通的,因为没有(好的/简单的)方法可以将如何执行 IO 的描述转换为 JSON 字符串。

    Haskell 处理组合这些值的方式是通过称为“monad”的抽象,这在许多其他情况下也很有用。 do 符号可用于以非常自然的风格编写此单子序列。您不能在这里只写posts <- withTestConnection S.selectPosts,因为Scotty 的get 函数采用一元ActionM 类型的值,而不是IO。然而,事实证明ActionM 基本上是一堆其他有用的东西,叠加在IO 之上,所以应该可以将 IO 动作从selectPosts“提升”到 Scotty 的 ActionM monad:

    get "/posts" $ do
      posts <- liftIO $ withTestConnection S.selectPosts
      json posts
    

    旁注:您可能已经注意到我写的是withTestConnection S.selectPosts 而不是withTestConnection $ \conn -&gt; do S.selectPosts conn。通常,如果您有一个只有一个表达式的do 块(不是x &lt;- act 的形式),这与do 块之外的单个表达式相同:\conn -&gt; S.selectPosts conn。此外,Haskell 倾向于鼓励部分应用:你有S.selectPosts,这是一个函数Sql.Connection -&gt; IO [M.Post]\conn -&gt; S.selectPosts conn 是另一个相同类型的函数,它将连接传递给selectPosts,然后返回与selectPosts 相同的结果---这个函数与selectPosts 本身没有区别!因此,如果这就是您在 withTestConnection 中所需要的全部内容,您应该能够将整个 lambda 和 do 块简化​​为 S.selectPosts

    【讨论】:

    • 非常感谢您的教学解释。我还在学习haskell,但我必须从列表、递归和其他基本知识开始这个项目。就在现在,我真的在学习 Monads 是如何工作的。此外,也感谢您的提示。
    猜你喜欢
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 2019-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多