是否可以对更高类型的 class 实例强制执行类型约束?

Is it possible to enforce a type constraint on a class instance for a higher-kinded type?

我有一个这样定义的类型:

newtype PrimeSet a = P Integer
    deriving Eq

我还定义了一个将素数集转换为列表的函数,假设其类型参数是 Integral

toList :: Integral a => PrimeSet a -> [a]

我现在要给 PrimeSet 一个 Foldable 实例,所以这是我的第一次尝试(在从 Data.Foldable 导入 fold 之后):

instance Foldable PrimeSet where
    foldMap f = fold . map f . toList

然而,这没有用,编译器告诉我它 Could not deduce (Integral a) arising from a use of ‘toList’。我对此消息的理解是 toList 要求其参数为 Integral a => PrimeSet a 类型,但在 Foldable 实例中不一定如此。

该消息还说,一个可能的修复方法是将 Integral a 添加到我的 foldMap 实现的类型签名的上下文中,但当然我随后被告知我不是允许为 class 方法提供我自己的类型定义,除非我使用 InstanceSigs,所以我试过了,但似乎也没有用。

所以我的问题是:如果我正在为其编写 class 实例的类型的类型参数被隐藏,是否可以向 class 实例添加类型约束 - 或者,重申一下,我可以做这样的事情吗?

instance (Integral a) => Foldable (PrimeSet a) where

(这当然不起作用,因为 PrimeSet a 具有 * 类型,而 Foldable 需要 * -> *

不,这是不可能的。高级类型的全部意义在于处理 any 参数类型。而 PrimeSet 根本不是真正的参数化——基本上,它是 always PrimeSet Integer。为什么你有那个 a 参数?

然而,对于“有点容器”的类型有不同的 class,但对于任意类型则没有:MonoTraversable, or actually MonoFoldable 在这种情况下。

{-# LANGUAGE FlexibleInstances, TypeFamilies #-}

import Data.MonoTraversable

type instance Element (PrimeSet a) = a
-- or, if `PrimeSet` is not parameterised,
-- type instance Element PrimeSet = Integer

instance (Integral a) => MonoFoldable (PrimeSet a) where
  otoList = YourImplementation.toList

另一种方法是您确实使用参数化类型,实际上是仿函数,但不属于 all 的正常 Hask 类别Haskell 类型,但仅在类型s Integer 的子类别中。我有这么一个classin my constrained-categories package。但是,尤其是对于你这种类型,这似乎真的没有任何意义。

可以使用GADT来约束参数类型:

{-# LANGUAGE GADTs #-}

data PrimeSet a where
    PrimeSet :: Integral a => Integer -> PrimeSet a

instance Foldable PrimeSet where
    foldr f b (PrimeSet x) = f (fromInteger x) b

您可以使用 ConstraintKinds(以及可折叠的 class)进行一些概括:

data Monomorphic f c a where
    Monomorphic :: c a => f -> Monomorphic f c a

instance (item ~ Item f, MonoFoldable f) => Foldable (Monomorphic f ((~) item)) where
    foldr f b (Monomorphic xs) = ofoldr f b xs

data PrimeSet = PrimeSet Integer

instance Foldable (Monomorphic PrimeSet Integral) where
    foldr f b (Monomorphic (PrimeSet i)) = f (fromInteger i) b