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
从我所观察到的,它与Maybe
input有关,但这几乎是我所学的东西。
非常感谢Just
意思。
它实际上只是一个普通的types构造函数,它恰好在Prelude中定义,它是自动导入到每个模块中的标准库。
也许是结构性的
定义看起来像这样:
data Maybe a = Just a | Nothing
该声明定义了一个types, Maybe a
,它是由一个typesvariablesa
参数化a
,这意味着您可以使用它来代替a
。
build设和破坏
该types有两个构造函数, Just a
和Nothing
。 当一个types有多个构造函数时,这意味着这个types的值必须是用可能的构造函数构造的。 对于这种types,通过Just
或Nothing
构造一个值,不存在其他(非错误)可能性。
由于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 Integer
值m_x
和一个Int -> Int
函数f
,你可以通过fmap f m_x
将函数f
直接应用到Maybe Integer
而不用担心它是否真的有一个值。 事实上,你可以将一整串提升的Integer -> Integer
函数应用于Maybe Integer
值,只需要担心在完成后显式检查Nothing
。
也许作为一个Monad
我不确定你对Monad
的概念有多熟悉,但是你至less已经使用了IO a
,并且types签名IO a
与Maybe a
非常相似。 虽然IO
的特殊之处在于它并不向您公开其构造函数,因此只能由Haskell运行时系统“运行”,除了作为Monad
之外,它仍然是一个Functor
。 事实上,有一个重要的意义, Monad
只是一个特殊的Functor
有一些额外的特征,但是这不是进入的地方。
无论如何,Monads将IO
映射types转换为表示“导致值的计算”的新types,您可以通过称为liftM
的非常fmap
的函数将liftM
提升为Monad
types,将函数转换为“计算结果通过评估函数获得“。
你可能猜到了(如果你已经阅读了这个), Maybe
也是Monad
。 它表示“可能无法返回值的计算”。 就像使用fmap
示例一样,这可以让您执行大量计算,而无需在每个步骤之后明确检查错误。 事实上, Monad
实例的构造方式, Maybe
值的计算会在遇到Nothing
停止 ,所以它就像在计算过程中立即中止或无值返回一样。
你可能写过
就像我之前说过的那样, Maybe
语言或者运行时系统中的Maybe
types并不是什么固有的东西。 如果Haskell没有默认提供它,你可以自己提供所有的function! 事实上,你可以用自己的名字重新写一遍,得到相同的function。
希望你现在能够理解Maybe
types及其构造函数,但是如果还有什么不清楚的地方,请告诉我!
目前的大部分答案都是对“ Just
和“朋友”如何运作的高度技术性的解释。 我想我可能会试着解释它的用途。
很多语言的值都是null
,可以用来代替实际值,至less对于某些types来说。 这使得很多人非常生气,被广泛认为是一个不好的举动。 尽pipe如此,有一个像null
这样的null
来表示没有事物是有用的。
Haskell解决了这个问题,让你明确地标记你可以有一个Nothing
(它的版本为null
)。 基本上,如果你的函数通常返回typesFoo
,它应该返回Maybe Foo
types。 如果你想表明没有价值,则返回Nothing
。 如果你想返回一个值bar
,你应该返回Just bar
。
所以基本上,如果你不能拥有Nothing
,你就不需要Just
。 如果你Nothing
都Nothing
,你需要Just
。
Maybe
没什么不可思议的; 它build立在Haskelltypes系统上。 这意味着你可以使用所有通常的Haskell 模式匹配技巧。
给定typest
, Just t
的值是typest
的现有值,其中Nothing
表示未能达到值,或者具有值的情况将是无意义的。
在你的例子中,有一个负的平衡是没有意义的,所以如果发生这样的事情,它被replace为Nothing
。
又例如,这可以用于划分,定义一个除a
和b
的除法函数,如果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的ifTrue
和ifFalse
。
所以,当我们写then Nothing
,我们必须在else f
使用Maybe a
types
if balance < reserve then (Nothing :: Maybe nb) -- same type else (Just newBalance :: Maybe nb) -- same type