Haskell 如何处理链锁?

Haskell 如何处理链锁?,haskell,Haskell,我有一个关于chainedControl.Monad.Except的问题。为了使问题更清楚,请考虑下面的例子: 首先,我们将误差定义如下: import Control.Monad.Except import Prelude hiding (lex) data BufError = BufClosedError deriving Show data LexError = NoSuchTokenError deriving Show data ParseError = M

我有一个关于chained
Control.Monad.Except
的问题。为了使问题更清楚,请考虑下面的例子:

首先,我们将误差定义如下:

import Control.Monad.Except
import Prelude hiding (lex)

data BufError   = BufClosedError      deriving Show
data LexError   = NoSuchTokenError    deriving Show
data ParseError = MismatchSymbolError deriving Show
buferor
来自读取缓冲区

readBuf::Except BufError a
readBuf = throwError BufClosedError
当它发生时,这种错误嵌入在
lex

lex::ExceptT LexError (Except BufError) a
lex = lift $ readBuf
解析器可以处理任何源,并将源中发生的错误提升到
中,但ParseError m a除外:

parse::Monad m=>m a->ExceptT ParseError m a
parse source = lift source
现在,当使用不同的源运行解析器时,它需要不同的处理程序从
ExceptT
as中提取结果

handleError3 = runExceptT . runExceptT . runExceptT
handleError2 = runExceptT . runExceptT

runParse3 = handleError3 $ parse lex
runParse2 = handleError2 $ parse readBuf
问题是如何将
handleError3
handleerr2
组合成一个通用函数
handleerror
,这样我们就可以用单个函数处理
parse lex
parse readBuf

runParse = handlerError . parse

runParse lex
runParse readBuf
此外,如果使用“可变长度”链接的
除外,例如

是否可以定义一个函数来处理它

handler = runExceptT . runExceptT ... . runExceptT

与每个
ExceptT
对应的
runExceptT
的正确数量?

实用答案

首先,我们将误差定义如下:

import Control.Monad.Except
import Prelude hiding (lex)

data BufError   = BufClosedError      deriving Show
data LexError   = NoSuchTokenError    deriving Show
data ParseError = MismatchSymbolError deriving Show
你的起步很差。如果您想简单地捕获所有错误并拥有一组闭合的错误构造函数,则使用单一类型:

data Error = BufClosedError | NoSuchTokenError | MismatchSymbolError
    deriving (Show)
通过这种方式,您可以为catch例程使用单个monad堆栈和单个类型:

readBuf::Except Error a
readBuf = throwError BufClosedError

lex::Except Error a
lex = readBuf

handleError3 = runExcept
handleError2 = runExcept
... etc...
学术答案

如果没有一组封闭的错误类型,或者不希望创建包含所有错误的单一类型,则可以从中借用。在那篇文章中,我们看到了如何定义数据声明的固定点,以便对它们进行扩展。读报纸。努力克服它。作为挑逗者,请考虑:

首先,我们需要一个固定的数据点。这通常被称为
data Fix=In…
,但对于您的使用,我们可能应该称之为
Error

data Error f = Error (f (Error f))
这是接下来一切的核心,不要置之不理。我们在这里的功能是使用系统其余部分的任何错误值参数化错误。我们现在应该将错误定义为接受此
f
参数的数据类型,而不是问题中所示的计划单例:

data BufErrorTy f = BufCloseError deriving (Show)
data LexErrorTy f = LexErrorTy deriving (Show)
data ParseErrorTy f = ParseErrorTy deriving (Show)
现在,我们可以添加多态类型,允许我们在任何错误堆栈的上下文中讨论缓冲区、lexer和解析错误:

bufCloseError :: (BufErrorTy :<: e) => Error e
bufCloseError = inject BufCloseError
lexError :: (LexErrorTy :<: e) => Error e
lexError = inject LexErrorTy
parseError :: (ParseErrorTy :<: e) => Error e
parseError = inject ParseErrorTy
很酷,但是我们如何构建允许的错误集呢

data (f :+: g) e = Inl (f e) | Inr (g e)
这很像
或者
,除了有一个额外的
e
参数。我们有一组错误,其中实际错误是左错误(
fe
)或右错误(
ge

仍然需要说明我们如何在该堆栈上注入和恢复错误,因此让我们编写这些实例:

instance f :<: (f :+: g) where
    inj = Inl
    rec (Inl f) = Just f
    rec _ = Nothing
instance (f :<: g) => f :<: (h :+: g) where
    inj = Inr . inj
    rec (Inr hg) = rec hg
    rec _ = Nothing
当然,我们可以在此基础上继续发展,比如抛出不同的错误,以及上下文抛出的大量可能错误。请注意,只要我们抛出的错误是
中列出的类型之一,所有类型都会进行检查,但e
可能出现的错误集除外:

parse2 :: Except (Error (ParseErrorTy :+: LexErrorTy :+: BufErrorTy :+: FooTy)) a
parse2 = throwError parseError
通过恢复所需错误并忽略其他类型的错误,可以使用仅捕获所需准确错误的函数捕获这些错误:

catchSome ::
    (s :<: t)
     => Except (Error t) a
     -> (s (Error t) -> Except (s (Error t)) a)
     -> Except (Error t) a
catchSome op c =
    catchError op (\orig ->
        case recover orig of
            Just e -> withExcept inject (c e)
            Nothing -> throwError orig)
完整代码:

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE UndecidableInstances #-}
import Control.Monad.Except

data (f :+: g) e = Inl (f e) | Inr (g e)

infixr 6 :+:

class (sub :<: sup) where
    inj :: sub a -> sup a
    rec :: sup a -> Maybe (sub a)

instance f :<: f where
    inj = id
    rec = Just . id

instance f :<: (f :+: g) where
    inj = Inl
    rec (Inl f) = Just f
    rec _ = Nothing

instance (f :<: g) => f :<: (h :+: g) where
    inj = Inr . inj
    rec (Inr hg) = rec hg
    rec _ = Nothing

inject :: (s :<: t) => s (Error t) -> Error t
inject = Error . inj

recover :: (s :<: t) => Error t -> Maybe (s (Error t))
recover (Error x) =
    case rec x of
        Just e -> Just e
        Nothing -> Nothing

data FooTy f = FooTy deriving (Show)
data BufErrorTy f = BufCloseError deriving (Show)
data LexErrorTy f = LexErrorTy deriving (Show)
data ParseErrorTy f = ParseErrorTy deriving (Show)

data Error f = Error (f (Error f))

bufCloseError :: (BufErrorTy :<: e) => Error e
bufCloseError = inject BufCloseError
lexError :: (LexErrorTy :<: e) => Error e
lexError = inject LexErrorTy
parseError :: (ParseErrorTy :<: e) => Error e
parseError = inject ParseErrorTy

fooError :: (FooTy :<: e) => Error e
fooError = inject FooTy

readBuf :: (BufErrorTy :<: e) => Except (Error e) a
readBuf = throwError bufCloseError

lex :: Except (Error (LexErrorTy :+: BufErrorTy)) a
lex = readBuf

parse1 :: Except (Error (ParseErrorTy :+: LexErrorTy :+: BufErrorTy)) a
parse1 = throwError bufCloseError

parse2 :: Except (Error (ParseErrorTy :+: LexErrorTy :+: BufErrorTy :+: FooTy)) a
parse2 = throwError parseError

foo :: Except (Error (LexErrorTy :+: BufErrorTy :+: FooTy)) a
foo = throwError fooError

catchSome ::
    (s :<: t)
    => Except (Error t) a
    -> (s (Error t) -> Except (s (Error t)) a)
    -> Except (Error t) a
catchSome op c =
    catchError
        op
        (\orig ->
            case recover orig of
                Just e -> withExcept inject (c e)
                Nothing -> throwError orig)

main :: IO ()
main = do
    let onlyBufError :: BufErrorTy s -> Except t ()
        onlyBufError BufCloseError = pure ()
    let nestedBufError :: BufErrorTy :<: t => BufErrorTy (Error t) -> Except (BufErrorTy (Error t)) ()
        nestedBufError BufCloseError = pure ()
    let nestedParseError :: ParseErrorTy :<: t => ParseErrorTy (Error t) -> Except (ParseErrorTy (Error t)) ()
        nestedParseError _ = pure ()
    case runExcept $ catchSome Main.lex onlyBufError of
        Left _e -> putStrLn "Uncaught (failure)"
        Right () -> putStrLn "Caught (success)"
    case runExcept $ catchSome Main.parse2 onlyBufError of
        Left _e -> putStrLn "Uncaught (success)"
        Right () -> putStrLn "Caught (failure)"
    case runExcept $ catchSome Main.parse1 nestedBufError of
        Left _e -> putStrLn "Uncaught (failure)"
        Right () -> putStrLn "Caught (success)"
    case runExcept $ catchSome Main.parse2 nestedParseError of
        Left _e -> putStrLn "Uncaught (failure)"
        Right () -> putStrLn "Caught (success)"
    case runExcept $ catchSome Main.foo nestedBufError of
        Left _e -> putStrLn "Uncaught (success)"
        Right () -> putStrLn "Caught (failure)"
{-#语言类型运算符#-}
{-#语言灵活实例}
{-#语言灵活语境#-}
{-#语言MultiParamTypeClasses}
{-#语言不连贯}
{-#语言不可判定实例}
进口管制。单子除外
数据(f:+:g)e=Inl(f e)| Inr(g e)
infixr 6:+:
类别(子类别:sup a
记录::辅助a->可能(辅助a)
例f:只是e
无->无
数据FooTy f=FooTy推导(显示)
数据BUF=BufCloseError派生(显示)
数据LexErrorTy f=LexErrorTy派生(显示)
数据ParseErrorTy f=ParseErrorTy派生(显示)
数据错误f=错误(f(错误f))
bufCloseError::(BufErrorTy:错误e
bufCloseError=注入bufCloseError
lexError::(LexErrorTy:Error e
lexError=注入LexErrorTy
parseError::(ParseErrorTy:错误e
parseError=注入ParseErrorTy
fooError::(FooTy:错误e
foorerror=注入FooTy
readBuf::(BufErrorTy:(错误e)a除外
readBuf=投掷器或BUFCLOSE错误
lex::除了(Error(LexErrorTy:+:BufErrorTy))a
lex=readBuf
parse1::除了(Error(ParseErrorTy:+:LexErrorTy:+:BufErrorTy))a
parse1=投掷者错误bufCloseError
parse2::除了(错误(ParseErrorTy:+:LexErrorTy:+:BufErrorTy:+:FooTy))a
parse2=投掷者或parseError
foo::除了(Error(LexErrorTy:+:buferrty:+:FooTy))a
foo=投掷者或foo错误
吸引人的::
(s:除(错误t)a外
->(s(错误t)->(s(错误t))a除外
->除了(错误t)a
捕手c=
捕捉错误
op
(\orig->
原发病例
Just e->withExcept inject(c e)
无->投掷者(原版)
main::IO()
main=do
只让BufError::BufErrorTy s->除了t()
onlyBufError BufCloseError=pure()
让nestedBuferor::BufErrorTy:BufErrorTy(错误t)->除了(BufErrorTy(错误t))()
nestedBufError BufCloseError=纯()
让nestedParseError::ParseErrorTy:ParseErrorTy(错误t)->除了(ParseErrorTy(错误t))()
nestedParseError=纯()
case runExcept$catchSome Main.lex only的bufferor
左e->putStrLn“未捕获(失败)”
右()->putStrLn“捕获(成功)”
case runExcept$catchSome Main.parse2 only buffer of
左_e->putStrLn“未捕获(成功)”
右()->putStrLn“捕获(失败)”
除$catchSome Main.parse1 nestedbuferor之外的案例运行
左e->putStrLn“未捕获(失败)”
右()->putStrLn“捕获(成功)”
除$catchSome Main.parse2 nestedParseError的
左e->putStrLn“未捕获(失败)”
右()->putStrLn“捕获(成功)”
除$catchSome Main.foo之外的case run
parse2 :: Except (Error (ParseErrorTy :+: LexErrorTy :+: BufErrorTy :+: FooTy)) a
parse2 = throwError parseError
catchSome ::
    (s :<: t)
     => Except (Error t) a
     -> (s (Error t) -> Except (s (Error t)) a)
     -> Except (Error t) a
catchSome op c =
    catchError op (\orig ->
        case recover orig of
            Just e -> withExcept inject (c e)
            Nothing -> throwError orig)
> case runExcept $ \
    catchSome (Main.lex >> pure "no error") (\BufCloseError -> pure "caught") of {
      Right r -> print r ;
      Left e -> print "Uncaught error }
"caught"
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE UndecidableInstances #-}
import Control.Monad.Except

data (f :+: g) e = Inl (f e) | Inr (g e)

infixr 6 :+:

class (sub :<: sup) where
    inj :: sub a -> sup a
    rec :: sup a -> Maybe (sub a)

instance f :<: f where
    inj = id
    rec = Just . id

instance f :<: (f :+: g) where
    inj = Inl
    rec (Inl f) = Just f
    rec _ = Nothing

instance (f :<: g) => f :<: (h :+: g) where
    inj = Inr . inj
    rec (Inr hg) = rec hg
    rec _ = Nothing

inject :: (s :<: t) => s (Error t) -> Error t
inject = Error . inj

recover :: (s :<: t) => Error t -> Maybe (s (Error t))
recover (Error x) =
    case rec x of
        Just e -> Just e
        Nothing -> Nothing

data FooTy f = FooTy deriving (Show)
data BufErrorTy f = BufCloseError deriving (Show)
data LexErrorTy f = LexErrorTy deriving (Show)
data ParseErrorTy f = ParseErrorTy deriving (Show)

data Error f = Error (f (Error f))

bufCloseError :: (BufErrorTy :<: e) => Error e
bufCloseError = inject BufCloseError
lexError :: (LexErrorTy :<: e) => Error e
lexError = inject LexErrorTy
parseError :: (ParseErrorTy :<: e) => Error e
parseError = inject ParseErrorTy

fooError :: (FooTy :<: e) => Error e
fooError = inject FooTy

readBuf :: (BufErrorTy :<: e) => Except (Error e) a
readBuf = throwError bufCloseError

lex :: Except (Error (LexErrorTy :+: BufErrorTy)) a
lex = readBuf

parse1 :: Except (Error (ParseErrorTy :+: LexErrorTy :+: BufErrorTy)) a
parse1 = throwError bufCloseError

parse2 :: Except (Error (ParseErrorTy :+: LexErrorTy :+: BufErrorTy :+: FooTy)) a
parse2 = throwError parseError

foo :: Except (Error (LexErrorTy :+: BufErrorTy :+: FooTy)) a
foo = throwError fooError

catchSome ::
    (s :<: t)
    => Except (Error t) a
    -> (s (Error t) -> Except (s (Error t)) a)
    -> Except (Error t) a
catchSome op c =
    catchError
        op
        (\orig ->
            case recover orig of
                Just e -> withExcept inject (c e)
                Nothing -> throwError orig)

main :: IO ()
main = do
    let onlyBufError :: BufErrorTy s -> Except t ()
        onlyBufError BufCloseError = pure ()
    let nestedBufError :: BufErrorTy :<: t => BufErrorTy (Error t) -> Except (BufErrorTy (Error t)) ()
        nestedBufError BufCloseError = pure ()
    let nestedParseError :: ParseErrorTy :<: t => ParseErrorTy (Error t) -> Except (ParseErrorTy (Error t)) ()
        nestedParseError _ = pure ()
    case runExcept $ catchSome Main.lex onlyBufError of
        Left _e -> putStrLn "Uncaught (failure)"
        Right () -> putStrLn "Caught (success)"
    case runExcept $ catchSome Main.parse2 onlyBufError of
        Left _e -> putStrLn "Uncaught (success)"
        Right () -> putStrLn "Caught (failure)"
    case runExcept $ catchSome Main.parse1 nestedBufError of
        Left _e -> putStrLn "Uncaught (failure)"
        Right () -> putStrLn "Caught (success)"
    case runExcept $ catchSome Main.parse2 nestedParseError of
        Left _e -> putStrLn "Uncaught (failure)"
        Right () -> putStrLn "Caught (success)"
    case runExcept $ catchSome Main.foo nestedBufError of
        Left _e -> putStrLn "Uncaught (success)"
        Right () -> putStrLn "Caught (failure)"