哪些语言扩展可以让我们编写 "class A (B c) => D c where ..."?这个类型类声明的含义是什么?

4

我正在尝试理解以下类声明:

class (MonadHold t (PushM t), MonadSample t (PullM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
  data Behavior t :: * -> *

这是在 Reflex FRP 库中的代码。

1) 什么语言扩展可以使得写出 Functor (Behavior t) => Reflex t ? 我猜测这涉及到多个语言扩展。我猜测类型族将会用到,但还有什么?

换句话说,为了使包含 class A (B c) => D c where ... 的代码编译通过,需要开启哪些语言扩展?

2) class A (B c) => D c where ... 的意思是什么?

3) 能否给一个超级简单的例子解释一下何时以及为什么需要编写class A (B c) => D c where ...

4) 在哪里描述了 class A (B c) => D c where ... 的含义?我猜SPJ论文或演讲中有描述,请指出相关内容。

编辑:

进一步相关信息:

这里 写道:

在 Haskell 98 中,类声明的上下文(引入父类)必须是简单的;也就是说,每个谓词必须由应用于类型变量的类组成。

如果我正确理解了以上引述,则在 Haskell98 中只允许 class A b=>C b where 形式的类声明,这意味着不允许 class A(B c) => D c。那么问题来了,如果后者允许,前者不允许,那么谁和什么定义了后者的含义?后者不是正确的 Haskell 98 语法,因此其含义在任何Haskell 98书籍中都没有描述,那么后者的含义在哪里被描述/记录/指定?

3个回答

5

首先,正如你正确猜测的那样,Behaviour是一个相关数据类型,所以属于TypeFamilies扩展。

class Functor (Behaviour t) => Reflex t where data Behaviour t :: * -> * 的目的是强制要求对于任何一个被定义为 Reflex 实例的类型 t,无论你如何定义与其关联的数据类型 Behaviour t,你都必须将该类型定义为 Functor 的实例。

下面是一个小例子:假设我有这个类定义:

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}

class Functor (F a) => Funky a where
    data F a :: * -> *

这样可以写出类似于以下的内容:
frobulate :: (Funky a) => F a Int -> F a Bool
frobulate = fmap (< 5)

由于frobulate可以从Funky a中自由推断出Functor(F a),因此:

仅有以下代码会被类型检查器拒绝:

instance Funky Int where
    data F Int a = MkF a

因为关联的数据类型F Int不是Functor的实例:

No instance for (Functor (F Int))
  arising from the superclasses of an instance declaration
In the instance declaration for `Funky Int'

这会强制你添加一个实例定义,例如:

instance Functor (F Int) where
    fmap f (MkF x) = MkF (f x)

注意后面的Functor实例需要打开FlexibleInstances选项。

非常感谢,"类Functor(行为t)=> Reflex t where data Behaviour t :: * -> *"的要点是强制要求对于任何类型t,它是Reflex的一个实例,无论您定义与数据类型行为t相关联的任何数据类型,您也必须将该类型变成Functor的一个实例。"这个定义在哪里?你是怎么知道的?SPJ的哪篇论文描述了这个? - jhegedus
1
@jhegedus 这只是普通的类型类约束语法。当涉及到类型族时,它的含义并没有真正改变(类型族只是增加了你可以在约束中提到的更多种类的东西)。class Functor (F a) => Funky a 只是意味着对于声明为 Funky 类的所有实例,必须存在一个 F aFunctor 实例(对于相同的 a)。这与 class Eq a => Ord a 的含义没有任何区别,后者意味着对于所有 Ord 的实例,必须存在一个 Eq 的实例。 - Ben
@Ben,你是怎么知道以下内容的:“class Functor(F a)=> Funky a只是意味着对于为Funky类声明的所有实例,必须存在F a的Functor实例(对于相同的a)”?这个规则来自哪里?我认为这个规则在Haskell98的教科书中没有描述。那么它在哪里被描述了?换句话说,你是怎么知道“class Functor(F a)=> Funky a”的含义的?你有在其他地方读到过吗?如果有,那在哪里? - jhegedus
@jhegedus 我现在想不起来了。但你肯定可以写出像 class Eq a => Eq (Maybe a) 这样的代码。无论是不是 Haskell 98,用 class Eq (Maybe a) => Eq a 来表达并没有涉及到任何新概念。 - Ben
1
@jhegedus 我不确定你能在使用FlexibleContexts时找到更多关于特定类型类约束的含义; 即使是GHC文档中对于FlexibleContexts扩展的描述(你引用并链接了它),也没有详细说明这些约束意味着什么,只是说明了现在允许的约束形式。原因是这些约束仍然意味着与以前完全相同的事情(“当你使用我声明的东西时,必须有一个匹配此约束的实例”),只是可以使用更灵活的规则来定义这些约束。 - Ben
显示剩余8条评论

3
针对第四部分,Haskell Report指出:

假设实例类型 (T u1 … uk) 中的类型变量符合实例上下文 cx′ 中的限制。在此假设下,还必须满足以下两个条件:

  1. C 的超类上下文 cx[(T u1 … uk)/u] 所表示的限制也必须满足。换句话说,T 必须是每个超类的一个实例,并且所有超类实例的上下文都必须被包含在 cx′ 中。
  2. 还必须满足实例类型中类型变量的任何限制,这些限制是为了使类方法声明具有良好的类型而需要的。

这段讨论中没有假设超类上下文是报告其他地方要求的简化形式;因此,它可以(并且确实)作为更复杂的超类上下文的含义。


这非常有趣。非常感谢Daniel,我会更新我的答案。 - jhegedus

0

以下是我对这些答案的理解。我花了一些时间才理解,因此我把它写下来,或许能帮到其他人。

传统的类型类声明(Haskell 98)class (Eq a) => MyClass a where表示如果aMyClass的实例,那么a必须是Eq的实例。左侧的Eq a(表示aEq的实例)是一个谓词,如果右侧MyClass a(表示aMyClass的实例)为真,则该谓词也必须为真。

因此,换句话说,类声明就像class ($Predicate) => MyClass classInstance,其中$Predicate可以是例如Eq a,这意味着aEq的实例。

但是,如果启用了FlexibleContexts语言扩展,则谓词可以比ATypeClass typeParameter更复杂。

它可以是ATypeClass SomeComplexType,这意味着 - 通过将Haskell 98的规则泛化SomeComplexTypeATypeClass的一个实例。

这个泛化过程在Haskell报告中隐含地描述(如Daniel的答案所解释的那样)。

以下三个简短的程序演示了这个泛化规则。第一个编译第二个不编译,第三个再次编译。

第二个无法编译,因为Smells不是IntLike类声明中所需的StringLike。然而,如果我们删除该约束条件(在第三个程序中),则程序再次编译。

这里有一个玩具示例,可以编译:

{-# LANGUAGE FlexibleContexts #-}


class StringLike a where
 getString :: a->String

class (StringLike Smells) =>IntLike a where
 getInt :: a-> Int

instance IntLike Colors where
  getInt x = 0

instance StringLike Colors where
 getString x = "Hello"

instance StringLike Smells where
 getString x = "Bad"

data Colors = Blue | Red
data Smells = Rose | Trash

以下操作失败了:

{-# LANGUAGE FlexibleContexts #-}


class StringLike a where
 getString :: a->String

class (StringLike Smells) =>IntLike a where
 getInt :: a-> Int

instance IntLike Colors where
  getInt x = 0

instance StringLike Colors where
 getString x = "Hello"


data Colors = Blue | Red
data Smells = Rose | Trash

附带错误信息:

test.hs:10:10:
    No instance for (StringLike Smells)
      arising from the superclasses of an instance declaration
    In the instance declaration for `IntLike Colors'
Failed, modules loaded: none.

然而,以下的代码可以正常编译:

{-# LANGUAGE FlexibleContexts #-}


class StringLike a where
 getString :: a->String

class (StringLike Smells) =>IntLike a where
 getInt :: a-> Int


instance StringLike Colors where
 getString x = "Hello"


data Colors = Blue | Red
data Smells = Rose | Trash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接