4

假设我想写两个 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

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

4

3 回答 3

3

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

似乎您想要的意思是“一类与应用于其元素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数据类型,这可能会在定义中引发另一个问题。)

于 2021-10-12T08:06:12.773 回答
2

我认为这里同时发生了几个推理错误。

第一:当你写

class (forall a. Monoid (t a)) => ExClass t

这意味着如果有人想要实现一个ExClass t实例,那么他们必须证明有一个instance Monoid (t a) where ...没有任何约束的表单实例a。另请注意,仅存在用于所有可能选择的实例是不够的a——实例本身必须是参数化的。

第二:写的时候

instance (forall a. Num a) => ExClass ExType

a这里a提到的和定义中提到的没有神奇的联系ExClass

第三:写的时候

instance (forall a. Num a) => ExClass ExType

这实际上并没有成为一个实例ExClass ExType。它使一个实例以证明为条件forall a. Num a。一般来说, 的意思instance Ctx => C t是任何想假设事实的人都C t必须能够自己证明Ctx。没有人能够合理地展示forall a. Num a,所以这个实例是不可用的。

最后两条评论基本上不变地适用于您的第二次尝试,

instance (forall a. Monoid(ExType a)) => ExClass ExType

不幸的是,如果没有更多关于你想要做什么以及为什么做的细节,几乎不可能提出正确的前进路线。可能性包括类似索引单子的方法,将类约束从类上下文移动到各种方法上下文,根本不创建类等等。

于 2021-10-11T23:21:21.823 回答
1

我也想有一个默认实现exFunc,但它涉及的假设t aMonoid

你只需要Monoid (t a)为了有一个默认的实现?那是倒退。如果该约束在概念上不需要作为 的超约束ExClass,那么它不应该在类头中。

您仍然可以拥有更具限制性的默认实现:

{-# LANGUAGE DefaultSignatures #-}

class ExClass t where
  exFunc :: [t (Complex a)] -> t (Complex a)
  default exFunc :: Monoid (t (Complex a)) => [t (Complex a)] -> t (Complex a)
  exFunc = mconcat
  exFunc = mconcat -- the actual function is more complicated
  exFunc2 :: (RealFloat a) => t (Complex a) -> a

然后简单地

instance ExClass ExType where
  exFunc2 (ExType a) = magnitude a

您仍然可以提供根本不满足Monoid约束的实例,然后您也需要手动实现exFunc

更简单的是根本不关心默认实现,而只需提供一个帮助程序,可以用来获得一个简单的实现,而无需复制任何代码:

class 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

monoidIshExFunc :: Monoid (t (Complex a)) => [t (Complex a)] -> t (Complex a)
monoidIshExFunc = mconcat

instance ExClass ExType where
  exFunc = monoidIshExFunc
  exFunc2 (ExType a) = magnitude a

monoidIshExFunc甚至可能需要ExClass t,很明显你应该小心不要以循环定义结束!)

于 2021-10-12T08:26:25.110 回答