我正在尝试在任意类型的列表上创建一个函数,并进行一些计算,将中间结果存储在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-&gt; Int]或其他任何东西的泛型函数。
我该怎么办?谢谢!
答案 0 :(得分:3)
这里的问题是a
行中的类型变量buf <-
实际上与a
行中的echoArray :: [a] -> [[a]]
不同!您可以通过启用ScopedTypeVariables
并撰写
echoArray :: forall a. [a] -> [[a]]
将a
放在echoArray
主体内的类型级别的范围内。
答案 1 :(得分:0)
所以基本上,你希望你可以将buf
的右侧声明为ST s (STArray s Int [a])
,但如果你这样做,那么a
将是一个新的类型变量独立于a
签名中的echoArray
。但是你希望它与a
相同。
你可以使用ScopedTypeVariables
,正如@DanielWagner所示。
但是有一种方法可以在不使用ScopedTypeVariables
或forall
的情况下完成。
函数的签名允许您建立参数和结果类型之间的关系。因此,不使用签名来约束“结果”上的类型,而是使用签名来约束函数的类型,该函数的参数在某种程度上也包含类型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
你可以看到:
newSTListArray
简单地定义为newListArray
,因此从计算的角度来看它是无用的。但是,它可以使类型更具体。ScopedTypeVariables
或forall
。 a
签名中的newSTListArray
未明确与a
签名中的echoArray
绑定,但通过推理强制相同。这可行的原因和您的代码没有的原因是签名中的a
是单独的,而此处结果类型中的a
与参数类型中的a
相关联