Haskell列表Monad使用报错:类型不匹配问题求助
Haskell Monad练习:列表Monad类型不匹配问题解析
已实现的numberSplit函数
功能是将整数拆分为符合规则的整数对:
- 输入3 → 输出
[(2,1), (1,2)] - 输入4 → 输出
[(3,1), (2,2), (1,3)]
实现代码:
numberSplit :: Int -> [(Int,Int)] numberSplit 0 = [] numberSplit 1 = [] numberSplit n = numberSplitHelper 1 (n - 1) -- Helper functions -- numberSplitHelper :: Int -> Int -> [(Int, Int)] numberSplitHelper 0 0 = [] numberSplitHelper 0 1 = [] numberSplitHelper 1 0 = [] numberSplitHelper 1 1 = [(1, 1)] numberSplitHelper n 1 = [(n, 1)] numberSplitHelper n m = (n, m) : numberSplitHelper (n + 1) (m - 1)
目标:实现andExpressionsAtSize函数
需求:通过列表Monad,将numberSplit拆分出的整数对传入给定函数生成表达式,再用EAnd组合成目标表达式列表。
预期输入
(sort (andExpressionsAtSize (\ i -> if i == 1 then [EBase True,EBase False] else if i == 2 then [ENot (EBase True),ENot (EBase False)] else []) 3))
预期输出
[EAnd (EBase False,EBase False) ,EAnd (EBase False,EBase True) ,EAnd (EBase True,EBase False) ,EAnd (EBase True, EBase True)]
错误代码与报错信息
编写的错误代码:
andExpressionsAtSize :: (Int -> [Expression]) -> Int -> [Expression] andExpressionsAtSize f 0 = [] andExpressionsAtSize f n = do intList <- numberSplit n intList >>= f
触发的类型错误:
Couldn't match type ‘(,) Int’ with ‘[]’ Expected: [Expression] Actual: (Int, Expression)
问题原因
核心错误是对>>=操作符的适用场景理解偏差:
numberSplit n返回的是[(Int, Int)],所以do块中intList <- numberSplit n取出的是单个元组(a, b),而非列表。>>=在列表Monad中的类型是[a] -> (a -> [b]) -> [b],但你这里把元组(a,b)当成了列表Monad的实例来调用>>=。元组本身有自己的Monad实例(Monad ((,) r)),它的>>=会返回(r, b)类型的值,这和函数预期返回的[Expression]类型完全不匹配,因此触发类型报错。
简单来说:你需要处理元组内的两个整数,分别传入f生成表达式列表,再取笛卡尔积组合成EAnd,而非直接对元组使用>>=。
修正方案
正确的实现需要遍历每个拆分出的整数对,对元组中的两个整数分别生成表达式列表,再组合所有可能的表达式对并包装成EAnd:
用列表Monad的do块实现
andExpressionsAtSize :: (Int -> [Expression]) -> Int -> [Expression] andExpressionsAtSize _ 0 = [] andExpressionsAtSize f n = do (x, y) <- numberSplit n -- 取出每个拆分的整数对 expr1 <- f x -- 遍历x对应的所有表达式 expr2 <- f y -- 遍历y对应的所有表达式 return $ EAnd (expr1, expr2) -- 组合成EAnd表达式并返回
用列表推导式实现(等价于do块)
andExpressionsAtSize :: (Int -> [Expression]) -> Int -> [Expression] andExpressionsAtSize _ 0 = [] andExpressionsAtSize f n = [ EAnd (e1, e2) | (x, y) <- numberSplit n , e1 <- f x , e2 <- f y ]
这两种实现都能正确生成预期的表达式列表,满足需求。
内容的提问来源于stack exchange,提问作者chen
相关产品推荐
相关产品推荐

