如何使用scotty从sqlite和响应json获取数据?

时间:2018-06-27 21:46:25

标签: json haskell functional-programming scotty

我正在尝试使用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之后使用堆栈构建即可运行。在建筑物中,我已经收到此错误。

1 个答案:

答案 0 :(得分:1)

在Haskell中,所有函数都是纯函数,因此selectPosts之类的东西需要出去并与数据库进行IO通讯,不能仅仅这样做并从数据库返回值。而是,这些类型的函数返回类型为IO a的东西,您可以将其视为对如何执行和执行IO以获取类型为a的值的描述。这些“ IO操作”可以组合在一起,并且可以将其中之一分配给main;在运行时,RTS将执行这些IO操作。

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

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

ActionM

旁注:您可能已经注意到我写了get "/posts" $ do posts <- liftIO $ withTestConnection S.selectPosts json posts 而不是withTestConnection S.selectPosts。通常,如果您有一个withTestConnection $ \conn -> do S.selectPosts conn块,其中只有一个表达式(不是do的形式),则与x <- act块之外的单个表达式相同:{{ 1}}。此外,Haskell倾向于鼓励部分应用:您拥有do,这是一个函数\conn -> S.selectPosts connS.selectPosts是同一类型的另一个函数,该函数将连接传递到Sql.Connection -> IO [M.Post],然后返回与\conn -> S.selectPosts conn相同的结果-该函数与selectPosts本身是无法区分的!因此,如果这是selectPosts中所需的全部内容,那么您应该能够将整个lambda和selectPosts块简化为withTestConnection