Haskell使用>>运算符后无法推断Writer Monad返回类型的原因
Haskell Writer Monad类型推断歧义问题解析
代码示例
import Control.Monad.Writer class Foo c where fromInt :: Int -> c instance Foo [Int] where fromInt n = [n] instance (Monoid c, Foo c) => Foo (Writer c ()) where fromInt d = writer ((), fromInt d) onetwo :: Writer [Int] () onetwo = fromInt 1 >> fromInt 2
编译错误信息
Ambiguous type variable `a0' arising from a use of `fromInt' prevents the constraint `(Foo (WriterT [Int] Data.Functor.Identity.Identity a0))' from being solved.
问题原因解释
核心问题出在类型推断的顺序和实例定义的特异性上:
fromInt的类型签名是Int -> c,编译器处理fromInt 1时,只知道返回类型c需要满足Foo c,同时因为后面用了>>(Monad操作符),c还得是Monad实例。- 你定义的
Foo (Writer c ())实例是针对特定类型的——它只给Writer c ()(即返回值为()的Writer Monad)实现了Foo,但这个()是实例的一部分,不是fromInt签名的约束。 - 编译器在推断时,会先把
fromInt 1的类型暂定为Writer [Int] a0(因为onetwo的类型是Writer [Int] ()),但它不知道a0必须是()——它看不到实例里的这个限定,除非你明确告知。此时编译器无法确定a0的取值,就会抛出类型歧义错误。
简单说:编译器需要先确定Writer的返回值类型a0,才能匹配你定义的Foo (Writer c ())实例;但它又没有足够的线索从代码里直接推断出a0就是(),于是陷入了推断循环。
解决方法
有几种常见的修复方式:
- 给调用加类型注解:明确指定
fromInt的返回类型onetwo :: Writer [Int] () onetwo = (fromInt 1 :: Writer [Int] ()) >> (fromInt 2 :: Writer [Int] ()) - 使用TypeApplications扩展:直接指定实例的类型参数
{-# LANGUAGE TypeApplications #-} onetwo :: Writer [Int] () onetwo = fromInt @(Writer [Int] ()) 1 >> fromInt @(Writer [Int] ()) 2 - 修改类型类定义:把返回类型的单元约束写到类签名里
class Monad m => Foo m where fromInt :: Int -> m () instance Foo [] where fromInt n = [n] instance (Monoid c, Foo c) => Foo (Writer c) where fromInt d = writer ((), fromInt d)
内容的提问来源于stack exchange,提问作者141592653
相关产品推荐
相关产品推荐

