“公正”的句法在哈斯克尔是什么意思?

我已经在互联网上搜索了这个关键词的实际作用的解释。我看过的每一个 Haskell 教程都是随机使用的,从来没有解释过它的功能(我看过很多)。

下面是来自 真实世界 Haskell的一段使用 Just的基本代码。我知道代码是做什么的,但是我不知道 Just的用途和功能是什么。

lend amount balance = let reserve    = 100
newBalance = balance - amount
in if balance < reserve
then Nothing
else Just newBalance

根据我的观察,它与 Maybe打字有关,但这几乎是我所学到的全部。

如能详细解释 Just的含义,我们将不胜感激。

61549 次浏览

给定一个 t类型,一个 Just t值就是一个 t类型的现有值,其中 Nothing表示未能达到某个值,或者在某种情况下拥有一个值是没有意义的。

在您的示例中,负平衡是没有意义的,因此,如果出现这种情况,它将被 Nothing取代。

另一个例子是,这可以在除法中使用,定义一个除法函数,该函数接受 ab,如果 b非零,则返回 Just a/b,否则返回 Nothing。它经常被这样使用,作为异常的一种方便的替代方法,或者像您之前的示例一样,用来替换没有意义的值。

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

从结构上来说,也许是

定义大致如下:

data Maybe a = Just a
| Nothing

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

建造与破坏

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

由于 Nothing没有参数类型,当它被用作构造函数时,它为所有类型的 a命名一个常量值,该常量值是 Maybe a类型的成员。但是 Just构造函数确实有一个类型参数,这意味着当作为构造函数使用时,它的作用类似于从 aMaybe a的函数,也就是说,它具有 a -> Maybe a类型

因此,类型的构造函数构建该类型的值,另一方面,当你希望使用该值时,模式匹配就会发挥作用。与函数不同,构造函数可以在模式绑定表达式中使用,通过这种方式,您可以对属于具有多个构造函数的类型的值执行 案例分析

为了在模式匹配中使用 Maybe a值,您需要为每个构造函数提供一个模式,如下所示:

case maybeVal of
Nothing   -> "There is nothing!"
Just val  -> "There is a value, and it is " ++ (show val)

在这种情况下,如果值是 Nothing,则第一个模式将匹配,如果值是用 Just构造的,则第二个模式将匹配。如果第二个匹配,它还将名称 val绑定到构造所匹配的值时传递给 Just构造函数的参数。

也许意味着什么

也许你已经熟悉了它的工作原理,对于 ABc0值来说并没有什么神奇之处,它只是一个普通的 Haskell 代数类型(ADT)。但是它被使用得很多,因为它有效地“提升”或扩展了一个类型,例如从您的示例中提取的 Integer,到一个新的上下文中,其中有一个额外的值(Nothing) ,表示缺乏值!然后,类型系统要求您检查那个额外的值,然后才允许您在 Integer处获得 也许吧。这可以防止大量的错误。

现在许多语言通过 NULL 引用处理这种“无值”值。著名的计算机科学家托尼 · 霍尔(他发明了 Quicksort,并获得了图灵奖)承认这是他的 “数十亿美元的错误”。“也许”类型不是解决这个问题的唯一方法,但它已被证明是一种有效的方法。

也许是作为一个函数器

将一种类型转换为另一种类型,这样旧类型上的操作就可以转换为新类型上的操作,这个想法是称为 Functor的 Haskell 类型类背后的概念,Maybe a有一个有用的 Functor实例。

Functor提供了一个名为 fmap的方法,该方法将范围从基类型(如 Integer)到范围从提升类型(如 Maybe Integer)的值的函数映射到范围从基类型(如 Maybe 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_x和一个 Int -> Int函数 f,那么可以执行 fmap f m_x将函数 f直接应用于 Maybe Integer,而不用担心它是否真的得到了一个值。实际上,您可以对 Maybe Integer值应用提升的 Integer -> Integer函数的整个链,只需要担心在完成时显式检查一次 Nothing

也许作为一个 Monad

我不确定您对 Monad的概念有多熟悉,但是您至少以前使用过 IO a,而且类型签名 IO a看起来非常类似于 Maybe a。尽管 IO的特殊之处在于它不向您公开它的构造函数,因此只能由 Haskell 运行时系统“运行”,但除了 Monad之外,它还是 Functor。事实上,有一个重要的意义,即 Monad只是一种特殊的 Functor与一些额外的功能,但这不是地方进入。

无论如何,Monads 像 IO映射类型到新类型,表示“导致值的计算”,你可以通过一个非常类似于 fmap的函数 liftM将函数提升到 Monad类型,它将正则函数转化为“通过计算函数得到的值的计算”

您可能已经猜到(如果您已经读到这里) Maybe也是 Monad。它表示“可能无法返回值的计算”。与 fmap示例一样,这使您可以执行大量计算,而不必在每个步骤之后显式检查错误。事实上,构造 Monad实例的方式,只要遇到 Nothing,就会在 Maybe上计算 停止值,所以这有点像是在计算中间立即中止或无值返回。

你本可以写信的

正如我前面所说的,Maybe类型没有什么内在的东西被嵌入到语言语法或运行时系统中。如果 Haskell 没有默认提供它,那么您可以自己提供它的所有功能!事实上,你可以自己再写一次,用不同的名字,得到相同的功能。

希望您现在已经理解了 Maybe类型及其构造函数,但是如果还有什么不清楚的地方,请让我知道!

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

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

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

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

在哈斯克尔,并非所有职能都是全部的。在这种特殊情况下,函数 lend不是总函数——它不是为余额小于余额的情况定义的(尽管,在我看来,不允许 newBalance 小于余额更有意义——正如所做的,你可以从余额100中借101)。

处理非全部功能的其他设计:

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

这些是语言中必要的设计限制,它们不能强制执行所有的功能(例如,Agda 可以,但是这会导致其他复杂性,比如变得图灵-不完整)。

返回特殊值或引发异常的问题在于,调用方很容易因为错误而忽略对这种可能性的处理。

无声地丢弃失败的问题也很明显——您限制了调用者对函数的使用。例如,如果 lend返回旧的余额,调用方就无法知道余额是否已经改变。这可能是一个问题,也可能不是,取决于预期的目的。

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

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

目前大多数的答案都是对 Just和朋友们如何工作的高度技术性的解释; 我想我可以试着解释一下它的用途。

许多语言都有一个类似 null的值,可以用来代替实际值,至少对于某些类型是这样。尽管如此,有时使用像 null这样的值来表示缺少某样东西还是很有用的。

Haskell 解决了这个问题,它让你明确地标记哪里可以有一个 Nothing(它的版本的 null)。基本上,如果函数通常返回类型 Foo,那么它应该返回类型 Maybe Foo。如果要指示没有值,请返回 Nothing。如果要返回值 bar,则应该返回 Just bar

所以基本上,如果你不能有 Nothing,你就不需要 Just。如果你能有 Nothing,你就需要 Just

Maybe并没有什么神奇之处,它是建立在 Haskell 类型系统之上的。这意味着您可以对它使用所有常用的 Haskell 模式匹配技巧。