gpt4 book ai didi

haskell - 在 Scotty/Haskell 中捕获异常

转载 作者:行者123 更新时间:2023-12-01 00:53:59 24 4
gpt4 key购买 nike

我刚刚开始学习 Haskell,并且一直坚持如何在 Scotty 中处理异常。

我有下面的基本功能。它获取 JSON POST,将其转换为 Haskell 数据记录,从配置读取器获取 postgres 连接池,然后将记录插入数据库。

create :: ActionT Text ConfigM ()
create = do
a :: Affiliate <- jsonData
pool <- lift $ asks pool
_ <- liftIO $ catchViolation catcher $ withResource pool $ \conn ->
PgSQL.execute conn "INSERT INTO affiliate (id, network, name, status) VALUES (?, ?, ?, ?)"
(slug a, network a, name a, status a)
let s = fromStrict $ unSlug $ slug a
text $ "Created: " `T.append` s
where
catcher e (UniqueViolation "mykey") = throw e --text "Error"
catcher e _ = throw e

此函数编译正常,但是当我将 UniqueViolation 更改为返回文本时,它无法编译。
catcher e (UniqueViolation "mykey") = text "Error"

给出的编译错误是:
Couldn't match type ‘ActionT e0 m0 ()’ with ‘IO Int64’
Expected type: PgSQL.SqlError -> ConstraintViolation -> IO Int64
Actual type: PgSQL.SqlError
-> ConstraintViolation -> ActionT e0 m0 ()
In the first argument of ‘catchViolation’, namely ‘catcher’
In the expression: catchViolation catcher

catchViolation 来自 Database.PostgreSQL.Simple.Errors并具有以下签名:
catchViolation :: (SqlError -> ConstraintViolation -> IO a) -> IO a -> IO a 

我知道问题的一部分是它从 PgSQL.execute 获取 IO Int64,但从捕手获取 ActionT 但不确定如何解决类型或更惯用的方法。

最佳答案

问题是 catchViolation 的返回值住在IO单子(monad),但是 text住在ActionT e IO monad,它是建立在 IO 之上的 monad使用 ActionT单子(monad)变压器。

Monad 转换器为它们的基本 monad 添加了额外的功能。在 ActionT 的情况下,它添加了诸如访问“构造中的响应”之类的内容(这就是 text 需要它的原因)。

一种可能的解决方案是使用 text。出 catchViolation .相反,使 catchViolation返回 Either ,然后,一旦回到 ActionT上下文,Either 上的模式匹配决定做什么。就像是:

ei <- liftIO $ catchViolation catcher $ fmap Right $ withResource pool
case ei of
Left str -> text str
Right _ -> return ()
where
catcher e (UniqueViolation "mykey") = return $ Left "some error"
catcher e _ = return $ Left "some other error"

还有另一种解决方案,更强大但不那么直观。碰巧 ActionT MonadBaseControl 的一个实例.这个类型类有 methods这让您可以将 monad 转换器添加的所有“额外层”隐藏到基本 monad 的普通值中。然后,您可以将该值传递给一些回调接受函数,例如 catchViolation ,然后“弹出”所有额外的图层。

(这有点像为了通过海关或其他什么,将一个千斤顶按回它的盒子,然后让它再次弹出。)

它会是这样的:
control $ \runInBase -> catchViolation 
(\_ _ -> runInBase $ text "some error")
(runInBase $ liftIO $ withResource $
.... all the query stuff goes here ...)

我们正在使用 control实用功能。 control为您提供了一个神奇的功能( RunInBase m b),让您“将千斤顶放回盒子里”。即构造一个 IO ActionT 中的值一。然后将该值传递给 catchViolation , 和 control负责解压缩结果中编码的层,带回完整的 ActionT单子(monad)最后。

关于haskell - 在 Scotty/Haskell 中捕获异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29474867/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com