Haskell中return 1 getLine通过类型检查返回1的原理是什么?
Haskell
return 1 getLine行为原理解析 所谓「Haskell中前面的return会被后面的Monad抵消」是完全错误的总结,该现象本质是Haskell的函数应用规则 + 函数类型的Monad实例特性共同导致的,和所谓「抵消」没有任何关系。
核心前提:函数应用的优先级与结合性
Haskell中所有函数默认是柯里化的,函数应用为左结合,且优先级高于所有运算符。因此表达式return 1 getLine的实际结构是(return 1) getLine,而非你可能误以为的return (1 getLine)或者return 1 >> getLine:
return本身只接收1个参数,return 1已经是完整的return调用,返回一个Monad类型的值- 后续的
getLine是你额外传给return 1这个结果的参数,和return本身没有直接关系
类型检查通过的原因:函数类型的Monad实例
先看基础类型定义:
return的类型签名为Monad m => a -> m areturn 1的类型为(Monad m, Num a) => m a,这里的m可以是任意实现了Monad的类型,包括你熟悉的IO,也包括函数类型(->) r
当你把return 1作为函数调用,传入类型为IO String的getLine作为参数时,编译器会自动推导此处的m为(->) (IO String)(即入参类型为IO String的函数类型):
- 此时
return 1的类型从m a具象化为IO String -> a,也就是一个接收IO String类型参数、返回数值类型的函数 - 传入
getLine后,整个表达式的类型就是Num a => a,完全符合类型规则,所以不会报错
对应测试代码解释
Prelude> :t return 1 return 1 :: (Monad m, Num a) => m a -- 含义:return 1是任意Monad类型下的数值,m可匹配任何Monad实例
Prelude> :t return 1 getLine return 1 getLine :: Num t => t -- 含义:编译器推导m为函数类型(->) (IO String),传入getLine后返回纯数值,无Monad约束
Prelude> return 1 getLine 1 -- 含义:等价于const 1 getLine,直接返回1,getLine被丢弃不执行
getLine不执行、结果为纯值的原因
函数类型(->) r的Monad实例中,return的实现就是const:
instance Monad ((->) r) where return x = \_ -> x -- 等价于const函数
也就是说,return 1在这里就是一个忽略所有入参、永远返回1的函数。你传入的getLine只是一个被直接丢弃的参数:
- IO操作只有被纳入主程序执行流(比如作为
main的组成部分,或GHCi顶层的IO类型表达式)时才会触发执行,这里getLine只是一个普通的、未被求值的IO类型值,不会产生任何副作用 - 整个表达式的返回类型就是纯数值类型,和IO没有任何关系,自然得到的是纯值
内容的提问来源于stack exchange,提问作者cobra
相关产品推荐
相关产品推荐

