1

考虑 Haskell 函数

test :: ST s [Int]
test = do
    arr <- newListArray (0,9) [0..9] :: ST s (STArray s Int Int)
    let f i = do writeArray arr i (2*i)
                 readArray arr i 
    forM [1,2] f

test' :: ST s [Int]
test' = do
    arr <- newListArray (0,9) [0..9] :: ST s (STArray s Int Int)
    let f = \i -> do writeArray arr i (2*i)
                     readArray arr i
    forM [1,2] f

第一个需要 FlexibleContexts 在 ghci 8.10.1 上编译,第二个编译没有额外的选项。为什么?

s特别欢迎根据类型变量的范围解释这种行为的答案。作为后续,可以将什么(如果有)类型签名添加到函数中以在没有 FlexibleContexts的情况下f进行编译?test最后,与单态限制有联系吗?

4

1 回答 1

1

您可以f在 GHCi 中检查 GHC 分配给哪种类型:

ghci> import Data.Array
ghci> import Data.Array.MArray
ghci> let arr :: STArray s Int Int; arr = undefined
ghci> :t \i -> do writeArray arr i (2*i); readArray arr i
\i -> do writeArray arr i (2*i); readArray arr i
  :: (MArray (STArray s1) Int m, MArray (STArray s2) Int m) =>
     Int -> m Int

这比您在评论中建议的类型和FlexibleContexts需要的原因更笼统。

您可以添加您建议的类型签名 ( Int -> ST s Int) 以避免必须使用FlexibleContexts

{-# LANGUAGE ScopedTypeVariables #-}

...

test :: forall s. ST s [Int]
test = do
    arr <- newListArray (0,9) [0..9] :: ST s (STArray s Int Int)
    let 
      f :: Int -> ST s Int
      f i = do
        writeArray arr i (2*i)
        readArray arr i 
    forM [1,2] f

请注意,作用域类型变量 和 在forall s.这里是必需的,因为您需要确保s所有类型签名中的 都引用相同的类型变量,并且不要全部引入新的类型变量。

单态限制以不同方式对待您的第一个和第二个版本的原因是因为它不适用于看起来像函数的东西。在你的第一个版本f中有一个参数,所以它看起来像一个函数,因此会得到一个通用类型。在您的第二个版本f中没有参数,因此它看起来不像一个函数,这意味着单态限制迫使它具有更具体的类型。

于 2022-01-23T12:19:03.600 回答