Exception Haskell中的纯异常

Exception Haskell中的纯异常,exception,haskell,monads,Exception,Haskell,Monads,如何在Haskell中使用异常而不经过IO 我有以下代码,用于在二元搜索树中插入一个元素,当该元素是树的成员时,比较最少,并且不进行复制。我注意到或用作捕获或左用作抛出: insert x t = either (const t) id (insert' x t Nothing) where insert' x E m = maybe (Right (T E x E)) (\v -> if x==v then Left E else Right (T E x E)) m

如何在Haskell中使用异常而不经过
IO

我有以下代码,用于在二元搜索树中插入一个元素,当该元素是树的成员时,比较最少,并且不进行复制。我注意到
用作
捕获
用作
抛出

insert x t = either (const t) id (insert' x t Nothing)
    where
    insert' x E m = maybe (Right (T E x E)) (\v -> if x==v then Left E else Right (T E x E)) m
    insert' x t@(T l v r) m = if x<v
                                 then fmap (\l' -> T l' v r) (insert' x l Nothing)
                                 else fmap (\r' -> T l v r') (insert' x r (Just v))
insert x t=one(const t)id(insert'x t Nothing)
哪里
插入'xem=maybe(Right(texe))(\v->如果x==v,则左E,否则右(texe))m
插入'x t@(t l v r)m=如果x t l v r')(插入'x r(仅v))

因此,我试图用
Control.Monad.Error
重写它,希望使代码更简单,但我弄得一团糟。有什么建议吗?

这取决于你想要什么例外

如果您试图从函数返回一个错误值(例如“找不到键”或“键已存在”),那么您应该使用以下内容。“Left”通常用于误差值,因为“Right”是正确的结果。这里使用的错误monad与“Maybe”monad的使用方式相同:当发生错误时,其余的计算将无法完成,而无需将大量的“if-then-else-if-then…”链接在一起。在这种情况下,“例外”并不是真正的例外;您的代码要么处理它,要么以某种方式将其传递到下一个级别

另一方面,您可能还希望捕捉不可预见的异常,例如“head[]”,您认为某些事情永远不会发生,但您错了。因为这些异常是不可预测的,可能是不确定的,并且通常不适合类型系统,所以它们必须被视为IO事件。通常的模式是忽略这些异常,但程序的最高层除外,您可以尝试保存用户的工作,并显示一条有用的消息,如“请报告此错误”

抛出后一种异常很容易:只需调用“error”。但只在你真正相信不会发生的事情上使用它;它永远不应该是代码的正常部分。

Hackage上的包有一个Exception monad(和Exception Ont monad transformer),您可以在没有IO的情况下使用它。当你运行它时,你会得到一个任意类型的结果。

棘手! 这是一个非常好的机制,可以在最后一刻将值与(=)进行比较,并且只需要iff。Byt为什么不至少用类型信息对其进行注释

data Tree a = E | T (Tree a) a (Tree a)

insert :: (Ord a) => a -> Tree a -> Tree a
insert x t = const t `either` id $ insert' x t Nothing
    where
    -- insert' (insert_this) (into_this_empty_tree) (except_if_it_equals_this) (because_then_the_tree_is_Left_unchanged)
    insert' :: (Ord a) => a -> Tree a -> Maybe a -> Either (Tree a) (Tree a)
    insert' x E Nothing = Right (T E x E)
    insert' x E (Just v) | x==v      = Left E
                         | otherwise = Right (T E x E)
    -- insert' (insert_this) (into_this_nonempty_tree) ((anyway)) (recursive:if_it_branches_to_the_left_insert_it_there)
    -- insert' (insert_this) (into_this_nonempty_tree) ((anyway)) (recursive:if_it_equals_or_branches_to_the_right_insert_it_there_except_if_the_right_branch_is_empty)
    insert' x t@(T l v r) _ | x<v       = (\l' -> T l' v r) `fmap` insert' x l Nothing
                            | otherwise = (\r' -> T l v r') `fmap` insert' x r (Just v)
然后。。。如果你想真正高效,不要构建那个(可能是一个)参数只是为了事后比较

--insert'1 :: (Ord a) => a -> Tree a -> Nothin -> Maybe (Tree a)
--insert'2 :: (Ord a) => a -> Tree a -> Just a -> Maybe (Tree a)
insert'1 :: (Ord a) => a -> Tree a -> Maybe (Tree a)
insert'2 :: (Ord a) => a -> Tree a -> a -> Maybe (Tree a)
解决方案如下所示:

insert :: (Ord a) => a -> Tree a -> Tree a
insert x t = fromMaybe t $ insert'1 x t
    where
    insert'1 :: (Ord a) => a -> Tree a -> Maybe (Tree a)
    insert'2 :: (Ord a) => a -> Tree a -> a -> Maybe (Tree a)
    insert'1 x E = Just (T E x E)
    insert'1 x (T l v r) | x<v       = do l' <- insert'1 x l
                                          Just (T l' v r)
                         | otherwise = do r' <- insert'2 x r
                                          Just (T l v r')
    insert'2 x E v = guard (x/=v) >> Just (T E x E)
    insert'2 x t _ = insert'1 x t
这意味着,这(或字符串)可能是您正在寻找的

insert :: (Ord a,MonadError String m) => a -> Tree a -> m (Tree a)
insert x t = maybe (throwError "Error: element already in tree") return $ insert'1 x t
    where ...

还可以看看现实世界哈斯克尔书中的章节。挑剔:如果不对错误案例使用
Left
,则不能使
成为函子、单子等,这是更重要的;这不仅仅是正确的两种含义。@Antal s-Z:就我个人而言,我认为这只是因为。实际上,我从
insert'1
insert'2
insert'
的方向正好相反。你完全正确,我可以使用
也许
作为
插入'
的结果。但是接下来的问题是如何用
抛出
替换
,或者用
捕获
替换
如果它等于或分支到右分支插入,除了如果右分支为空,这是我见过的最长的标识符。
Error e => MonadError e (Either e)
insert :: (Ord a,MonadError String m) => a -> Tree a -> m (Tree a)
insert x t = maybe (throwError "Error: element already in tree") return $ insert'1 x t
    where ...