在Haskell中返回空值而不收缩多态性
我将学习“真实世界的Haskell”,并将学习第2章,其中的练习是创建一个“lastButOne”函数,该函数返回列表中倒数第二个值。我最初的代码是:在Haskell中返回空值而不收缩多态性,haskell,Haskell,我将学习“真实世界的Haskell”,并将学习第2章,其中的练习是创建一个“lastButOne”函数,该函数返回列表中倒数第二个值。我最初的代码是: lastButOne xs = if null (tail (tail xs)) then head xs else lastButOne (tail xs) 这很好,除非我给它一个只有1或2项的列表。因此,我将其修改为: lastButOne xs = i
lastButOne xs = if null (tail (tail xs))
then head xs
else lastButOne (tail xs)
这很好,除非我给它一个只有1或2项的列表。因此,我将其修改为:
lastButOne xs = if null xs || null (tail xs)
then head xs
else
if null (tail (tail xs))
then head xs
else lastButOne (tail xs)
它会检查它是否只有1个或2个项目,但如果它只有1个项目,则会由于对head的调用而失败。我的问题是,除了“head xs”之外,我想不出任何其他可以返回的东西,理想情况下我希望它返回null或类似的东西,但是我找不到允许函数仍然是多态的“null”。为什么不使用模式匹配来处理只有1或2个元素的情况? 比如:
lastButOne :: [a] -> Maybe a
lastButOne [x,y] = Just x
lastButOne (x:y:t) = lastButOne (y:t)
lastButOne _ = Nothing
可能您正在寻找
可能类型:
lastButOne :: [a] -> Maybe a
lastButOne [x,_] = Just x
lastButOne (_:y:ys) = lastButOne (y:ys)
lastButOne _ = Nothing
这取决于您实际要执行的错误检查量
我怀疑,鉴于这只是在第2章中,你可能会忽略这个边缘案例。Haskell本身抛出一个异常:
Prelude> head []
*** Exception: Prelude.head: empty list
另一种选择是,如果这是一个需要大量处理的情况,则将lastButOne
的返回类型更改为Maybe a
,并在工作时返回Just(head xs)
,在失败时返回Nothing
。但这意味着您必须始终在中生活,这可能并不理想(因为您必须“打开”值才能使用它)。Andrew Jaffe提到的“扩展错误处理”示例可能是:
{-# LANGUAGE FlexibleContexts #-} module Main where
import Control.Monad.Error
lastButOne :: MonadError String m => [a] -> m a
lastButOne (_ : y : []) = return y
lastButOne (_ : t) = lastButOne
lastButOne _ = throwError "Error in lastButOne"
在这种情况下,如果成功,可以返回类型:右x
,如果失败,可以返回左“lastButOne中的错误”
,但是MonadError字符串m
的许多其他实例也是可能的
也可以阅读更多的可能性
[一] 我确实希望它返回null或类似的东西,但是我找不到允许函数仍然是多态的“null”
关于如何做到这一点,已经有很多答案,所以让我回答一个更基本的问题:“为什么没有‘空’?”
这是Haskell中类型检查器原理的一部分。大多数语言都包含某种形式的特殊值,表示错误或缺少数据。示例包括0
,-1
”
(空字符串)JavaScriptsnull
或Ruby的nil
。问题是,这会产生一整类潜在的bug,其中调用函数只处理“好”值,当返回一个特殊值时,程序崩溃或更糟地破坏数据。还有一个问题是,特殊值被指定为常规值,即0
实际上是一个数字,而不是故障状态
在您的问题中,问题可能是这样的:“调用lastButOne
的函数是否知道如何处理null
?”
对于某些形式良好的输入,没有有效答案的问题在Haskell中非常常见,有两种典型的解决方案。第一个来自Erlang:快速、干净地失败。在此解决方案下,调用者有责任确保数据“良好”。如果不是这样,则程序会因错误而死亡(或捕获异常,但在Haskell中这比其他语言更困难),这是大多数列表函数使用的解决方案,例如头,尾,等等
另一种解决方案是显式声明函数可能无法生成良好的数据。类型可能是这些解决方案中最简单的一种。它允许您返回一个值Nothing
,该值的工作原理与您要查找的null
非常相似。但这是有代价的。您的函数的类型签名变为lastButOne::[a]>可能是a
。现在,每个调用函数都必须接受一个a
,而不是一个,可能是一个,在得到答案之前,它必须告诉Haskell如果答案是Nothing
,它将做什么 null
不是传统的“null”值-它是一个测试列表是否为空的函数。我不认为这段代码可以编译…实际上我们可以逃避也许@AndrewJaffe:我想他是说通过良好的旧模式匹配:案例m的{Just x->…;Nothing->…}
必须打开Just,这正是拥有Maybe类型的关键,因为您可以让类型系统保护您免受NullPtrExceptions的影响。但关键是,某些错误有时确实最好留作错误。《前奏曲》没有开头是有原因的::[a]->可能是一个@AndrewJaffe我知道很多Haskeller喜欢开头和其他类似的东西来返回可能值。说得很好。在OP中,您还将看到可能
在通过RWH时使用了更多。然后,当你回到Java或者其他什么的时候,你会很害怕,因为如果你不小心的话,你程序中的每个变量都可能变成null。