是否可以对更高类型的 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
我有一个这样定义的类型:
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