我理解使用"普通"函数,我可以从where子句中定义的函数中访问顶层的参数.但是,当我尝试使用monadic函数时,我遇到编译时错误.
这有效:
module Main where
import Control.Monad.ST (ST, runST)
import Data.Array.ST (STUArray, newArray, readArray)
main :: IO ()
main = do
print $ runST $ do
arr <- newArray (0, 9) 0
checkArr arr
return ()
checkArr :: STUArray s Int Int -> ST s Bool
checkArr arr = do
val <- readArr arr
return $ val == 0
where
readArr :: STUArray s Int Int -> ST s Int
readArr arr = readArray arr 0
Run Code Online (Sandbox Code Playgroud)
但这不是:
module Main where
import Control.Monad.ST (ST, runST)
import Data.Array.ST (STUArray, newArray, readArray)
main :: IO ()
main = do
print $ runST $ do
arr <- newArray (0, 9) 0
checkArr arr
return ()
checkArr :: STUArray s Int Int -> ST s Bool
checkArr arr = do
val <- readArr
return $ val == 0
where
readArr :: ST s Int
readArr = readArray arr 0
Run Code Online (Sandbox Code Playgroud)
它会导致以下错误:
No instance for (Data.Array.Base.MArray (STUArray s) Int (ST s1)) arising from a use of ‘readArray’
Run Code Online (Sandbox Code Playgroud)
为什么我不能访问in 的arr参数?checkArrreadArr
首先请注意,错误消息说明缺少实例声明而不是未知变量名称.此外,作为第一个提示,请注意错误消息包含两个不同的ST标记:s来自STUArray(与arr您的函数的参数相关),另一个s1在ST s1.它s1来自哪里?的确,你的程序中没有提到它!
作为第二个提示,尝试删除签名声明readArr,以便该where子句看起来像
where
readArr = readArray arr 0
Run Code Online (Sandbox Code Playgroud)
突然间,typechecks和工作正常.这是怎么回事?
答案是,当你写作时readArr :: ST s Int,你的意思是s来自checkArr签名,但是范围规则是这样的,这实际上引入了一个新的类型变量,typechecker为你重命名s1,并且s不一定匹配s1,因此类型错误.如果省略此签名,则类型检查器能够自己派生正确的类型.
如果你真的想在这样的where-binding中编写类型,那就有了一个扩展!ScopedTypeVariables允许您从函数中引用自由变量(s来自checkArr签名).所以,添加一个{-# LANGUAGE ScopedTypeVariables #-}并让typechecker知道你想通过forall在顶级签名中写一个显式来限定它:checkArr :: forall s. STUArray s Int Int -> ST s Bool.之后,您的代码将进行类型检查.