从Haskell

时间:2018-06-03 15:14:23

标签: haskell monads

我理解"正常"函数,我可以在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’

为什么我无法访问arrcheckArr的{​​{1}}参数?

1 个答案:

答案 0 :(得分:6)

首先请注意,错误消息说明缺少实例声明而不是未知变量名称。此外,作为第一个提示,请注意错误消息包含两个不同的s标记:STUArray来自arr(与您的函数的s1参数相关)和ST s1中的另一个s1readArr来自哪里?实际上,你的程序中没有提到它!

作为第二个提示,请尝试删除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。之后,您的代码将进行类型检查。