我对Haskell相对较新,开始阅读“真实世界Haskell”。
我只是偶然发现了类型,并且有一个关于如何从Just 1
获得实际价值的问题。
我写了以下代码:
combine a b c = (eliminate a, eliminate b, eliminate c)
where eliminate (Just a) = a
eliminate Nothing = 0
如果我使用,这可以正常工作:
combine (Just 1) Nothing (Just 2)
但是,如果我将1更改为String,则无效。
我想我知道原因:因为eliminate
必须回馈一种类型,在本例中是Int
。但是,如何更改eliminate
至少使用字符串(或者可能包含各种类型)?
答案 0 :(得分:43)
来自标准Prelude
,
maybe :: b -> (a -> b) -> Maybe a -> b
maybe n _ Nothing = n
maybe _ f (Just x) = f x
给定默认值和函数,将函数应用于Maybe
中的值或返回默认值。
您的eliminate
可以写为maybe 0 id
,例如应用身份功能,或返回0。
来自标准Data.Maybe
,
fromJust :: Maybe a -> a
fromJust Nothing = error "Maybe.fromJust: Nothing"
fromJust (Just x) = x
这是一个部分函数(不会为每个输入返回一个值,而不是 total 函数),但会尽可能提取值。
答案 1 :(得分:22)
[作者编辑,6年后]这是一个不必要的长答案,我不确定为什么它被接受了。根据最高评价答案中的建议使用maybe
或Data.Maybe.fromMaybe
。接下来是更多的思考实验而非实际建议。
所以你试图创建一个适用于许多不同类型的函数。这是上课的好时机。如果您使用Java或C ++编程,Haskell中的类就像是这些语言中的接口。
class Nothingish a where
nada :: a
这个类定义了一个值nada
,它应该是类Nothing
的等价物。现在有趣的部分:制作这个类的实例!
instance Nothingish (Maybe a) where
nada = Nothing
对于Maybe a
类型的值,类似Nothing的值是Nothing
!这将在一分钟内成为一个奇怪的例子。但在此之前,让我们列出这个类的实例。
instance Nothingish [a] where
nada = []
空列表有点像Nothing,对吧?因此对于String(它是Char的列表),它将返回空字符串""
。
数字也很容易实现。你已经表明0显然代表了数字的“虚无”。
instance (Num a) => Nothingish a where
nada = 0
除非您在文件顶部放置一个特殊行
,否则这个实际上不起作用{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
或者在编译它时,您可以设置这些语言编译指示的标志。不要担心它们,它们只是神奇的东西,使更多的东西工作。
所以现在你已经有了这个类和它的这些实例......现在让我们重新编写你的函数来使用它们!
eliminate :: (Nothingish a) => Maybe a -> a
eliminate (Just a) = a
eliminate Nothing = nada
注意我只将0
更改为nada
,其余内容相同。让我们给它一个旋转!
ghci> eliminate (Just 2)
2
ghci> eliminate (Just "foo")
"foo"
ghci> eliminate (Just (Just 3))
Just 3
ghci> eliminate (Just Nothing)
Nothing
ghci> :t eliminate
eliminate :: (Nothingish t) => Maybe t -> t
ghci> eliminate Nothing
error! blah blah blah...**Ambiguous type variable**
看起来非常适合价值观和东西。注意(Just Nothing)变成Nothing,看?这是一个奇怪的例子,一个可能在一个Maybe中。无论如何...... eliminate Nothing
怎么样?那么,结果类型是模糊的。它不知道我们期待什么。所以我们必须告诉它我们想要什么类型。
ghci> eliminate Nothing :: Int
0
继续尝试其他类型;你会看到每个人得到nada
。现在,当您将此函数与combine
函数一起使用时,您会得到:
ghci> let combine a b c = (eliminate a, eliminate b, eliminate c)
ghci> combine (Just 2) (Just "foo") (Just (Just 3))
(2,"foo",Just 3)
ghci> combine (Just 2) Nothing (Just 4)
error! blah blah Ambiguous Type blah blah
请注意,您仍需指明“Nothing”的类型,或指出您期望的返回类型。
ghci> combine (Just 2) (Nothing :: Maybe Int) (Just 4)
(2,0,4)
ghci> combine (Just 2) Nothing (Just 4) :: (Int, Int, Int)
(2,0,4)
或者,您可以通过将其类型签名明确地放在源中来限制您的函数允许的类型。如果函数的逻辑用法是仅用于相同类型的参数,那么这是有意义的。
combine :: (Nothingish a) => Maybe a -> Maybe a -> Maybe a -> (a,a,a)
combine a b c = (eliminate a, eliminate b, eliminate c)
现在它只有在所有三个可能的东西都是同一类型时才有效。这样,它将推断Nothing
与其他类型的类型相同。
ghci> combine (Just 2) Nothing (Just 4)
(2,0,4)
没有歧义,耶!但现在混合和匹配是错误的,就像我们之前做的那样。
ghci> combine (Just 2) (Just "foo") (Just (Just 3))
error! blah blah Couldn't match expected type blah blah
blah blah blah against inferred type blah blah
嗯,我认为这是一个足够长且过于夸张的答案。享受。
答案 2 :(得分:3)
我也是Haskell的新手,所以我不知道它是否存在于平台中(我确信它确实如此),但是如果存在一个值,那么获取值的“get或else”函数怎么样? ,否则返回默认值?
getOrElse::Maybe a -> a -> a
getOrElse (Just v) d = v
getOrElse Nothing d = d
答案 3 :(得分:2)
这是我在回答这个问题时所寻找的答案:
https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Maybe.html#v:fromJust
......同样地,对于Either
:
https://hackage.haskell.org/package/either-unwrap-1.1/docs/Data-Either-Unwrap.html
它们提供了我自己编写的函数,可以从其上下文中解包该值。
答案 4 :(得分:1)
eliminate
函数的类型签名是:
eliminate :: Maybe Int -> Int
那是因为它在Nothing上返回0,迫使编译器假设a :: Int
函数中的eliminate
。因此,编译器推导出combine
函数的类型签名为:
combine :: Maybe Int -> Maybe Int -> Maybe Int -> (Int, Int, Int)
这就是为什么当你向它传递一个String时它不起作用。
如果你把它写成:
combine a b c = (eliminate a, eliminate b, eliminate c)
where eliminate (Just a) = a
eliminate Nothing = undefined
那么它可以使用String或任何其他类型。原因依赖于undefined :: a
这一事实,它使eliminate
多态并适用于除Int之外的类型。
当然,这不是你的代码的目的,即使联合功能完全。
实际上,即使combine
对某些Nothing参数的应用成功(因为Haskell默认是懒惰的),一旦您尝试评估结果,您将得到运行时错误{{1无法评估有用的东西(用简单的术语来表示)。