为Haskell的StateT实现Monad时遇编译错误,求技术解答
StateT的Monad实例实现编译错误排查
我正在通过fp-course学习Haskell,本次练习要求为StateT实现Monad实例,不清楚自己的实现为何触发编译错误,恳请帮忙排查。
我的实现代码
instance Monad k => Monad (StateT s k) where (=<<) :: forall a b. (a -> StateT s k b) -> StateT s k a -> StateT s k b (=<<) f sta = StateT $ \s -> let kas = runStateT sta s stb = (kas >>= (\(a, s1) -> (f a))) in runState stb s1
编译错误信息
src/Course/StateT.hs:94:17: error: • Couldn't match type ‘k’ with ‘(,) b’ ‘k’ is a rigid type variable bound by the instance declaration at src/Course/StateT.hs:89:10 Expected type: StateT s k b Actual type: StateT s ((,) b) b • In the expression: StateT $ \ s -> let kas = ... .... in runState stb s1 In an equation for ‘=<<’: (=<<) f sta = StateT $ \ s -> let ... in runState stb s1 In the instance declaration for ‘Monad (StateT s k)’ • Relevant bindings include sta :: StateT s k a (bound at src/Course/StateT.hs:94:11) f :: a -> StateT s k b (bound at src/Course/StateT.hs:94:9) (=<<) :: (a -> StateT s k b) -> StateT s k a -> StateT s k b (bound at src/Course/StateT.hs:94:3) src/Course/StateT.hs:95:65: error: • Couldn't match type ‘k’ with ‘StateT s k’ ‘k’ is a rigid type variable bound by the instance declaration at src/Course/StateT.hs:89:10 Expected type: k b Actual type: StateT s k b • In the expression: (f a) In the second argument of ‘(>>=)’, namely ‘(\ (a, s1) -> (f a))’ In the expression: (kas >>= (\ (a, s1) -> (f a))) • Relevant bindings include s1 :: s (bound at src/Course/StateT.hs:95:57) stb :: k b (bound at src/Course/StateT.hs:95:36) kas :: k (a, s) (bound at src/Course/StateT.hs:94:36) s :: s (bound at src/Course/StateT.hs:94:27) sta :: StateT s k a (bound at src/Course/StateT.hs:94:11) f :: a -> StateT s k b (bound at src/Course/StateT.hs:94:9) (Some bindings suppressed; use -fmax-relevant-binds=N or -fno-max-relevant-binds) src/Course/StateT.hs:96:49: error: • Variable not in scope: s1 :: (b, s) • Perhaps you meant ‘s’ (line 94) Failed, modules loaded: Course.Applicative, Course.Cheque, Course.Comonad, Course.Compose, Course.Contravariant, Course.Core, Course.ExactlyOne, Course.Extend, Course.FastAnagrams, Course.FileIO, Course.Functor, Course.JsonValue, Course.List, Course.Monad, Course.Optional, Course.Parser, Course.Person, Course.State, Course.Validation, Test.Framework.Random.
错误原因分析
- 核心类型不匹配:
kas的类型是k (a, s),使用>>=绑定的时候,回调函数必须返回k类型的值,但你直接返回了f a——它的类型是StateT s k b,和要求的k b完全不匹配,这是最关键的错误。 - 变量作用域错误:
s1是绑定在\(a, s1)这个lambda内部的变量,仅在该lambda的作用域内有效,你在lambda外部的runState stb s1中引用它,自然会提示“变量不在作用域”。 - 混淆运行函数:你错误使用了
runState(这是普通State类型的运行函数),而StateT对应的运行函数应该是runStateT,这导致类型推导混乱,让编译器误以为k是元组类型。
正确实现
instance Monad k => Monad (StateT s k) where (=<<) :: forall a b. (a -> StateT s k b) -> StateT s k a -> StateT s k b (=<<) f sta = StateT $ \s -> runStateT sta s >>= \(a, s1) -> runStateT (f a) s1
逻辑说明:
- 先用
runStateT sta s运行传入的StateT值,得到包裹在k里的(a, s1)。 - 通过
>>=绑定这个值,拿到计算结果a和新状态s1。 - 最后用
runStateT (f a) s1运行f a生成的StateT值,得到最终的k (b, s2),整个结果被包裹在StateT构造器中,完全符合类型要求。
内容的提问来源于stack exchange,提问作者zichao liu
相关产品推荐
相关产品推荐

