Как получить данные из sqlite и ответ JSON с помощью Скотти? - PullRequest
0 голосов
/ 28 июня 2018

Я пытаюсь создать простой блог, используя Haskell и Framework Scotty. Используя Model.hs у меня есть:

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

Я уже создал схему с использованием sqlite и заполнил ее некоторыми данными, сейчас я пытаюсь получить эти данные с помощью этого метода в моем хранилище. Hs

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

Мое намерение - получить формат данных как json в моем файле Main.hs:

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 голосов
/ 05 июля 2018

В Haskell все функции чистые - поэтому что-то вроде selectPosts, который должен выйти и выполнить IO для связи с базой данных, не может просто сделать это и вернуть значение из базы данных. Вместо этого, эти виды функций возвращают что-то типа IO a, которое вы можете рассматривать как описание того, как выйти и выполнить IO, чтобы получить значение типа a. Эти «действия ввода-вывода» могут быть составлены вместе, и одно из них может быть назначено на main; во время выполнения RTS выполнит эти действия ввода-вывода.

Однако вы не составляете значение IO a, которое вы возвращаете из selectPosts, чтобы быть частью большего значения IO, которое в конечном итоге становится main; Вы пытаетесь использовать его напрямую, вводя его в json. Это не сработает, потому что нет (хорошо / просто) способа преобразовать описание того, как сделать IO, в строку JSON.

Способ, которым Haskell имеет дело с составлением этих значений, заключается в абстракции, известной как «монада», которая также полезна во многих других случаях. do нотация может использоваться для написания этой монадической последовательности в очень естественном стиле. Вы не можете просто написать posts <- withTestConnection S.selectPosts здесь, потому что функция Скотти get принимает значение монадического типа ActionM, а не IO. Однако оказывается, что ActionM - это, по сути, куча других полезных вещей, наложенных поверх IO, поэтому должна быть возможность «поднять» действие ввода-вывода с selectPosts в монаду Скотти ActionM:

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

Примечание: возможно, вы заметили, что я написал withTestConnection S.selectPosts вместо withTestConnection $ \conn -> do S.selectPosts conn. Обычно, если у вас есть блок do с одним выражением (не в форме x <- act), это то же самое, что и одно выражение вне блока do: \conn -> S.selectPosts conn. Кроме того, Haskell имеет тенденцию поощрять частичное применение: у вас есть S.selectPosts, который является функцией Sql.Connection -> IO [M.Post]. \conn -> S.selectPosts conn - это другая функция того же типа, которая передает соединение в selectPosts и затем возвращает тот же результат, что и selectPosts --- эта функция неотличима от самой selectPosts! Так что, если это все, что вам нужно внутри withTestConnection, вы сможете упростить всю лямбду и блок do до S.selectPosts.

...