如何从单个函数返回多个类型的值?
我想做类似的事情:
take x y | x == [] = "error : empty list"
| x == y = True
| otherwise = False
我有使用命令式语言的背景。
答案 0 :(得分:9)
有一个名为Either
的类型构造函数,可以让您创建一个可以是两种类型之一的类型。它通常用于处理错误,就像在您的示例中一样。你可以这样使用它:
take x y | x == [] = Left "error : empty list"
| x == y = Right True
| otherwise = Right False
take
的类型将类似于Eq a => [a] -> [a] -> Either String Bool
。 Either
用于错误处理的约定是Left
表示错误,Right
表示正常返回类型。
如果您拥有Either
类型,则可以对其进行模式匹配,以查看其包含的值:
case take x y of
Left errorMessage -> ... -- handle error here
Right result -> ... -- do what you normally would
答案 1 :(得分:9)
根据您的意图,您的问题有多种解决方案:您是否希望在您的类型中制作您的功能可能失败的清单(在这种情况下,您是否希望返回失败的原因,这可能是不必要的如果这里只有一种失败模式,或者你估计在这个函数中得到一个空列表根本不会发生,所以想立即失败并抛出异常?
因此,如果您想明确表示类型失败的可能性,您可以使用Maybe,只是在没有解释的情况下指出失败(最终在您的文档中):
take :: (Eq a) => [a] -> [a] -> Maybe Bool
take [] _ = Nothing
take x y = x == y
或者要么注册失败的原因(请注意,要么是“从一个函数返回两种类型”的答案,一般来说,尽管你的代码更具体):
take :: (Eq a) => [a] -> [a] -> Either String Bool
take [] _ = Left "Empty list"
take x y = Right $ x == y
最后,您可以发出信号,表示此故障完全异常且无法在本地处理:
take :: (Eq a) => [a] -> [a] -> Bool
take [] _ = error "Empty list"
take x y = x == y
请注意,使用这种最后一种方式,调用站点不必立即处理失败,实际上它不能,因为异常只能在IO monad中捕获。使用前两种方式,必须修改调用站点以处理失败(并且可以)的情况,如果只是为了自己调用“错误”。
有一个最终解决方案允许调用代码选择所需的失败模式(使用失败包http://hackage.haskell.org/package/failure):
take :: (Failure String m, Eq a) => [a] -> [a] -> m Bool
take [] _ = failure "Empty list"
take x y = return $ x == y
这可以模仿Maybe和Either解决方案,或者你可以使用take作为IO Bool,如果它失败将抛出异常。它甚至可以在[Bool]上下文中工作(如果失败则返回空列表,这有时很有用)。
答案 2 :(得分:3)
您可以将error
函数用于例外:
take :: Eq a => [a] -> [a] -> Bool
take [] _ = error "empty list"
take x y = x == y
答案 3 :(得分:1)
到目前为止你得到的三个答案(来自Tikhon Jelvis,Jedai和Philipp)涵盖了处理这种情况的三种常规方法:
error
功能信号出错。但是,这通常是不受欢迎的,因为它使得使用您的函数的程序很难从错误中恢复。Maybe
表示无法生成Boolean
答案的情况。Either
,它通常用于执行与Maybe
相同的操作,但可以另外包含有关失败的更多信息(Left "error : empty list"
)。我选择了Maybe
和Either
方法,然后添加一个花絮(稍微高一点,但最终可能要达到):Maybe
和{{} {1}}可以组成monad,这可以用来编写在这两者之间选择中立的代码。 This blog post discusses eight different ways to tackle your problem,其中包括上述三个,第四个使用Either a
类型类来抽象Monad
和Maybe
之间的差异,还有其他四个。
博客文章是从2007年开始的,所以看起来有点陈旧,但我设法让#4以这种方式工作:
Either
现在这个{-# LANGUAGE FlexibleInstances #-}
take :: (Monad m, Eq a) => [a] -> [a] -> m Bool
take x y | x == [] = fail "error : empty list"
| x == y = return True
| otherwise = return False
instance Monad (Either String) where
return = Right
(Left err) >>= _ = Left err
(Right x) >>= f = f x
fail err = Left err
函数适用于两种情况:
take
虽然重要的是要注意*Main> Main.take [1..3] [1..3] :: Maybe Bool
Just True
*Main> Main.take [1] [1..3] :: Maybe Bool
Just False
*Main> Main.take [] [1..3] :: Maybe Bool
Nothing
*Main> Main.take [1..3] [1..3] :: Either String Bool
Right True
*Main> Main.take [1] [1..3] :: Either String Bool
Right False
*Main> Main.take [] [1..3] :: Either String Bool
Left "error : empty list"
是有争议的,所以我预计会对这种方法提出合理的反对意见。这里使用fail
并不重要,但可以将其替换为任何函数fail
,f :: (Monad m, ErrorClass m) => String -> m a
f err
Nothing
和Maybe
在Left err
。