You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

fmap与<$>的编译差异问题:为何仅前者可正常运行?

为什么fmap能正常运行但<$>却无法编译?

这是个很常见的坑,我之前也踩过!大概率是下面这几个原因之一,咱们一个个来看:

1. 你可能忘记导入Control.Applicative模块

虽然fmap属于Functor类型类,而Haskell默认导入的Prelude模块已经包含了Functor的内容,所以fmap可以直接用。但<$>本质上是Applicative类型类里的运算符(它确实是fmap的中缀别名,但在旧版Haskell里归属Control.Applicative模块),如果你的环境没有默认导入这个模块,编译器就找不到<$>的定义,自然会报错。

举个例子,这段代码会编译失败:

main = print $ (+1) <$> Just 5

只需要在开头加上导入语句就能解决:

import Control.Applicative  -- 导入后就能正常使用<$>了

main = print $ (+1) <$> Just 5

(注:在较新的GHC版本中,Prelude已经默认包含了Applicative的部分内容,所以可能不需要手动导入,但如果是旧版本或者自定义了Prelude,还是得加这行。)

2. 中缀运算符的优先级/结合性导致解析错误

<$>是中缀运算符,它的优先级比普通函数调用要低。如果你的代码里没有给复杂的参数加括号,编译器会按照它的规则解析,结果可能和你预期的完全不一样,进而导致类型不匹配的错误。而fmap作为前缀函数,参数是明确用括号分隔的,所以不会有这个问题。

比如你写了这样的代码:

main = print $ (+1) <$> Just 5 + 3

编译器会把它解析成:((+1) <$> Just 5) + 3——左边是Maybe Int类型,右边是Int,这俩根本没法相加,自然编译报错。

而对应的fmap写法你可能会写成:

main = print $ fmap (+1) (Just (5 + 3))

这里你明确给5+3加了括号,编译器能正确识别Just的参数,所以没问题。要修复<$>的版本,只需要给5+3加上括号就行:

main = print $ (+1) <$> Just (5 + 3)

3. 不小心覆盖了<$>的定义

如果你自己在代码里定义了一个名为<$>的运算符,那编译器会优先使用你定义的版本,而不是标准库的fmap别名。这时候如果你的自定义运算符类型和预期不符,就会导致编译错误,而fmap因为没被覆盖,所以能正常工作。

检查一下你的代码里有没有类似这样的定义:

(<$>) :: Int -> Int -> Int
a <$> b = a * b

如果有的话,要么改掉这个自定义运算符的名字,要么用限定导入来区分标准库的版本:

import qualified Control.Applicative as A

main = print $ (+1) A.<$> Just 5

内容的提问来源于stack exchange,提问作者plx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:42:55