我理解"正常"函数,我可以在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
但这不是:
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
导致以下错误:
No instance for (Data.Array.Base.MArray (STUArray s) Int (ST s1)) arising from a use of ‘readArray’
为什么我无法访问arr
中checkArr
的{{1}}参数?
答案 0 :(得分:6)
首先请注意,错误消息说明缺少实例声明而不是未知变量名称。此外,作为第一个提示,请注意错误消息包含两个不同的s
标记:STUArray
来自arr
(与您的函数的s1
参数相关)和ST s1
中的另一个s1
。 readArr
来自哪里?实际上,你的程序中没有提到它!
作为第二个提示,请尝试删除where
的签名声明,以便 where
readArr = readArray arr 0
子句看起来像
readArr :: ST s Int
突然之间,那个类型很好的工作。发生了什么事?
答案是,当你编写s
时,你的意思是来自checkArr
签名的s1
,但是范围规则是这样的,它实际上引入了一个新的类型变量, typechecker将您重命名为s
,而s1
不一定与where
匹配,因此类型错误。如果省略此签名,那么类型检查器就能够自己派生出正确的类型。
如果你真的想用s
这样的绑定来编写类型,那就有一个扩展名! ScopedTypeVariables
允许您从函数中引用自由变量(来自checkArr
签名的{-# LANGUAGE ScopedTypeVariables #-}
)。因此,添加一个forall
并让typechecker知道您希望通过在顶级签名中checkArr :: forall s. STUArray s Int Int -> ST s Bool
编写明确的Switch
来确定其范围。Regular Expressions
。之后,您的代码将进行类型检查。