6

ソースコードを調べて、Data.Hasそれがどのように機能するかを理解しようとしていました。次のコードは、誰かが と の両方の機能を持つ新しい値に と という 2 つの値を「結合」できるようにするためのものだと思いa :: Aます。b :: Bab

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 :++: b1 つのメソッド関数を使用して、マルチパラメーター型クラスを宣言します。また、プライオリティ 5 でライトアソシエーティブに設定します。(.++.)aba :++: b(.++.)

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

Append a b固定の第 1 パラメーター ( TyNil) と任意の第 2 パラメーター ( ) を使用して のインスタンスを宣言します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)

宣言された のインスタンスが既に存在する場合は、任意および任意の 2 番目のパラメーターAppend a bの形式で、最初のパラメーターを使用して のインスタンスを宣言します。関連付けられた型(ここでは明らかに ) は と等しいと宣言されています。メソッド定義もここで明確です。値のペアと別の値を取り、最初の要素が最初の引数と同じで、2 番目の要素がメソッドで 2 番目の引数と結合された最初の引数の 2 番目の要素である別のペアを作成します。制約により使用させていただいております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)で、それ自体は の観点から書かれてい:++:ます。

このような関連付けられた型の宣言は、 andだけで一意に決定される型 (a :++: bこの場合) があることを型システムに伝えるために必要です。つまり、タイプチェッカーが、特定の式で型が等しいことを知っている場合、たとえば、and 、および:ababIntDouble

  1. 制約がありますAppend a b
  2. Append Int Double関連付けられた型が宣言された型クラス インスタンスがあります。たとえばtype Int :++: Double = String

その場合、タイプチェッカーは、タイプに出会った場合a :++: b、実際にはこのタイプが であることを知ることができStringます。

について~は、「レイジー パターン マッチ」と呼ばれます。ここで非常に明確に説明されています

まだ不明な点がある場合はお気軽にお問い合わせください。

于 2012-08-03T07:49:10.373 に答える