1

我正在尝试在任意类型的列表上创建一个函数,并进行一些计算,将中间结果存储在一个 STArray 中。基本上,我想做这样的事情(是的,这是一个愚蠢的例子):

import Control.Monad.ST
import Data.Array.ST

echoArray :: [a] -> [[a]]
echoArray input = runST $ do
    let n = length input
    buf <- newListArray (0, n-1) $ map (\x->[x]) input :: ST s (STArray s Int [a])
    getElems buf

但是,ghci(7.4.2 版)给出了这个惊人的错误:

x.hs:7:12:
Couldn't match type `a' with `a1'
  `a' is a rigid type variable bound by
      the type signature for echoArray :: [a] -> [[a]] at x.hs:5:1
  `a1' is a rigid type variable bound by
       an expression type signature: ST s1 (STArray s1 Int [a1])
       at x.hs:7:12
Expected type: ST s (STArray s Int [a1])
  Actual type: ST s (STArray s Int [a])
In a stmt of a 'do' block:
  buf <- newListArray (0, n - 1) $ map (\ x -> [x]) input ::
           ST s (STArray s Int [a])
In the second argument of `($)', namely
  `do { let n = length input;
        buf <- newListArray (0, n - 1) $ map (\ x -> [...]) input ::
                 ST s (STArray s Int [a]);
        getElems buf }'

如果我删除类型签名(“:: ST s...”),我仍然会得到一个不同的错误:

x.hs:7:12:
No instance for (MArray a0 [a] (ST s))
  arising from a use of `newListArray'
Possible fix:
  add an instance declaration for (MArray a0 [a] (ST s))
In the expression: newListArray (0, n - 1)
In a stmt of a 'do' block:
  buf <- newListArray (0, n - 1) $ map (\ x -> [x]) input
In the second argument of `($)', namely
  `do { let n = length input;
        buf <- newListArray (0, n - 1) $ map (\ x -> [x]) input;
        getElems buf }'

如果我改为将出现的三个“a”更改为 Char,那么我当然可以编译它。但我想要一个可用于 [Int]、[Char]、[Int->Int] 或其他的通用函数。

我该怎么做?谢谢!

4

2 回答 2

3

这里的问题是该行中的类型变量实际上与该a行中的不同!您可以通过打开并写入来解决此问题buf <-aechoArray :: [a] -> [[a]]ScopedTypeVariables

echoArray :: forall a. [a] -> [[a]]

这将aechoArray.

于 2013-10-13T20:02:32.500 回答
0

buf所以基本上,你希望你可以声明as的右手边ST s (STArray s Int [a]),但如果你这样做,那么a将是一个新的类型变量,它独立aechoArray. 但是你希望它和那个一样a

ScopedTypeVariables正如@DanielWagner 所示,您可以使用。

但是有一种方法可以在不使用ScopedTypeVariablesor的情况下做到这一点forall

函数的签名允许您建立参数和结果类型之间的关系。因此,不要使用签名来约束“结果”上的类型,而是使用签名来约束函数上的类型,该函数的参数以某种方式也包含 type a。然后让类型推断建立连接。这是您的情况的一种解决方案:

echoArray :: [a] -> [[a]]
echoArray input = runST $ do
    let n = length input
    buf <- newSTListArray (0, n-1) $ map (\x->[x]) input
    getElems buf
  where newSTListArray :: (Ix i) => (i,i) -> [a] -> ST s (STArray s i a)
        newSTListArray = newListArray

你可以看到:

  1. newSTListArray被简单地定义为newListArray,因此从计算的角度来看它是无用的。但是,它用于使类型更具体。
  2. 没有ScopedTypeVariables或被forall使用。的ain 签名newSTListArray没有显式地与 的ain 签名绑定echoArray,但是通过推理强制它是相同的。这有效而您的代码无效的原因是a您的签名中的 in 是单独的,而这里a结果类型中的ain 与参数类型中的 in 相关联。
于 2013-10-15T09:38:48.400 回答