我有大量类型
的vector函数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
注释掉LINE 1会导致第2行出错:
Ambiguous type variable `m0' in the constraint:
(PrimMonad m0) arising from a use of `applyFun'
我尝试了各种显式输入(使用ScopedTypeVariables,显式foralls等),但还没有找到修复第一个错误的方法。对于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
这显然是不能令人满意的:我必须创建一个假的类,使用更加无意义的方法,其唯一的工作就是演示类参数的种类。然后我为所有内容创建了一个通用实例,这样我就可以在m
的范围内拥有topLevelFun
,这样我就可以添加约束并转换f
。 GOT是更好的方式。
我可能会在这里做各种各样的错误,所以任何建议都会有所帮助。
答案 0 :(得分:1)
applyFunc
的以下类型是否适合您?
applyFunc :: (Vector v a) =>
(forall s. Mutable v s a -> Mutable v s a -> ST s ())
-> v a -> v a
只要您拥有Rank2Types
扩展名,就应该编译没有问题,因为您使用的函数必须处理所有 ST monads。原因是runST
的类型为(forall s. ST s a) -> a
,因此runST
之后的代码正文需要适用于所有s
,因此需要g
适用于所有s
。
(你可以改为使用一个适用于所有PrimMonads
的函数,但这些函数的数量要少得多。
GHC无法推断出更高等级的类型。有很好的理由不推断RankNTypes
(它是不可判定的),尽管Rank2
在理论上是可推断的,但GHC人员决定“只推断原则类型是否为Hindley” -Milner类型“对于像我这样的人很容易推理,并使编译器编写者的工作不那么难。
在评论中你要问一个元组。具有多态类型的元组需要ImpredicativeTypes
,并且可以像
applyFuncInt :: (Vector v a) =>
((forall s. Mutable v s a -> Mutable v s a -> ST s ()),Int)
-> v a -> v a
applyFuncInt (g,_) x = runST $ do
y <- V.thaw x
g y y
V.unsafeFreeze y
尽管通常最好将数字作为单独的参数传递。