我有大量现成的向量函数,类型如下:
f :: (M.MVector v r, PrimMonad m) =>
v (PrimState m) r -> v (PrimState m) r -> m ()
这些函数大多数是原地操作的,因此将其参数设置为可变向量非常方便,这样我就可以进行组合、迭代等操作。但是,在顶层,我只想使用不可变的“Haskell”/纯向量来操作。
以下是一个示例:
{-# LANGUAGE TypeFamilies,
ScopedTypeVariables,
MultiParamTypeClasses,
FlexibleInstances #-}
import Data.Vector.Generic as V hiding (eq)
import Data.Vector.Generic.Mutable as M
import Control.Monad.ST
import Control.Monad.Primitive
f :: (M.MVector v r, PrimMonad m) =>
v (PrimState m) r -> v (PrimState m) r -> m ()
f vIn vOut = do val <- M.read vIn 0
M.write vOut 0 val
applyFunc :: (M.MVector v r, PrimMonad m, V.Vector v' r, v ~ Mutable v') =>
(v (PrimState m) r -> v (PrimState m) r -> m ()) -> v' r -> v' r
applyFunc g x = runST $ do
y <- V.thaw x
g y y -- LINE 1
V.unsafeFreeze y
topLevelFun :: (V.Vector v r) => r -> v r
topLevelFun a =
let x = V.replicate 10 a
in applyFunc f x -- LINE 2
代码如此编写,会在第1行产生错误:
Could not deduce (m ~ ST s)
Expected type: ST s ()
Actual type: m ()
in the return type of g, LINE 1
注释掉第1行会导致第2行出错:
Ambiguous type variable `m0' in the constraint:
(PrimMonad m0) arising from a use of `applyFun'
我尝试过多种明确的类型(使用ScopedTypeVariables、显式的forall等),但没有找到修复第一个错误的方法。对于LINE 1的错误,似乎应该将m
推断为ST s
,因为我在runST
中。
对于LINE 2的错误(LINE 1已注释掉),我唯一想到的解决办法是:
class Fake m v where
kindSig :: m a -> v b c
instance Fake m v
topLevelFun :: forall m v v' r . (V.Vector v' r, M.MVector v r, PrimMonad m, Fake m v, v ~ Mutable v') => r -> v' r
topLevelFun a =
let x = V.replicate 10 a
in applyFunc (f::Transform m v r) x -- LINE 2
这显然是不令人满意的:我必须创建一个假类,具有更加无意义的方法,其唯一的工作是展示类参数的类型。然后我为所有内容创建一个通用实例,以便我可以在
topLevelFun
中使用m
,从而可以添加约束并强制转换f
。肯定有更好的方法。我可能在这里做了各种错误的事情,因此任何建议都将是有帮助的。
applyFunc
承诺适用于调用者选择的任何PrimMonad m
,但是runST $ do ...
强制m
为ST s
。你可以通过声明g
适用于你选择的任何PrimMonad m
,或者仅限制m
为ST s
来解决这个问题。这是代码。如果这确实回答了你的问题,请让我知道,我会写下一个答案。 - VitusapplyFunc
中的rank2类型应该是(forall m. (PrimMonad m) => ...)。非常感谢!最终成功引入了Rank2Types。 - crockeea.ghci
中有RankNTypes
。顺便说一下,我相信Rank2Types
正在被弃用,取而代之的是RankNTypes
。 - Vitus(M.MVector v r, V.Vector v' r, v ~ Mutable v') => (forall m . (PrimMonad m) => ((v (PrimState m) r -> v (PrimState m) r -> m ()), Int)) -> v' r -> v' r
。但是,仅仅添加这个pair(以跟踪大小)会导致新的错误couldn't match type m0 with 'ST s'
。为什么添加pair会导致这种情况?是GHC的问题吗? - crockeeaapplyFunc
的新类型是:(M.MVector v r, V.Vector v' r, v ~ Mutable v') => ((forall m . (PrimMonad m) => ((v (PrimState m) r -> v (PrimState m) r -> m ()), Int) -> v' r -> v' r
。使用ImpredicativeTypes,这可以编译。此外,如果这是一个合理的解决方案,则不再需要Rank2 / NTypes。 - crockeeaImpredicativeTypes
意味着RankNTypes
,所以你无论如何都需要这个扩展。但是,如果你想在某些结构中保留多态函数,你需要使用特定的数据类型(例如newtype I = I (forall a. a -> a)
)或者ImpredicativeTypes
。 - Vitus