假设我想写两个类型类。
标题:
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Complex
第一个类型类 ExClass 定义如下:class (forall a. (Monoid (t a))) => ExClass t where
exFunc :: [t (Complex a)] -> t (Complex a)
exFunc = mconcat -- the actual function is more complicated
exFunc2 :: (RealFloat a) => t (Complex a) -> a
我将它定义为更高级的类型类,因为它的一个函数的输出类型取决于其嵌套值的类型 a
.我还想为 exFunc
设置一个默认实现,但它涉及假设t a
是幺半群。现在我想为以下类型编写一个实例:newtype ExType a = ExType a
ExType a
只有当 Num a
时才是幺半群是真的:instance (Num a) => Semigroup (ExType a) where
ExType a <> ExType b = ExType (a * b)
instance (Num a) => Monoid (ExType a) where
mempty = ExType 1
现在我继续为 ExClass
定义类型类实例,指定约束 Num a
:instance (forall a. Num a) => ExClass ExType where
exFunc2 (ExType a) = magnitude a
上面的代码编译没有问题。但是,如果我尝试像这样使用已实现的函数:x = ExType 2 :: ExType (Complex Double)
func = exFunc2 x
我收到以下投诉:• No instance for (Num a) arising from a use of ‘exFunc2’
Possible fix: add (Num a) to the context of a quantified context
• In the expression: exFunc2 x
In an equation for ‘func’: func = exFunc2 x
当我使用不同的实例声明时也会发生这种情况:instance (forall a. Monoid(ExType a)) => ExClass ExType where
exFunc2 (ExType a) = magnitude a
有没有办法让这个类型类工作?或者我根本不应该像这样构建我的程序?
最佳答案
Daniel Wagner 已经在他的回答中解释了当前定义的问题。
看来你要ExClass
意思是“一类容器类型与应用于其元素的另一个类 c
具有特殊关系,并且当它们的元素满足 c
时,容器本身就是幺半群”。
例如:ExType
与Num
有特殊关系.当元素a
的 ExType
满足 Num
, ExType a
变成 Monoid
.
(这已经在 ExType
s Monoid
实例中得到确认,但似乎您想用更高的抽象级别来表达它;为那些以类似方式成为幺半群的容器创建一个类。)
在 Haskell 中,有多种可能的方式来表达两种类型之间的关系——或者类型和 Constraint
之间的关系。 .让我们使用 MultiParameterTypeClasses
:
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ConstraintKinds #-}
import Data.Kind (Type, Constraint)
-- This kind signature is not strictly required, but makes things clearer
type ExClass :: (Type -> Constraint) -> (Type -> Type) -> Constraint
class (forall a . c a => Monoid (t a)) => ExClass c t | t -> c where
exFunc :: c a => [t a] -> t a
exFunc = mconcat
请注意 ExClass
现在有两个参数,即类型 f
容器的(通过函数依赖)确定约束 c
这是 f
的元素所必需的为 f
成为 Monoid
. c
可能因不同而不同 f
!Semigroup
和 Monoid
ExType
的实例不要改变。 ExClass
ExType
的实例现在是:instance ExClass Num ExType where
exFunc = mconcat
让它工作:main :: IO ()
main = print $ exFunc [ExType (1::Int), ExType 2]
(我遗漏了 Complex
数据类型,这可能会给定义带来另一个麻烦。)
关于haskell - 高级类型类的量化约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69529835/