Haskell 如何处理链锁?
我有一个关于chainedHaskell 如何处理链锁?,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
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)"