Functor 实例是否可以声明为函数的附加类型限制

Can Functor instance be declared with additional type restriction for function

我正在努力将 GHC/Arr.hs 移植到 Frege。

数组定义:

data Array i e = Array{u,l::i,n::Int,elems::(JArray e)}

有功能:

amap :: (Ix i, ArrayElem e) => (a -> b) -> Array i a -> Array i b

现在,我不知道如何为它定义 Functor 实例,因为

instance (Ix i) => Functor (Array i) where
    fmap = amap

但是编译器抱怨推断的类型比预期的更受限制,这似乎是真的。我可以使 Array 成为一个对函数 ArrayElem -> ArrayElem 有限制的仿函数吗?

不,这不可能。

如果您 Array 基于 JArray 并且想要一个仿函数实例,则不得使用任何出现 ArrayElem(或任何其他附加)上下文的函数。

另一种说法是,您不能将 Array 基于类型安全的 java 数组,而必须处理 java 类型 Object[] 的数组。因为,正如您毫无疑问指出的那样,ArrayElem 类型 class 只是一个技巧,可以在创建 java 数组时提供正确的 java 类型。当然,这对于与 Java 交互和出于性能原因很重要。

请注意,类型安全 java 数组还有另一个问题。假设我们想要创建一个 Double 的数组(但相同的参数适用于任何其他元素类型)。 AFAIK,Haskell 要求 Arrays 元素必须是惰性的。因此我们真的不能使用 java 类型 double[]JArray Double 将是 Frege 的对应物)来建模它。因为,如果我们这样做,每个数组元素都必须在设置后立即求值。

出于这个原因,我建议您使用一些通用的自定义数组元素类型,例如

data AElem a = AE () a 
mkAE = A () 
unAE (AE _ x) = x
derive ArrayElement AElem

并更改您的定义:

data Array i e = Array{u,l::i,n::Int,elems::(JArray (AElem e))}

现在,你的仿函数实例可以写了,因为 ArrayElem 约束没有出现,因为当你访问 elems 数组时,编译器知道你有 AElem 个元素并且可以并且将会提供正确的实例。

此外,AElem 的构造和 AElem 的使用作为实际数组元素不会 对实际值施加严格。

不用说,Array 模块的用户不应该(不需要)知道那些实现细节,即 AElem 类型。