在类和实例声明中输入关键字

7
我正在查看Data.Has的源代码,试图弄清楚它是如何工作的。我认为下面的代码旨在允许将两个值(比如a :: Ab :: B)“Join”成一个新值,该新值具有ab的功能。
我特别不理解类声明和实例声明中type的含义。
此外,我不知道下面的~符号表示什么。
请问是否可以解释一下来自Data.Has.TypeList 的下面代码?
-- | Provides type-list functionality

module Data.Has.TypeList where

import Control.Applicative
import Data.Monoid (Monoid (..))
import Test.QuickCheck (Arbitrary (..), CoArbitrary (..))
import Data.Typeable
import Data.Data
-- | Cons a type onto type-list.
data a ::: b = a ::: b deriving (Show,Eq,Ord,Read,Bounded,Typeable,Data)

-- | The empty type-list.
data TyNil = TyNil deriving (Read,Typeable,Data)

-- | Appends a type-list and another.
class Append a b where
    type a :++: b
    (.++.) :: a -> b -> a :++: b
infixr 5 :++:

-- Implementation of Append

instance Append TyNil b where
    type TyNil :++: b = b
    _ .++. b = b

instance (Append y b) => Append (x ::: y) b where
    type (x ::: y) :++: b = x ::: (y :++: b)
    ~(x ::: y) .++. b = x ::: (y .++. b)

它们被称为关联类型别名。这些结构类似于函数依赖,但使用类型函数而不是关系。 - Jan Christiansen
1个回答

11
在类型类和实例声明中,type语法是TypeFamilies扩展的一部分。可以将类型族视为从类型到类型的函数。Haskell维基上有关于类型和数据族的详细解释(请参见链接)。
应用于类型类,类型族成为关联类型。在这方面,它们非常接近FunctionalDependencies,也就是说,它们允许无歧义的实例解析。这种需求在GHC手册中已经得到了充分的解释。
你的示例中的类型定义非常简单。:::是2元组(一对值)的另一个名称,而TyNil与单元类型()同构。
我会尝试阅读类和实例声明,以便清楚地理解它们的含义。
class Append a b where
    type a :++: b
    (.++.) :: a -> b -> a :++: b
infixr 5 :++:

声明一个多参数类型类Append a b,其中包含一个关联类型a :++: b和一个方法函数(.++.),该函数接受类型为ab的值,并生成类型为a :++: b的值。我们还将(.++.)设置为优先级为5的右结合运算符。
instance Append TyNil b where
    type TyNil :++: b = b
    _ .++. b = b

声明一个Append a b的实例,其中第一个参数(TyNil)是固定的,第二个参数(b)是任意的,关联类型a :++: b(在这种情况下是TyNil :++: b)被声明为等于b。(我不会描述方法的作用,它非常清晰明了)。
instance (Append y b) => Append (x ::: y) b where
    type (x ::: y) :++: b = x ::: (y :++: b)
    ~(x ::: y) .++. b = x ::: (y .++. b)

声明一个实例 Append a b,第一个参数形式为x ::: y,其中xy是任意的,第二个参数b也是任意的,前提条件是已经声明了一个Append y b的实例。关联类型a :++: b(显然是(x ::: y) :++: b)被声明为等于x ::: (y :++: b)。方法定义也很清晰:它接受一对值和另一个值,并构造另一对值,其中第一个元素与第一个参数中的相同,第二个元素是第一个参数中的第二个元素与第二个参数用.++.方法组合而成。我们可以使用.++.是因为有Append y b的约束。

这些是类声明和实例声明中(.++.)方法的类型签名:

(.++.) ::               a       -> b -> a :++: b
(.++.) ::               TyNil   -> b -> b
(.++.) :: Append y b => x ::: y -> b -> x ::: (y :++: b)

请注意,在每个实例中,非常抽象的a :++: b都会转变为更具体的类型。在第一种情况下,它是普通的b,而在更复杂的x ::: (y :++: b)中,它本身是用:++:编写的。
这样声明关联类型是为了告诉类型系统,有一些类型(在这种情况下是a :++: b)仅由ab唯一确定。也就是说,如果类型检查器知道在某个表达式中,ab的类型等于,比如,IntDouble,并且:
  1. 存在一个约束Append a b
  2. 存在一个类型类实例Append Int Double,其中关联类型声明为type Int :++: Double = String
那么类型检查器将知道,如果它遇到类型a :++: b,它将知道实际上这个类型是String
关于 ~,它被称为 '懒惰模式匹配'。 这里here非常清楚地解释了它。
如果还有不清楚的地方,请随时询问。

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