2014-11-24 56 views
7

Edx Haskell course上,Erik Meijer反复指出,使用Maybe类型进行失败的计算不是应该做的事情;相反,应该为此使用空列表。空列表和可能代表Haskell中的失败计算

我的理解是,Maybe类型是一件好事,我们应该使用它。但是,似乎列表可以模拟Maybe可以建模的所有内容......因此,为什么我们完全需要Maybe类型?

+0

一个例子讲千言万语。另外,你提到的课程的链接会很好。 – Jubobs 2014-11-24 10:30:49

+1

@Jubobs:可能是:https://www.edx.org/course/introduction-functional-programming-delftx-fp101x。 – Zeta 2014-11-24 10:34:51

+0

@Zeta当然,但添加链接的责任在OP上:) – Jubobs 2014-11-24 10:39:24

回答

7

列表可以模拟任意数量的结果。另一方面,Maybe模型完全是一个结果或根本没有结果

想想以下功能:

f1 :: A -> [B] 
f2 :: B -> [C] 
f3 :: C -> [D] 
f4 :: D -> [E] 

目前尚不清楚有多少元素f1f2f3f4回报。那么如果你排序他们会发生什么?

f :: A -> [E] 
f s = f1 s >>= f2 >>= f3 >>= f4 

结果应包含多少个元素?一?零?我们是否意外地用n^n(n〜输入长度)元素创建了一个列表?

然而,如果计算将要在所有恰好返回一个值或没有价值,正确的类型为我们提供了所有必要的信息马上:

f1 :: A -> Maybe B 
f2 :: B -> Maybe C 
f3 :: C -> Maybe D 
f4 :: D -> Maybe E 

f :: A -> Maybe E 
f s = f1 s >>= f2 >>= f3 >>= f4 

不过就是这样。现在回到Meijer的声明:

Erik Meijer一再声明,对失败的计算使用Maybe类型不是应该做的事情;相反,应该为此使用空列表。

没有任何额外的客观推理,这只是个人偏好。我可以告诉大家,fmapmap好,这是我们应该做的。在那个时候,你要么相信我,要么提问。如果他在演讲中没有说清楚,直接问他。

17

但是似乎列表可以模拟一切或许可以模拟多

的“多”就是用Maybe一个很好的理由。作为列表的使用者,您需要能够处理零个,一个或多个值。作为Maybe的消费者,您只需要能够处理零个或一个值。因此,如果多个值没有意义,那么使用Maybe会更好,这样您就可以静态地知道您不会得到无意义的值。

+3

一个列表是一个很好的选择,对于那些返回多个值的非常成功的计算:-) – yatima2975 2014-11-24 10:44:00

0

Maybe的另一个有价值的观点是它只是最简单的错误处理单子的例子,它可以用一种方便和一致的方式来表示和组合“可破坏”的计算(另一个例子是Either和纯异常) 。列表monad在语义上是不同的(它表示非确定性计算),并且仅在空/单例情况下具有类似的行为,如上所示。

1

赞成名单:

  • 额外的值是没有问题的。当有多个结果时,客户端总是可以选择忽略列表的其余部分。

  • 仅使用列表可以避免Maybe和列表之间的繁琐转换,因为我们必须将两者混合。不需要listToMaybemaybeToList

  • catMaybes变成只是concat(或join)。

一个可能的问题是,重复使用(>>=)作为列表monad可以创建真正的大列表。但是,Haskell很懒。如果我们只使用第一个元素,则不会计算列表的其余部分。

>>> head (let xs = [1..1000000] in xs >>= \_ -> xs >>= \_ -> xs) 
1 
+0

这是一个很好的观点,没想到。然而,作为使用列表的函数的人,如果它扔掉了尾部,我会觉得有些惊讶。例如,假设'map'被实现为'map f [] = [];映射f(x:xs)= [f x]'。 – 2014-11-25 08:42:27

1

我想我会在这里一起合唱团说,除非我提交了他所有的它论证的细节我无法评价梅耶尔的建议。对我来说这似乎很简单:

  1. 使用Maybe返回0或1结果的函数。
  2. 对于返回0个或更多结果的函数,使用[]
  3. 如果您需要在做出不同选择的功能之间进行混合和匹配,则可以使用和maybeToList :: Maybe a -> [a]等功能来调整以某种风格编写的函数以在另一种风格中工作。
  4. 如果要延迟选择是使用Maybe还是[],则可以使用AlternativeMonadPlus类。点#4的

实施例:

import Control.Applicative (pure, Alternative(..)) 

safeDiv :: (Alternative f, Fractional a, Eq a) => a -> a -> f a 
safeDiv _ 0 = empty 
safeDiv x y = pure (x/y) 

{- 

>>> safeDiv 5 2 :: Maybe Float 
Just 2.5 

>>> safeDiv 5 0 :: Maybe Float 
Nothing 

>>> safeDiv 5 2 :: [Float] 
[2.5] 

>>> safeDiv 5 0 :: [Float] 
[] 

-} 

bothSqrt :: (Alternative f, Floating a) => a -> f a 
bothSqrt x = let x' = sqrt x 
      in pure x' <|> pure (-x') 

{- 

>>> bothSqrt 5 :: Maybe Float 
Just 2.236068 

>>> bothSqrt 5 :: [Float] 
[2.236068,-2.236068] 

>>> bothSqrt 5 >>= flip safeDiv 2 :: Maybe Float 
Just 1.118034 

>>>> bothSqrt 5 >>= flip safeDiv 2 :: [Float] 
[1.118034,-1.118034] 

-}