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

使用State monad的foldM无法通过类型检查,求排查

foldM结合State Monad的编译错误修复

问题描述

现有foldM相关资料未覆盖State Monad场景,官方文档无对应示例,编写的代码无法编译,代码及报错信息如下:

原代码

import Control.Monad.State.Lazy
import Control.Monad ( foldM )

data Student = Student { name :: String, sid :: Int } deriving ( Show )
data TestResult = TestResult { tested :: Student, grade :: Int } deriving ( Show )
data Classroom = Classroom { students :: [ Student ] } deriving ( Show )

test :: Student -> State Classroom TestResult
test s = do { c <- get; put c { students = s:(students c) }; return $ TestResult { tested = s, grade = 2 * (sid s) } }

tests :: [ Student ] -> State Classroom [ TestResult ]
tests = mapM test

worst :: State Classroom TestResult
worst = return $ TestResult { tested = Student { name = "MMM", sid = 0 }, grade = 0 }

better :: TestResult -> TestResult -> State Classroom TestResult
better r1 r2 = return $ case (grade r1) < (grade r2) of { True -> r2; False -> r1 }

brightest :: [ Student ] -> State Classroom TestResult
brightest students = foldM better worst (tests students)

main = print $ evalState (brightest [ Student "X" 19, Student "Y" 49, Student "Z" 46 ]) (Classroom [])

编译报错

Main.hs:22:35: error: [GHC-83865]
    • Couldn't match type ‘StateT
                             Classroom Data.Functor.Identity.Identity TestResult’
                     with ‘TestResult’
      Expected: TestResult
        Actual: State Classroom TestResult
    • In the second argument of ‘foldM’, namely ‘worst’
      In the expression: foldM better worst (tests students)
      In an equation for ‘brightest’:
          brightest students = foldM better worst (tests students)
   |
22 | brightest students = foldM better worst (tests students)
   |                                   ^^^^^

Main.hs:22:42: error: [GHC-83865]
    • Couldn't match type ‘[TestResult]’ with ‘TestResult’
      Expected: StateT
                  Classroom Data.Functor.Identity.Identity TestResult
        Actual: State Classroom [TestResult]
    • In the third argument of ‘foldM’, namely ‘(tests students)’
      In the expression: foldM better worst (tests students)
      In an equation for ‘brightest’:
          brightest students = foldM better worst (tests students)
   |
22 | brightest students = foldM better worst (tests students)
   |                                          ^^^^^^^^^^^^^^

错误分析

两个错误均源于对foldM类型签名的误解:

  • foldM的类型为Foldable t => (b -> a -> m b) -> b -> t a -> m b,其中:
    1. 第二个参数是累积器的初始值,类型为b,而非m b。原代码中worst被包裹在State monad中,导致类型不匹配。
    2. 第三个参数是待遍历的序列t a,而非m (t a)。原代码中tests students返回的是State Classroom [TestResult],是monadic容器中的列表,无法直接作为foldM的输入。

修复方案

  1. 将worst改为纯TestResult值,不需要包裹在State中;
  2. 在brightest函数中,先通过do块提取tests students返回的测试结果列表,再传给foldM;
  3. 简化代码中的冗余语法,提升可读性。

修复后的完整代码

import Control.Monad.State.Lazy
import Control.Monad ( foldM )

data Student = Student { name :: String, sid :: Int } deriving ( Show )
data TestResult = TestResult { tested :: Student, grade :: Int } deriving ( Show )
data Classroom = Classroom { students :: [ Student ] } deriving ( Show )

test :: Student -> State Classroom TestResult
test s = do 
  c <- get
  put c { students = s : students c }
  return $ TestResult s (2 * sid s)

tests :: [ Student ] -> State Classroom [ TestResult ]
tests = mapM test

worst :: TestResult
worst = TestResult (Student "MMM" 0) 0

better :: TestResult -> TestResult -> State Classroom TestResult
better r1 r2 = return $ if grade r1 < grade r2 then r2 else r1

brightest :: [ Student ] -> State Classroom TestResult
brightest students = do
  allResults <- tests students
  foldM better worst allResults

main = print $ evalState (brightest [ Student "X" 19, Student "Y" 49, Student "Z" 46 ]) (Classroom [])

运行结果

执行后会输出最高成绩的测试结果:

TestResult {tested = Student {name = "Y", sid = 49}, grade = 98}

同时Classroom的students列表会包含所有测试过的学生(test函数会将学生添加到列表中)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 05:55:14