首页 > 解决方案 > 在执行返回 Either 的函数后如何知道它是在左侧还是在右侧?

问题描述

我是 Haskell 的新手,我想做一个函数,如果它离开则返回退出,但我不知道该怎么做。这是我的函数,它返回:

getOpts :: Conf->[String]-> Either String Conf
getOpts Help _ = Right Help
getOpts c@Conf{} ("--start":x:xs) = readPos x >>= (\x-> getOpts c{start = x} xs)
getOpts c@Conf{} ("--lines":x:xs) = 
    readPos x >>= (\x-> getOpts c{Main.lines = x} xs)
getOpts c@Conf{} ("--window":x:xs) = 
    readPos x >>= (\x-> getOpts c{window = x} xs)
getOpts c@Conf{} ("--move":x:xs) = readPos x >>= (\x-> getOpts c{move = x} xs)
getOpts c@Conf{} ("--rules":x:xs) = 
    readMPos x >>= (\x-> getOpts c{rules = x} xs)
getOpts c []  = Right c 
getOpts _ _ = Left "Invalid Parameter!"```

标签: haskelleither

解决方案


有几种方法:

如果要显式处理结果,可以在Either构造函数上进行模式匹配:

foo :: Either a b -> c
foo (Left a) = <some function from (a -> c)>
foo (Right b) = <some function from (b -> c)>

如果您有一些要根据先前结果处理的条件,则可以使用isLeftand isRightfrom 。Data.Either看起来你正在使用命令行参数,所以你可能在 IO monad 中。有类似fromLeftand的函数fromRight,如果它们的模式匹配失败,它们会采用默认值,否则它们会解开匹配的构造函数。这看起来像fromLeft :: a -> Either a b -> a,如果它是一个值,a您提供的将被返回。Right你可以像这样使用这些:

whenIsLeft :: (a -> IO ()) -> Either a b -> IO ()
whenIsLeft f e = if isLeft e then (applyToLeft f e) else pure ()
  where applyToLeft func (Left x) = func x

explodeIfRight :: Either a b -> a
explodeIfRight = fromLeft (error "KABOOM")

whichHand :: Either a b -> String
whichHand e = if isLeft e then "Left!!!" else "Right :DDD"

对于whenIsLeft,f可能类似于\err -> print err >> exitWith (ExitFailure 1). Maybe请记住, :有一个类似的功能,fromJust如果模式匹配失败,默认情况下会抛出错误。

您还可以使用模式匹配来表达这一点,以避免if isLeft then fromLeft...

whenIsLeft' :: (a -> IO ()) -> Either a b -> IO ()
whenIsLeft' f (Left e) = f e
whenIsLeft' _ (Right _) = pure ()

最后,前奏中有一个非常方便的组合函数叫做either,它有两个函数,一个用于每个可能的“路径”。

either :: (a -> c) -> (b -> c) -> Either a b -> c
either errFunc successFunc leftOrRight = 
  case leftOrRight of
    Left e -> errFunc e
    Right x -> successFunc x

两个函数都需要返回相同的类型,因此您可以使用它来处理错误并回到正轨,或者在错误不可恢复时退出。

exitIfLeft :: Either a b -> IO ()
exitIfLeft = either (\err -> print err >> exitWith (ExitFailure 1)) (pure . const ())

一般来说,Either如果一连串的 -returning 函数失败,它会形成一个短路的 monad Either,所以你通常不需要模式匹配直到最后。

例如,如果您有 3 个函数,其中任何一个都可能失败并显示错误消息:

f :: w -> Either String x
g :: x -> Either String y
h :: y -> Either String z

然后你可以像链接(组合)它们一样

fgh :: w -> Either String z
fgh w = f w >>= g >>= h

并且只需要在最后检查结果。

unlessZ :: w -> String
unlessZ w = case (f w >>= g >>= h) of
  Left e ...
  -- the ideal use case for `either`

monad 实例为您抽象出所有模式匹配/错误检查。如果您有一些只想应用到一个分支的函数,您可以使用Control.Arrow 中的leftand ,或者如果值在构造函数内,则使用for 的实例将该函数映射到容器内。rightFunctorEitherRight

一般来说,Haskell 中控制流是由不同数据构造函数的模式匹配决定的。您可以通过将函数分配给数据类型的每个构造函数来构建整个处理程序系统。


推荐阅读