为什么对[Maybe Integer]列表求和需要两次调用sum函数?
核心原因
你观察到的行为是Haskell的类型类重载和Functor实例共同作用的结果,分两点拆解:
- 你用到的
fmap是列表类型的Functor实现fmap的通用类型是Functor f => (a -> b) -> f a -> f b,当你将fmap作用在类型为[Maybe Integer]的列表上时,这里的f就是列表类型,列表的fmap实现等价于普通的map函数。所以fmap sum (map square [1..64])本质是对列表里的每个Maybe Integer元素单独调用sum,而不是对整个列表求和。 sum可以直接作用于Maybe类型sum的类型签名是(Foldable t, Num a) => t a -> a,它支持所有实现了Foldable类型类的容器作为入参,而Maybe是标准库中已经实现了Foldable的类型:
- 当入参是
Just x时,sum直接返回x - 当入参是
Nothing时,sum返回Num类型的默认零值
所以这一步fmap sum的效果就是把列表里的Maybe包装拆掉,Nothing转成0,Just值转成内部的整数,最终得到一个[Integer]类型的列表,你再对这个列表调用sum才能得到总和。
更优雅的复用写法
你原来的写法虽然能跑,但可读性一般,可以用更直观的方式拆包Maybe值再求和:
import Data.Maybe (maybe) total :: Integer total = sum $ map (maybe 0 id) $ map square [1..64]
因为你遍历的范围是明确合法的[1..64],也可以直接用等比数列求和公式复用square的边界逻辑,计算效率更高:
total :: Integer total = case square 64 of Just v -> v * 2 - 1 Nothing -> 0
内容的提问来源于stack exchange,提问作者arsalanQ
相关产品推荐
相关产品推荐

