38

我对 Haskell 比较陌生,并开始阅读“Real World Haskell”。

我只是偶然发现了 Maybe 类型,并且有一个关于如何从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以至少处理字符串(或者可能处理所有类型)?

4

5 回答 5

48

从标准来看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

这是一个部分函数(不为每个输入返回一个值,与函数相反),但会在可能的情况下提取该值。

于 2011-02-09T02:01:23.170 回答
24

[从作者编辑,6 年后] 这是一个不必要的长答案,我不确定它为什么被接受。使用maybeData.Maybe.fromMaybe按照最高投票的答案中的建议。接下来是一个思想实验,而不是实际的建议。

因此,您正在尝试创建一个适用于多种不同类型的函数。这是上课的好时机。如果您使用 Java 或 C++ 编程,那么 Haskell 中的类有点像这些语言中的接口。

class Nothingish a where
    nada :: a

这个类定义了一个值nada,它应该是类的等价物Nothing。现在有趣的部分:制作这个类的实例!

instance Nothingish (Maybe a) where
    nada = Nothing

对于 type 的值Maybe a,Nothing-like 值是,嗯,Nothing!这将是一个奇怪的例子。但在那之前,让我们也让列表成为这个类的一个实例。

instance Nothingish [a] where
    nada = []

一个空列表有点像什么都没有,对吧?所以对于一个字符串(它是一个字符列表),它将返回空字符串,"".

数字也是一个简单的实现。您已经指出 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

请注意,我只更改0nada,其余的都是一样的。让我们试一试吧!

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 in a 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)

现在它只有在所有三个 Maybe 都是相同类型的情况下才有效。这样,它将推断出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

好吧,我认为这是一个足够长和夸大其词的答案。享受。

于 2011-02-09T05:08:20.540 回答
4

I'm new to Haskell too, so I don't know if this exists in the platform yet (I'm sure it does), but how about a "get or else" function to get a value if it exists, else return a default?

getOrElse::Maybe a -> a -> a
getOrElse (Just v) d = v
getOrElse Nothing d  = d
于 2014-02-26T17:53:51.460 回答
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

它们提供了我自己编写的函数,这些函数将值从其上下文中解开。

于 2016-08-29T14:41:57.590 回答
1

eliminate函数的类型签名是:

eliminate :: Maybe Int -> Int

a :: Int那是因为它在 Nothing 上返回 0,迫使编译器在你的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 函数完全。

事实上,即使combine对一些 Nothing 参数的应用会成功(这是因为 Haskell 默认是惰性的),一旦你尝试评估结果,你就会得到一个运行时错误,因为undefined无法评估一些有用的东西(放简单来说)。

于 2011-02-09T12:50:03.320 回答