Haskell中为什么对Maybe Integer调用succ需要使用fmap?
问题解答
你对报错原因的判断完全正确:succ 是 Enum 类型类提供的方法,只有实现了该类型类的类型可以调用该函数,Maybe Integer 没有默认的 Enum 实例,所以直接对 collatz 返回的 Maybe Integer 类型值调用 succ 会触发你看到的类型错误。
fmap succ 能够解决问题的原因如下:
fmap是函子(Functor)类型类的核心方法,签名为fmap :: (a -> b) -> f a -> f b,作用是将一个作用在普通值上的函数,提升为作用在函子容器上的函数,不需要破坏容器的结构。Maybe本身就是Functor的实现实例,它的fmap逻辑非常直观:- 当输入是
Just 有效值时,会将函数作用在有效值上,再用Just包裹返回:fmap f (Just x) = Just (f x) - 当输入是
Nothing时,直接返回Nothing不做任何处理:fmap f Nothing = Nothing
- 当输入是
放在你的代码场景里:succ 本身是 Integer -> Integer 类型的函数,只能作用在整数上;经过 fmap 提升后得到的 fmap succ 是 Maybe Integer -> Maybe Integer 类型的函数,刚好可以作用在 collatz 递归调用的返回值上:
- 如果递归返回的是
Just 步数,就自动给步数加1再包回Just - 如果递归返回的是
Nothing(比如输入了不合法的非正整数),会直接透传Nothing,不会触发运行时错误。
你也可以手动写模式匹配实现完全等价的逻辑,本质上和 fmap succ 做的事情没有区别:
| even n = case collatz (n `div` 2) of Just step -> Just (succ step) Nothing -> Nothing
使用 fmap 只是帮你省略了重复的模式匹配代码,写法更简洁。
内容的提问来源于stack exchange,提问作者arsalanQ
相关产品推荐
相关产品推荐

