我已经在互联网上搜索了这个关键字的作用的实际解释。我看过的每个Haskell教程都是随机开始使用它,而不是解释它的作用(我看了很多)。
以下是使用Just
的 Real World Haskell 的基本代码。我理解代码的作用,但我不明白Just
的目的或功能是什么。
lend amount balance = let reserve = 100
newBalance = balance - amount
in if balance < reserve
then Nothing
else Just newBalance
从我观察到的情况来看,它与Maybe
打字有关,但这几乎是我设法学习的全部内容。
对Just
的含义的一个很好的解释将非常感激。
答案 0 :(得分:185)
它实际上只是一个普通的数据构造函数,恰好在 Prelude 中定义,它是自动导入每个模块的标准库。
该定义如下所示:
data Maybe a = Just a
| Nothing
该声明定义了一个类型Maybe a
,它由类型变量a
参数化,这意味着您可以使用任何类型代替a
。
该类型有两个构造函数Just a
和Nothing
。当一个类型有多个构造函数时,这意味着必须仅使用一个可能的构造函数构造该类型的值。对于此类型,值是通过Just
或Nothing
构建的,没有其他(非错误)可能性。
由于Nothing
没有参数类型,因此当它用作构造函数时,它为所有类型Maybe a
命名一个常量值,该值是a
类型的成员。但是Just
构造函数确实有一个类型参数,这意味着当它用作构造函数时,它就像一个从a
类型到Maybe a
的函数,即它具有类型{{1} }
因此,类型的构造函数构建该类型的值;事情的另一面是你想要使用那个值,那就是模式匹配的用武之地。与函数不同,构造函数可用于模式绑定表达式,这是您可以对属于具有多个构造函数的类型的值进行案例分析的方式。
为了在模式匹配中使用a -> Maybe a
值,您需要为每个构造函数提供一个模式,如下所示:
Maybe a
在该case表达式中,如果值为case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
,则第一个模式将匹配,如果值由Nothing
构造,则第二个模式将匹配。如果第二个匹配,它还会将名称Just
绑定到传递给val
构造函数的参数,当您构建匹配的值时。
也许你已经熟悉这是如何运作的;对Just
值没有任何魔力,它只是一个普通的Haskell代数数据类型(ADT)。但它的使用相当多,因为它有效地提升了#34;或者将一个类型(例如Maybe
)扩展到一个新的上下文中,在该上下文中它有一个代表缺乏价值的额外值(Integer
)!然后,类型系统要求您检查该额外值,然后才能获得可能所在的Nothing
。这可以防止大量的错误。
今天,许多语言都处理这种“无价值”的语言。通过NULL引用的值。 Tony Hoare是一位杰出的计算机科学家(他发明了Quicksort并且是图灵奖得主),他拥有这个作为他的"billion dollar mistake"。 Maybe类型不是修复此问题的唯一方法,但事实证明它是一种有效的方法。
将一种类型转换为另一种类型的想法使得旧类型上的操作也转换为对新类型起作用是Haskell类型类后面的概念Integer
,Functor
有一个有用的实例。
Maybe a
提供了一个名为Functor
的方法,该方法将范围超出基本类型值(例如fmap
)的函数映射到范围超过提升类型值的函数(例如Integer
作为Maybe Integer
)。使用fmap
转换为Maybe
值的函数的工作方式如下:
case maybeVal of
Nothing -> Nothing -- there is nothing, so just return Nothing
Just val -> Just (f val) -- there is a value, so apply the function to it
因此,如果您有Maybe Integer
值m_x
和Int -> Int
函数f
,则可以fmap f m_x
应用函数f
直接到Maybe Integer
而不用担心它是否真的有价值。实际上,您可以将一整套提升的Integer -> Integer
函数应用于Maybe Integer
值,只需担心在您完成后明确检查Nothing
。
我不确定您对Monad
的概念有多熟悉,但您之前至少使用过IO a
,而且类型签名IO a
看起来非常显着类似于Maybe a
。虽然IO
的特殊之处在于它不会向您展示其构造函数,因此只能运行&#34;在Haskell运行时系统中,除了Functor
之外,它还是Monad
。事实上,Monad
只是一种特殊的Functor
具有一些额外的功能,这是一个重要的意义,但这并不是进入它的地方。< / p>
无论如何,像IO
这样的Monads将类型映射到表示&#34;导致值&#34;的计算的新类型。你可以通过一个名为Monad
的非常fmap
函数将函数提升为liftM
类型,它将常规函数转换为计算结果,通过计算得到的值得到。功能&#34;
您可能已经猜到(如果您已经读过这篇文章),Maybe
也是Monad
。它表示&#34;计算可能无法返回值&#34;。与fmap
示例一样,这使您可以执行一大堆计算,而无需在每个步骤后显式检查错误。事实上,构建Monad
实例的方式,Maybe
值的计算在遇到Nothing
时就会停止,因此它会在有点像计算过程中的直接中止或无价值的回报。
就像我之前说的那样,Maybe
类型中没有固有的东西可以融入语言语法或运行时系统。如果Haskell默认不提供它,您可以自己提供所有功能!实际上,无论如何,您可以自己再次编写它,使用不同的名称,并获得相同的功能。
希望您现在了解Maybe
类型及其构造函数,但如果仍有任何不清楚的地方,请告诉我们!
答案 1 :(得分:35)
目前大多数答案都是Just
和朋友如何工作的高度技术性解释;我想我可以尝试解释它的用途。
许多语言都有null
之类的值,可以用来代替实际值,至少对某些类型而言。 This has made a lot of people very angry and been widely regarded as a bad move.但是,有时候像null
这样的值来表示没有东西是有用的。
Haskell通过明确标记您可以拥有Nothing
(其版本为null
)的地方来解决此问题。基本上,如果您的函数通常会返回类型Foo
,则应返回类型Maybe Foo
。如果您想表明没有值,请返回Nothing
。如果您想返回值bar
,则应返回Just bar
。
所以基本上,如果你不能Nothing
,则不需要Just
。如果您有Nothing
,则需要Just
。
Maybe
没有什么神奇之处;它建立在Haskell类型系统上。这意味着您可以使用所有常用的Haskell pattern matching技巧。
答案 2 :(得分:13)
如果类型为t
,则值Just t
是t
类型的现有值,其中Nothing
表示无法达到某个值,或者拥有一个价值将毫无意义。
在您的示例中,负余额没有意义,因此如果发生这种情况,则会被Nothing
替换。
另一个例子,这可以用于除法,定义一个取a
和b
的除法函数,如果Just a/b
非零则返回b
,{ {1}}否则。它通常像这样使用,作为异常的替代方法,或者像前面的例子一样,替换那些没有意义的值。
答案 3 :(得分:2)
总函数a-> b可以为类型a的每个可能值找到类型b的值。
在Haskell中并非所有功能都是完整的。在这个特殊情况下,函数lend
不是全部 - 它没有定义为余额小于保留的情况(尽管根据我的口味,不允许newBalance小于保留更有意义 - 因为,你可以从100的余额中借101.
其他涉及非全部功能的设计:
lend
以返回旧余额这些是不能强制执行全部功能的语言的必要设计限制(例如,Agda可以,但这会导致其他复杂情况,例如变得不完整)。
返回特殊值或抛出异常的问题是调用者很容易忽略错误处理这种可能性。
无声地丢弃故障的问题也很明显 - 您限制了呼叫者可以对该功能执行的操作。例如,如果lend
返回旧余额,则调用者无法知道余额是否已更改。根据预期目的,它可能是也可能不是问题。
Haskell的解决方案强制部分函数的调用者处理类似Maybe a
或Either error a
的类型,因为函数的返回类型。
这样定义的lend
是一种并不总能计算新余额的函数 - 在某些情况下,未定义新余额。我们通过返回特殊值Nothing或将新余额包装在Just中来向调用者发出这种情况的信号。调用者现在可以自由选择:要么以特殊方式处理失败,要么忽略并使用旧余额 - 例如maybe oldBalance id $ lend amount oldBalance
。
答案 4 :(得分:-2)
功能if (cond :: Bool) then (ifTrue :: a) else (ifFalse :: a)
必须具有相同类型的ifTrue
和ifFalse
。
因此,当我们撰写then Nothing
时,我们必须在Maybe a
中使用else f
类型
if balance < reserve
then (Nothing :: Maybe nb) -- same type
else (Just newBalance :: Maybe nb) -- same type