好奇,似乎在声明一个名字时,我们总是指定一些有效的值,比如让a = 3.问题是,在命令式语言中包括c / java,总是有一个关键字“null”。 Haskell有类似的东西吗?函数对象何时可以为空?
答案 0 :(得分:13)
是一个“null”值,可用于任何类型的变量。它被称为⟂(发音为 bottom )。我们不需要关键字来生成最低价值;实际上⟂是任何不终止的计算的值。例如,
bottom = let x = x in x -- or simply `bottom = bottom`
将无限循环。故意这样做显然不是一个好主意,但是你可以使用undefined
作为“标准底值”。它可能是Haskell与Java null
关键字最接近的事情。
但是你绝对不应该在Java程序员抓住null
的大多数应用程序中使用它。
你知道吗? Haskell不允许这样做真的很棒!在Java中,您经常需要警惕值可能为null。在Haskell中,如果某个值低于某些值,那么这将永远不会成为您可能需要检查的预期行为的一部分。如果某些值意图它可能未被定义,那么您必须始终通过将类型包装在Maybe
中来使其显式化。通过这样做,您确保尝试使用值的任何人必须首先检查它是否存在。不可能忘记这一点并在运行时遇到空引用异常!
因为Haskell非常善于处理变体类型,所以检查Maybe
- 包装值的内容实际上并不太麻烦。您可以使用模式匹配明确地执行此操作,
quun :: Int -> String
quun i = case computationWhichMayFail i of
Just j -> show j
Nothing -> "blearg, failed"
computationWhichMayFail :: Int -> Maybe Int
或者您可以使用Maybe
是仿函数的事实。实际上它是几乎所有特定仿函数类的一个实例:Functor
,Applicative
,Alternative
,Foldable
,Traversable
,Monad
,{{ 3}}。它还将MonadPlus
提升为semigroups。
† 因为Haskell很懒惰,所以通常不需要推迟以后进行任何计算。编译器会自动确认计算是在必要时完成的,并且不久就完成了。
答案 1 :(得分:7)
Haskell中没有null。你想要的是Maybe
monad。
data Maybe a
= Just a
| Nothing
Nothing
指经典null,Just
包含值。
然后你可以对它进行模式匹配:
foo Nothing = Nothing
foo (Just a) = Just (a * 10)
或使用case
语法:
let m = Just 10
in case m of
Just v -> print v
Nothing -> putStrLn "Sorry, there's no value. :("
或者使用由Functor
,Applicative
,Alternative
,Monad
,MonadPlus
和Foldable
的类型类实例提供的超级优化功能。
这可能是这样的:
foo :: Maybe Int -> Maybe Int -> Maybe Int
foo x y = do
a <- x
b <- y
return $ a + b
您甚至可以使用更一般的签名:
foo :: (Monad m, Num a) => m a -> m a -> m a
这使得此功能适用于任何能够提供Monad
功能的数据类型。因此,您可以将foo
与(Num a) => Maybe a
,(Num a) => [a]
,(Num a) => Either e a
等一起使用。
答案 2 :(得分:5)
Haskell没有&#34; null&#34;。这是一个设计功能。它完全防止了由于空指针异常导致代码崩溃的任何可能性。
如果你看一下使用命令式语言编写的代码,99%的代码期望东西永远不会为空,如果你给它null,它将会发生灾难性的故障。但是,然后1%的代码确实期望空值,并且使用此功能来指定可选参数或其他任何参数。但是,通过查看代码,哪些部分期望空值作为合法参数,以及哪些部分不是,您无法轻易地告诉您。希望它有记录 - 但不要屏住呼吸!
在Haskell中,没有空值。如果该参数声明为Customer
,那么必须是真实的Customer
。你不能传递空(有意或无意)。因此,99%的期望真实Customer
的代码将始终有效。
但另外1%呢?好吧,为此我们有Maybe
。但这是一个明确的事情;你必须明确地说&#34;这个值是可选的&#34;。并且您必须显式检查何时使用它。你不能忘记&#34;去检查;它不会编译。
所以是的,没有&#34; null&#34;,但有Maybe
有点相似,但更安全。
答案 3 :(得分:2)
不在Haskell(或许多其他FP语言)中。如果您有某种类型T
的表达式,则其评估将提供类型为T
的值,但以下情况除外:
无限递归可能会使程序“永远循环”并且无法返回任何内容
let f n = f (n+1) in f 0
运行时错误可以提前中止程序,例如:
head []
,fromJust Nothing
以及无效输入上使用的其他部分功能undefined
,error "message"
或其他引发异常的原语请注意,即使上述情况可能被视为称为“底部”的“特殊”值(名称来自领域理论),通常也不能在运行时测试这些值。所以,这些与Java的null
完全不同。更准确地说,你不能写像
-- assume f :: Int -> Int
if (f 5) is a division-by-zero or infinite recursion
then 12
else 4
可以在IO
monad中捕获一些异常值,但遗忘 - Haskell中的异常不是惯用的,并且大致仅用于IO错误。
如果您想要一个可在运行时测试的特殊值,请使用Maybe a
类型,如@ bash0r已建议的那样。这种类型类似于Scala的Option[A]
或Java不太常用的Optional<A>
。
值为T
和类型Maybe T
都能够精确地识别哪些函数总是成功,哪些函数可能失败。在Haskell中,以下是不赞成的,例如:
-- Finds a value in a list. Returns -1 if not present.
findIndex :: Eq a => [a] -> a -> Int
相反,这是首选:
-- Finds a value in a list. Returns Nothing if not present.
findIndex :: Eq a => [a] -> a -> Maybe Int
后者的结果不如前者那么方便,因为Int
必须在每次通话时打开。这是好,因为通过这种方式,该函数的每个用户都被阻止简单地“忽略”不存在的情况,并编写错误的代码。