空列表vs可能代表Haskell中的失败计算

时间:2014-11-24 10:28:20

标签: haskell maybe

Edx Haskell course处,Erik Meijer反复声明使用Maybe类型进行失败的计算并不是应该做的事情;相反,应该使用空列表。

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

5 个答案:

答案 0 :(得分:17)

  

然而,似乎列表可以模拟Maybe可以建模的所有内容以及更多

"以及更多"使用Maybe是一个很好的理由。作为列表的使用者,您需要能够处理零个,一个或多个值。作为Maybe的使用者,您只需要能够处理零个或一个值。因此,在多个值没有意义的情况下,最好使用Maybe,以便您静静地知道您不会获得无意义的值。

答案 1 :(得分:8)

列表可以模拟任意数量的结果。另一方面,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好,这是我们应该做的事情。在这一点上,你要么相信我,要么提出问题。如果他在演讲中没有说清楚,请直接问他。

答案 2 :(得分:1)

支持列表:

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

  • 只使用列表可以避免在我们必须混合使用时,在Maybe和列表之间进行繁琐的转换。无需listToMaybemaybeToList

  • catMaybes变为concat(或join)。

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

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

答案 3 :(得分:1)

我想我会加入合唱团,说我不能评价Meijer的建议,除非我被告知他的论点的所有细节。对我而言似乎很简单:

  1. Maybe用于返回0或1结果的函数。
  2. 对返回0或更多结果的函数使用[]
  3. 如果您需要在进行不同选择的函数之间进行混合和匹配,则可以选择使用Data.Maybe中的listToMaybe :: [a] -> Maybe amaybeToList :: Maybe a -> [a]等函数来调整以一种样式编写的函数在另一个。
  4. 如果您想延迟选择是使用Maybe还是[],可以使用AlternativeMonadPlus类。
  5. 第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]
    
    -}
    

答案 4 :(得分:0)

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