Monad Transformer参数缺失疑问:StateT与IO的参数传递问题
核心原因就是类型构造器的部分应用,和函数的部分应用逻辑完全一致——先传一部分参数,剩下的留到后续补全。
关于StateT的参数缺失问题
你说StateT的种类是* -> (* -> *) -> * -> *,没错,它需要三个参数:
- 第一个是状态的类型(这里填的是String)
- 第二个是底层的monad构造器(这里填的是IO)
- 第三个是最终的结果类型(比如你的类型别名里的a)
当你写StateT String IO时,相当于给StateT传了前两个参数,第三个参数还没填。这时候StateT String IO本身就变成了一个接受结果类型a、返回完整StateT类型的构造器,它的种类是* -> *——刚好符合ReaderT对第二个参数的要求(ReaderT的第二个参数必须是一个* -> *的monad类型)。
等ReaderT填第三个参数a的时候,这个a会自动传递给StateT String IO,补全成完整的StateT String IO a。
关于IO没传参数的问题
IO的种类是* -> *,它确实需要一个结果类型参数,但这里它是作为StateT的第二个参数来用的——StateT的第二个参数要求的是一个* -> *的monad构造器,而不是一个完整的monad类型(比如IO Int这种)。
打个简单的比方:如果有个函数需要接收“String转Bool”的函数当参数,你直接传length > 5这种函数就行,不用先给它传个具体的String值。这里同理,StateT需要的是“能接受结果类型、生成monad的构造器”,所以直接传IO这个构造器就够了,后面用到的时候,StateT会自动把结果类型传递给IO。
结合你的类型别名看
type Board a = ReaderT String (StateT String IO) a 展开后,本质等价于:
type Board a = String -> StateT String IO a
而StateT String IO a又可以拆解为:
StateT String IO a = String -> IO (a, String)
所以最终Board a就是一个“接受环境字符串、接受初始状态字符串、返回IO动作(产出结果a和新状态字符串)”的类型。
内容的提问来源于stack exchange,提问作者user14749247

