В 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
.