只是好奇,似乎在声明一个名称时,我们总是指定一些有效的值,比如let a = 3。问题是,在命令式语言中,包括c/java,总是有一个关键字“null”。Haskell有类似的功能吗?什么时候函数对象可以为null?
kuuvgm7e1#
这里有一个“null”值,你可以用它来表示任何类型的变量。它被称为(发音为 bottom)。我们不需要一个关键字来产生底部值;实际上是任何不终止的计算的值。比如说
bottom = let x = x in x -- or simply `bottom = bottom`
字符串会无限循环故意这样做显然不是一个好主意,但是您可以使用undefined作为“标准底部值”。这可能是Haskell最接近Java的null关键字的东西。但是对于大多数Java程序员会使用null的应用程序,您绝对不应该/不能使用它。
undefined
null
你知道吗?Haskell不允许这样做真是太好了!在Java中,您需要经常注意值可能为null。在Haskell中,如果一个值是bottom,那么有些东西是明显损坏的,但这永远不会是预期行为的一部分/你可能需要检查的东西。如果对于某个值,它可能没有被定义,那么你必须总是通过将类型 Package 在Maybe中来明确这一点。通过这样做,您可以确保任何试图使用该值的人 * 必须 * 首先检查它是否存在。不可能忘记这一点,并在运行时遇到空引用异常!由于Haskell非常擅长处理变量类型,因此检查Maybe Package 的值的内容并不太麻烦。你可以直接用模式匹配来做
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、MonadPlus。它也提升半群到monoids。
Functor
Applicative
Alternative
Foldable
Traversable
Monad
MonadPlus
你不需要知道这些东西到底是什么但是,当你了解了它们的作用之后,你将能够编写非常简洁的代码,总是以正确的方式自动处理丢失的值,并且没有丢失检查的风险。†因为Haskell是懒惰的,你通常不需要推迟任何计算。编译器将自动确保计算 * 在必要时 * 完成,而且不会更早。
oxosxuxt2#
Haskell中没有null。你想要的是Maybe单子。
data Maybe a = Just a | Nothing
字符串Nothing表示经典的null,Just包含一个值。然后你可以对它进行模式匹配:
Nothing
Just
foo Nothing = Nothing foo (Just a) = Just (a * 10)
型或者使用case语法:
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等一起使用。
foo
(Num a) => Maybe a
(Num a) => [a]
(Num a) => Either e a
am46iovg3#
Haskell没有null。这是一个设计特征。它完全防止了由于空指针异常而导致代码崩溃的任何可能性。如果你看一下用命令式语言编写的代码,99%的代码 * 期望 * 的东西永远不会为null,如果你给予它null,它会发生灾难性的故障。但是1%的代码 * 确实 * 期望空值,并且 * 使用 * 这个特性来指定可选参数或其他东西。但是,通过查看代码,您无法轻松地判断哪些部分需要null作为法律的参数,哪些部分不需要。希望它的文件-但不要屏住呼吸!在Haskell中,没有null。如果这个参数被声明为Customer,那么这里 * 必须 * 是一个实际的、真实的Customer。你不能只是传入一个null(故意或错误地)。因此,99%的代码期望一个真实的的Customer将始终工作。那剩下的1%呢?我们有Maybe。但这是一个明确的事情;你必须明确地说“这个值是可选的”。而且你必须在使用它的时候明确地检查。你不能“忘记”检查;编译不了所以,是的,没有“null”,但有Maybe,它有点类似,但更安全。
Customer
g0czyy6m4#
在Haskell(或许多其他FP语言)中不存在。如果你有一些T类型的表达式,它的求值将给予一个T类型的值,但有以下例外:
T
let f n = f (n+1) in f 0
字符串
head []
fromJust Nothing
error "message"
请注意,即使上述情况可能被视为称为“bottoms”(该名称来自域理论)的“特殊”值,通常也不能在运行时对这些值进行测试。因此,这些与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中,以下内容是不受欢迎的:
IO
Maybe a
Option[A]
Optional<A>
Maybe T
-- 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必须在每次调用时展开。这很好,因为通过这种方式,可以防止函数的每个用户简单地“忽略”不存在的情况,并编写错误代码。
Int
4条答案
按热度按时间kuuvgm7e1#
这里有一个“null”值,你可以用它来表示任何类型的变量。它被称为(发音为 bottom)。我们不需要一个关键字来产生底部值;实际上是任何不终止的计算的值。比如说
字符串
会无限循环故意这样做显然不是一个好主意,但是您可以使用
undefined
作为“标准底部值”。这可能是Haskell最接近Java的null
关键字的东西。但是对于大多数Java程序员会使用
null
的应用程序,您绝对不应该/不能使用它。你知道吗?Haskell不允许这样做真是太好了!在Java中,您需要经常注意值可能为null。在Haskell中,如果一个值是bottom,那么有些东西是明显损坏的,但这永远不会是预期行为的一部分/你可能需要检查的东西。如果对于某个值,它可能没有被定义,那么你必须总是通过将类型 Package 在
Maybe
中来明确这一点。通过这样做,您可以确保任何试图使用该值的人 * 必须 * 首先检查它是否存在。不可能忘记这一点,并在运行时遇到空引用异常!由于Haskell非常擅长处理变量类型,因此检查
Maybe
Package 的值的内容并不太麻烦。你可以直接用模式匹配来做型
或者你可以使用
Maybe
是一个函子的事实。事实上,它几乎是每个特定函子类的示例:Functor
、Applicative
、Alternative
、Foldable
、Traversable
、Monad
、MonadPlus
。它也提升半群到monoids。Dᴏɴ'ᴛ Pᴀɴɪᴄ现在
你不需要知道这些东西到底是什么但是,当你了解了它们的作用之后,你将能够编写非常简洁的代码,总是以正确的方式自动处理丢失的值,并且没有丢失检查的风险。
†因为Haskell是懒惰的,你通常不需要推迟任何计算。编译器将自动确保计算 * 在必要时 * 完成,而且不会更早。
oxosxuxt2#
Haskell中没有null。你想要的是
Maybe
单子。字符串
Nothing
表示经典的null,Just
包含一个值。然后你可以对它进行模式匹配:
型
或者使用
case
语法:型
或者使用
Functor
、Applicative
、Alternative
、Monad
、MonadPlus
和Foldable
的类型类示例提供的支持功能。这可能看起来像这样:
型
您甚至可以使用更通用的签名:
型
这使得该函数适用于任何能够实现
Monad
所提供功能的数据类型。因此,您可以将foo
与(Num a) => Maybe a
,(Num a) => [a]
,(Num a) => Either e a
等一起使用。am46iovg3#
Haskell没有null。这是一个设计特征。它完全防止了由于空指针异常而导致代码崩溃的任何可能性。
如果你看一下用命令式语言编写的代码,99%的代码 * 期望 * 的东西永远不会为null,如果你给予它null,它会发生灾难性的故障。但是1%的代码 * 确实 * 期望空值,并且 * 使用 * 这个特性来指定可选参数或其他东西。但是,通过查看代码,您无法轻松地判断哪些部分需要null作为法律的参数,哪些部分不需要。希望它的文件-但不要屏住呼吸!
在Haskell中,没有null。如果这个参数被声明为
Customer
,那么这里 * 必须 * 是一个实际的、真实的Customer
。你不能只是传入一个null(故意或错误地)。因此,99%的代码期望一个真实的的Customer
将始终工作。那剩下的1%呢?我们有
Maybe
。但这是一个明确的事情;你必须明确地说“这个值是可选的”。而且你必须在使用它的时候明确地检查。你不能“忘记”检查;编译不了所以,是的,没有“null”,但有
Maybe
,它有点类似,但更安全。g0czyy6m4#
在Haskell(或许多其他FP语言)中不存在。如果你有一些
T
类型的表达式,它的求值将给予一个T
类型的值,但有以下例外:字符串
head []
、fromJust Nothing
和其他用于无效输入的部分函数undefined
、error "message"
或其他异常抛出原语请注意,即使上述情况可能被视为称为“bottoms”(该名称来自域理论)的“特殊”值,通常也不能在运行时对这些值进行测试。因此,这些与Java的
null
完全不同。更准确地说,你不能写这样的东西型
一些异常值可以在
IO
monad中捕获,但请忘记这一点--Haskell中的异常不是惯用的,而且大致上只用于IO错误。如果您想要一个可以在运行时测试的异常值,请使用
Maybe a
类型,正如@bash0r已经建议的那样。这种类型类似于Scala的Option[A]
或Java的不常用的Optional<A>
。该值同时具有类型
T
和类型Maybe T
,以便能够精确地识别哪些函数总是成功,哪些函数可能失败。例如,在Haskell中,以下内容是不受欢迎的:型
相反,这是优选的:
型
后者的结果不如前者方便,因为
Int
必须在每次调用时展开。这很好,因为通过这种方式,可以防止函数的每个用户简单地“忽略”不存在的情况,并编写错误代码。