PureScript中do notation使用困惑:等价改写为何触发类型错误?
PureScript中do块使用
lines函数的类型错误问题解析 问题重现
第一段可正常运行的代码:
module Main where import Prelude import Data.String.Common (joinWith) import Data.String.Utils (lines) import Effect (Effect) import Effect.Console (log) contents :: String contents = "1 2\n3 4" l :: Array String l = lines contents main :: Effect Unit main = do log (joinWith "\n" l)
将l移至do块内使用l <- lines contents改写后,出现类型错误:
module Main where import Prelude import Data.String.Common (joinWith) import Data.String.Utils (lines) import Effect (Effect) import Effect.Console (log) contents :: String contents = "1 2\n3 4" main = do l <- lines contents log (joinWith "\n" l)
编译器报错:
Could not match type String with type Array String while checking that type String is at least as general as type Array String while checking that expression l has type Array String in value declaration main
错误原因
核心问题在于do块里的<-符号的用途:它是用来提取monadic上下文(比如Effect、Maybe这类容器类型)内部的值,要求右侧的表达式必须是m a类型(比如Effect String),而非纯值类型。
lines是纯函数,类型为String -> Array String,接收字符串后直接返回Array String纯值,没有任何monadic上下文。但main的类型是Effect Unit,do块会强制要求<-右侧的表达式属于Effect类型,因此编译器会强行把lines contents的类型推断为Effect String——这就产生了矛盾:lines实际返回的是Array String,却被编译器当成了Effect String,通过<-提取出的l便被推断为String类型,而后续joinWith需要的是Array String,自然出现类型不匹配错误。
修正方案
因为lines是纯函数,不需要monadic绑定,直接在do块里用let声明变量即可:
module Main where import Prelude import Data.String.Common (joinWith) import Data.String.Utils (lines) import Effect (Effect) import Effect.Console (log) contents :: String contents = "1 2\n3 4" main = do let l = lines contents log (joinWith "\n" l)
这样l的类型会被正确推断为Array String,代码可正常运行。
内容的提问来源于stack exchange,提问作者SolarBear
相关产品推荐
相关产品推荐

