我可以在Haskell中声明一个NULL值吗?

时间:2016-02-04 10:34:58

标签: haskell null

好奇,似乎在声明一个名字时,我们总是指定一些有效的值,比如让a = 3.问题是,在命令式语言中包括c / java,总是有一个关键字“null”。 Haskell有类似的东西吗?函数对象何时可以为空?

4 个答案:

答案 0 :(得分:13)

是一个“null”值,可用于任何类型的变量。它被称为⟂(发音为 bottom )。我们不需要关键字来生成最低价值;实际上⟂是任何不终止的计算的值。例如,

bottom = let x = x in x   -- or simply `bottom = bottom`

将无限循环。故意这样做显然不是一个好主意,但是你可以使用undefined作为“标准底值”。它可能是Haskell与Java null关键字最接近的事情。

但是你绝对不应该在Java程序员抓住null的大多数应用程序中使用它。

  • 由于Haskell中的所有内容都是不可变的,因此未定义的值始终保持不变。不可能将其用作“暂停一下,我稍后再定义”指示
  • 无法检查 值是否为最低值。事实上,对于rather deep theoretical reasons。因此,您不能将此用于可能定义或未定义的值。

你知道吗? 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是仿函数的事实。实际上它是几乎所有特定仿函数类的一个实例:FunctorApplicativeAlternativeFoldableTraversableMonad,{{ 3}}。它还将MonadPlus提升为semigroups

monoids现在,

你不需要知道这些东西到底是什么。但是,当您了解他们所做的事情时,您将能够编写非常简洁的代码,以正确的方式自动处理缺失值,并且没有错过支票的风险。

因为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. :("

或者使用由FunctorApplicativeAlternativeMonadMonadPlusFoldable的类型类实例提供的超级优化功能。

这可能是这样的:

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以及无效输入上使用的其他部分功能
    • 显式调用undefinederror "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必须在每次通话时打开。这是,因为通过这种方式,该函数的每个用户都被阻止简单地“忽略”不存在的情况,并编写错误的代码。