在Edx Haskell course上,Erik Meijer反复指出,使用Maybe
类型进行失败的计算不是应该做的事情;相反,应该为此使用空列表。空列表和可能代表Haskell中的失败计算
我的理解是,Maybe
类型是一件好事,我们应该使用它。但是,似乎列表可以模拟Maybe
可以建模的所有内容......因此,为什么我们完全需要Maybe
类型?
在Edx Haskell course上,Erik Meijer反复指出,使用Maybe
类型进行失败的计算不是应该做的事情;相反,应该为此使用空列表。空列表和可能代表Haskell中的失败计算
我的理解是,Maybe
类型是一件好事,我们应该使用它。但是,似乎列表可以模拟Maybe
可以建模的所有内容......因此,为什么我们完全需要Maybe
类型?
列表可以模拟任意数量的结果。另一方面,Maybe
模型完全是一个结果或根本没有结果。
想想以下功能:
f1 :: A -> [B]
f2 :: B -> [C]
f3 :: C -> [D]
f4 :: D -> [E]
目前尚不清楚有多少元素f1
,f2
,f3
或f4
回报。那么如果你排序他们会发生什么?
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类型不是应该做的事情;相反,应该为此使用空列表。
没有任何额外的客观推理,这只是个人偏好。我可以告诉大家,fmap
比map
好,这是我们应该做的。在那个时候,你要么相信我,要么提问。如果他在演讲中没有说清楚,直接问他。
但是似乎列表可以模拟一切或许可以模拟多
的“多”就是用Maybe
一个很好的理由。作为列表的使用者,您需要能够处理零个,一个或多个值。作为Maybe
的消费者,您只需要能够处理零个或一个值。因此,如果多个值没有意义,那么使用Maybe
会更好,这样您就可以静态地知道您不会得到无意义的值。
一个列表是一个很好的选择,对于那些返回多个值的非常成功的计算:-) – yatima2975 2014-11-24 10:44:00
Maybe的另一个有价值的观点是它只是最简单的错误处理单子的例子,它可以用一种方便和一致的方式来表示和组合“可破坏”的计算(另一个例子是Either和纯异常) 。列表monad在语义上是不同的(它表示非确定性计算),并且仅在空/单例情况下具有类似的行为,如上所示。
赞成名单:
额外的值是没有问题的。当有多个结果时,客户端总是可以选择忽略列表的其余部分。
仅使用列表可以避免Maybe和列表之间的繁琐转换,因为我们必须将两者混合。不需要listToMaybe
或maybeToList
。
catMaybes
变成只是concat
(或join
)。
一个可能的问题是,重复使用(>>=)
作为列表monad可以创建真正的大列表。但是,Haskell很懒。如果我们只使用第一个元素,则不会计算列表的其余部分。
>>> head (let xs = [1..1000000] in xs >>= \_ -> xs >>= \_ -> xs)
1
这是一个很好的观点,没想到。然而,作为使用列表的函数的人,如果它扔掉了尾部,我会觉得有些惊讶。例如,假设'map'被实现为'map f [] = [];映射f(x:xs)= [f x]'。 – 2014-11-25 08:42:27
我想我会在这里一起合唱团说,除非我提交了他所有的它论证的细节我无法评价梅耶尔的建议。对我来说这似乎很简单:
Maybe
返回0或1结果的函数。[]
。maybeToList :: Maybe a -> [a]
等功能来调整以某种风格编写的函数以在另一种风格中工作。Maybe
还是[]
,则可以使用Alternative
或MonadPlus
类。点#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]
-}
一个例子讲千言万语。另外,你提到的课程的链接会很好。 – Jubobs 2014-11-24 10:30:49
@Jubobs:可能是:https://www.edx.org/course/introduction-functional-programming-delftx-fp101x。 – Zeta 2014-11-24 10:34:51
@Zeta当然,但添加链接的责任在OP上:) – Jubobs 2014-11-24 10:39:24