Haskell类型不匹配错误:m0 KAutomat与KAutomat类型兼容问题排查
m0 KAutomat与实际类型KAutomat”的错误 咱们先拆解这个错误的核心原因:你在do块里用<-提取firstFor的结果,但do块的规则是——<-右侧必须是一个monadic类型(比如IO KAutomat、Maybe KAutomat这种带上下文的包装类型),可你的firstFor函数返回的是直接的KAutomat纯值,没有被任何monad包裹,所以编译器才会抱怨“预期是m0 KAutomat,实际拿到KAutomat”。
先看你的代码片段:
reduce ts | (Set.null (setW ts))==False = do tsko <- firstFor (take 1 $ Set.toList $ setW ts) ts reduce tsko | otherwise = return ts
这里的return ts已经暗示reduce的返回类型是m KAutomat(某个monad包裹的自动机),但firstFor没跟上这个要求,输出的是纯值,和do块的上下文不兼容。
给你两种针对性的解决办法:
办法一:如果不需要monad上下文,改成纯递归函数
如果firstFor和整个reduce逻辑都没有副作用(比如不需要IO、不需要处理可能的失败),那完全可以去掉do块和return,让reduce成为纯函数:
reduce ts | not (Set.null (setW ts)) = let tsko = firstFor (take 1 $ Set.toList $ setW ts) ts in reduce tsko | otherwise = ts
顺便提一句,(Set.null (setW ts))==False可以写成not (Set.null (setW ts)),这是Haskell里更惯用的写法。
办法二:如果需要保留monadic返回类型
要是reduce必须返回monadic类型(比如后续要处理IO操作、或者用Maybe处理失败),那你只需要把firstFor的纯值提升到monad上下文里就行,有两种写法:
写法1:用let替代<-
reduce ts | not (Set.null (setW ts)) = do let tsko = firstFor (take 1 $ Set.toList $ setW ts) ts reduce tsko | otherwise = return ts
因为reduce本身返回monadic类型,所以在do块里直接调用reduce tsko就会自动把纯值的处理整合到monad流里。
写法2:用bind操作符>>=
如果你习惯更函数式的风格,也可以去掉do块,用>>=来串联逻辑:
reduce ts | not (Set.null (setW ts)) = firstFor (take 1 $ Set.toList $ setW ts) ts >>= reduce | otherwise = return ts
这里>>=会自动把firstFor返回的纯值包装进monad,然后传递给reduce函数。
内容的提问来源于stack exchange,提问作者user7303261

