6

我正在查看源代码Data.Has并试图弄清楚它是如何工作的。我相信下面的代码旨在允许某人“加入”两个值,比如a :: Aandb :: B成一个新值,它同时具有aand的功能b

我特别不明白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)
4

1 回答 1

9

type类型类和实例声明中的语法是TypeFamilies扩展的一部分。类型族可以被认为是从类型到类型的函数。Haskell wiki 中有关于类型和数据族的非常详细的解释(参见链接)。

应用于类型类,类型族成为关联类型。在这方面,它们非常接近FunctionalDependencies,也就是说,它们允许明确的实例解析。GHC 手册中充分说明了这一需求。

您示例中的类型定义非常简单。:::是 2-tuple(一对值)的另一个名称,与TyNilunit type 同构()

我将尝试阅读类和实例声明,以便清楚它们的含义。

class Append a b where
    type a :++: b
    (.++.) :: a -> b -> a :++: b
infixr 5 :++:

声明Append a b具有关联类型a :++: b和一个方法函数的多参数类型类,该函数(.++.)接受类型的值并产生类型aba :++: 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 ::: yxybAppend y ba :++: b(x ::: y) :++: bx ::: (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

那么类型检查器就会知道,如果他遇到 typea :++: b它就会知道这个类型实际上是String.

至于~,它被称为“惰性模式匹配”。这里解释得很清楚。

随意询问是否仍有不清楚的地方。

于 2012-08-03T07:49:10.373 回答