Haskell中的“Just”语法是什么意思?

我已经在互联网上search这个关键字的实际解释。 我看过的每一个Haskell教程都会随机开始使用它,而不会解释它做了什么(而且我看了很多)。

下面是使用Just Real World Haskell的一段基本代码。 我明白代码的作用,但我不明白Just的目的或function是什么。

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

从我所观察到的,它与Maybeinput有关,但这几乎是我所学的东西。

非常感谢Just意思。

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

也许是结构性的

定义看起来像这样:

 data Maybe a = Just a | Nothing 

该声明定义了一个types, Maybe a ,它是由一个typesvariablesa参数化a ,这意味着您可以使用它来代替a

build设和破坏

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

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

所以,一个types的构造函数build立了这种types的值; 事情的另一方面是当你想使用这个价值,那就是模式匹配的地方。 与函数不同的是,构造函数可以在模式绑定expression式中使用,这是您可以对属于具有多个构造函数的types的值进行个案分析的方式。

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

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

在这种情况下,如果值为Nothing ,则第一个模式匹配,如果值使用Just构build,则第二个模式匹配。 如果第二个匹配,它也绑定名称val的parameter passing给Just构造函数,当你匹配的值构build。

什么也许意思

也许你已经熟悉如何工作了, Maybe值没有任何魔力,这只是一个正常的Haskell代数数据types(ADT)。 但是它被使用了很多,因为它可以有效地“提升”或者扩展一个types,比如Integer从你的例子到一个新的上下文,在这个上下文中它有一个额外的值( Nothing ),代表缺乏价值! 然后,types系统要求您检查额外的值,然后才能让您得到可能存在的Integer 。 这可以防止大量的错误。

今天很多语言通过NULL引用来处理这种“无值”的值。 托尼·霍尔(Tony Hoare)是一位着名的计算机科学家(他发明了Quicksort,是图灵奖的获得者),因此成为他“十亿美元的错误” 。 Maybetypes不是解决这个问题的唯一方法,但它已被certificate是一种有效的方法。

也许作为一个Functor

将一种types转换为另一种types的想法使旧types的操作可以转化为新types的操作是Haskelltypes类Functor背后的概念,其中Maybe a有一个有用的实例。

Functor提供了一个名为fmap的方法,该方法将函数的范围从基types(例如Integer )映射到函数,这些函数的范围超出了提升types的值(如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而不用担心它是否真的有一个值。 事实上,你可以将一整串提升的Integer -> Integer函数应用于Maybe Integer值,只需要担心在完成后显式检查Nothing

也许作为一个Monad

我不确定你对Monad的概念有多熟悉,但是你至less已经使用了IO a ,并且types签名IO aMaybe a非常相似。 虽然IO的特殊之处在于它并不向您公开其构造函数,因此只能由Haskell运行时系统“运行”,除了作为Monad之外,它仍然是一个Functor 。 事实上,有一个重要的意义, Monad只是一个特殊的Functor有一些额外的特征,但是这不是进入的地方。

无论如何,Monads将IO映射types转换为表示“导致值的计算”的新types,您可以通过称为liftM的非常fmap的函数将liftM提升为Monadtypes,将函数转换为“计算结果通过评估函数获得“。

你可能猜到了(如果你已经阅读了这个), Maybe也是Monad 。 它表示“可能无法返回值的计算”。 就像使用fmap示例一样,这可以让您执行大量计算,而无需在每个步骤之后明确检查错误。 事实上, Monad实例的构造方式, Maybe值的计算会在遇到Nothing 停止 ,所以它就像在计算过程中立即中止或无值返回一样。

你可能写过

就像我之前说过的那样, Maybe语言或者运行时系统中的Maybetypes并不是什么固有的东西。 如果Haskell没有默认提供它,你可以自己提供所有的function! 事实上,你可以用自己的名字重新写一遍,得到相同的function。

希望你现在能够理解Maybetypes及其构造函数,但是如果还有什么不清楚的地方,请告诉我!

目前的大部分答案都是对“ Just和“朋友”如何运作的高度技术性的解释。 我想我可能会试着解释它的用途。

很多语言的值都是null ,可以用来代替实际值,至less对于某些types来说。 这使得很多人非常生气,被广泛认为是一个不好的举动。 尽pipe如此,有一个像null这样的null来表示没有事物是有用的。

Haskell解决了这个问题,让你明确地标记你可以有一个Nothing (它的版本为null )。 基本上,如果你的函数通常返回typesFoo ,它应该返回Maybe Footypes。 如果你想表明没有价值,则返回Nothing 。 如果你想返回一个值bar ,你应该返回Just bar

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

Maybe没什么不可思议的; 它build立在Haskelltypes系统上。 这意味着你可以使用所有通常的Haskell 模式匹配技巧。

给定typestJust t的值是typest的现有值,其中Nothing表示未能达到值,或者具有值的情况将是无意义的。

在你的例子中,有一个负的平衡是没有意义的,所以如果发生这样的事情,它被replace为Nothing

又例如,这可以用于划分,定义一个除ab的除法函数,如果b不为零,则返回Just a/b ,否则不返回Nothing 。 它经常被这样使用,作为例外的一种方便的替代方法,或者像你之前的例子一样,用来代替没有意义的值。

总的函数a-> b可以为typesa的每个可能值findtypesb的值。

在Haskell中并不是所有的函数都是全部的。 在这种情况下,functionlend并不是完全的 – 在余额低于储备金的情况下,它并没有被定义(尽pipe如此,根据我的口味,如果不允许newBalance低于储备金,那么可以借用101从100的平衡)。

处理非全部function的其他devise:

  • 在检查input值时抛出exception不适合范围
  • 返回一个特殊的值(原始types):最喜欢的select是整数函数的负值,意味着返回自然数(例如,String.indexOf – 当找不到子string时,返回的索引通常devise为负数)
  • 返回一个特殊的值(指针):NULL或一些这样的
  • 默默无闻地回报:例如,如果贷款条件不符合, lend可以被写回旧的余额
  • 返回一个特殊的值:Nothing(或者Left包装一些错误描述对象)

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

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

默默地抛弃失败的问题也是显而易见的 – 你限制了调用者可以用这个函数做什么。 例如,如果lend返还的旧余额,则主叫方无法知道余额是否已经改变。 这可能是也可能不是问题,取决于预期的目的。

Haskell的解决scheme强制部分函数的调用者处理像Maybe a这样的types,或者由于函数的返回types而导致Either error a

这种方式lend因为它是定义,是一个函数,并不总是计算新的余额 – 在某些情况下,新的余额没有定义。 我们通过返回特殊值Nothing或通过在Just中包装新的余额来向呼叫者表明这种情况。 现在,调用者可以自由select:处理失败以特殊方式借出,或者忽略并使用旧的余额 – 例如, maybe oldBalance id $ lend amount oldBalance

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

所以,当我们写then Nothing ,我们必须在else f使用Maybe atypes

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