Haskell中“Just”语法意味着什么?

时间:2013-09-15 02:08:24

标签: haskell syntax

我已经在互联网上搜索了这个关键字的作用的实际解释。我看过的每个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的含义的一个很好的解释将非常感激。

5 个答案:

答案 0 :(得分:185)

它实际上只是一个普通的数据构造函数,恰好在 Prelude 中定义,它是自动导入每个模块的标准库。

可能是什么,结构

该定义如下所示:

data Maybe a = Just a
             | Nothing

该声明定义了一个类型Maybe a,它由类型变量a参数化,这意味着您可以使用任何类型代替a

构建和破坏

该类型有两个构造函数Just aNothing。当一个类型有多个构造函数时,这意味着必须仅使用一个可能的构造函数构造该类型的值。对于此类型,值是通过JustNothing构建的,没有其他(非错误)可能性。

由于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类型不是修复此问题的唯一方法,但事实证明它是一种有效的方法。

也许作为Functor

将一种类型转换为另一种类型的想法使得旧类型上的操作转换为对新类型起作用是Haskell类型类后面的概念IntegerFunctor有一个有用的实例。

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 Integerm_xInt -> Int函数f,则可以fmap f m_x应用函数f直接到Maybe Integer而不用担心它是否真的有价值。实际上,您可以将一整套提升的Integer -> Integer函数应用于Maybe Integer值,只需担心在您完成后明确检查Nothing

也许是Monad

我不确定您对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 tt类型的现有值,其中Nothing表示无法达到某个值,或者拥有一个价值将毫无意义。

在您的示例中,负余额没有意义,因此如果发生这种情况,则会被Nothing替换。

另一个例子,这可以用于除法,定义一个取ab的除法函数,如果Just a/b非零则返回b,{ {1}}否则。它通常像这样使用,作为异常的替代方法,或者像前面的例子一样,替换那些没有意义的值。

答案 3 :(得分:2)

总函数a-> b可以为类型a的每个可能值找到类型b的值。

在Haskell中并非所有功能都是完整的。在这个特殊情况下,函数lend不是全部 - 它没有定义为余额小于保留的情况(尽管根据我的口味,不允许newBalance小于保留更有意义 - 因为,你可以从100的余额中借101.

其他涉及非全部功能的设计:

  • 在检查输入值不符合范围时抛出异常
  • 返回一个特殊值(基本类型):最喜欢的选择是整数函数的负值,用于返回自然数(例如,String.indexOf - 当找不到子字符串时,返回的索引通常设计为是负面的)
  • 返回一个特殊值(指针):NULL或一些这样的
  • 默默地返回而不做任何事情:例如,如果不符合贷款条件,可以写lend以返回旧余额
  • 返回一个特殊值:Nothing(或者包装一些错误描述对象)

这些是不能强制执行全部功能的语言的必要设计限制(例如,Agda可以,但这会导致其他复杂情况,例如变得不完整)。

返回特殊值或抛出异常的问题是调用者很容易忽略错误处理这种可能性。

无声地丢弃故障的问题也很明显 - 您限制了呼叫者可以对该功能执行的操作。例如,如果lend返回旧余额,则调用者无法知道余额是否已更改。根据预期目的,它可能是也可能不是问题。

Haskell的解决方案强制部分函数的调用者处理类似Maybe aEither error a的类型,因为函数的返回类型。

这样定义的lend是一种并不总能计算新余额的函数 - 在某些情况下,未定义新余额。我们通过返回特殊值Nothing或将新余额包装在Just中来向调用者发出这种情况的信号。调用者现在可以自由选择:要么以特殊方式处理失败,要么忽略并使用旧余额 - 例如maybe oldBalance id $ lend amount oldBalance

答案 4 :(得分:-2)

功能if (cond :: Bool) then (ifTrue :: a) else (ifFalse :: a)必须具有相同类型的ifTrueifFalse

因此,当我们撰写then Nothing时,我们必须在Maybe a中使用else f类型

if balance < reserve
       then (Nothing :: Maybe nb)         -- same type
       else (Just newBalance :: Maybe nb) -- same type