首页 > 解决方案 > 高级类型类的量化约束

问题描述

假设我想写两个 Typeclass。标题:

{-# 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。我也希望有一个默认实现,但它涉及到MonoidexFunc的假设。t a现在我想为以下类型编写一个实例:

newtype ExType a = ExType a

ExType a仅当Num a为真时才为 Monoid:

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

有没有办法让这个类型类工作?还是我根本不应该像这样构建我的程序?

标签: haskellquantified-constraints

解决方案


丹尼尔瓦格纳已经在他的回答中解释了当前定义的问题。

似乎您想要的意思是“一类与应用于其元素ExClass的另一个类具有特殊关系的容器类型,并且当它们的元素满足时,容器本身就是幺半群”。cc

例如:ExType与 有特殊关系Num。当满足的元素,a就变成了。ExTypeNumExType aMonoid

(这已经在ExTypesMonoid实例中得到确认,但您似乎想用更高级别的抽象来表达它;为那些以类似方式成为幺半群的容器提供一个类。)

在 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容器的类型(通过函数依赖)确定了 for 元素所需的约束c为a 。不同的s可能会有所不同!ffMonoidcf

SemigroupMonoid实例ExType不变。现在的ExClass实例ExType是:

instance ExClass Num ExType where
    exFunc = mconcat

让它发挥作用:

main :: IO ()
main = print $ exFunc [ExType (1::Int), ExType 2]

(我省略了Complex数据类型,这可能会在定义中引发另一个问题。)


推荐阅读