8

Haskell パッケージ Data.List.Class のソース コードを読み取ろうとしています。(リスト-0.4.2)。しかし、私はいくつかの構文で立ち往生しています。

冒頭に、次のように書かれています。

data ListItem l a =
    Nil |
    Cons { headL :: a, tailL :: l a }

3行目の構文に慣れていません。Cons a (l a)この最後の行は??と同等だと思います。しかし、よくわかりません。ファイルのヘッダーに次のように記載されていることに気付きました: {-# LANGUAGE FlexibleContexts, TypeFamilies #-}.

次に、私が理解できなかったtypeステートメントの奇妙な使用法があります: 。type ItemM l :: * -> *

Data.List.Class
-- | A class for list types. Every list has an underlying monad.
class (MonadPlus l, Monad (ItemM l)) => List l where
    type ItemM l :: * -> *
    runList :: l a -> ItemM l (ListItem l a)
    joinL :: ItemM l (l a) -> l a
    cons :: a -> l a -> l a
    cons = mplus . return

これらの意味を説明できる人はいますか? 私は Data.List を完全に理解していますが、この型クラスのことは私にはよくわかりません。また、Data.List.{Class,Tree} を使用するための wiki、例、および/またはチュートリアルについて検索しましたが、コードに付属するコメントを除いて、何もないようです。ここに何かポインタはありますか?

ありがとう。

-- 更新 -- 最初の回答 (@Chris) は、Kind 署名とレコード構文を理解するのに役立ちました。これは非常に役立ちます。ただし、リストの動作をどのようにキャプチャ/定義するか、および使い慣れた Data.List 定義にどのような値を追加するかという点で、そのコード全体を理解することはまだできません。インスタンスステートメントが 2 つしかない場合の詳細を次に示します。また、このIdentity用語は から来ていimport Data.Functor.Identity (Identity(..))ます。私たちが通常知っているリストの特徴を捉えるために、型クラスが何をするのか説明していただけませんか? 繰り返しますが、オンラインで検索しましたが、コード自体以外に Data.List.Class のドキュメントは実際にはありません。誰でも知っていますか?

また、typeこの例にあるものと同様の型クラス制約でのステートメントの別の使用例はありますか? Learnyouahaskell.com/ (@Landei) を検索しましたが、そのような例は見つかりませんでした。hereの使用法は、C++ テンプレートで 's をtype使用して「型の関数」を定義する方法と似ていると思いますよね?typedef

再度、感謝します。

instance List [] where
    type ItemM [] = Identity
    runList [] = Identity Nil
    runList (x:xs) = Identity $ Cons x xs
    joinL = runIdentity
    cons = (:)

instance Functor m => Functor (ListItem m) where
    fmap _ Nil = Nil
    fmap func (Cons x xs) = Cons (func x) (fmap func xs)
4

1 に答える 1

9

レコード構文

これ

data ListItem l a = Nil | Cons { headL :: a, tailL :: l a }

をレコード構文と呼びます。構造が入力した場合と同じであると推測した場合、あなたは正しいです

data ListItem l a = Nil | Cons a (l a)

ただし、次の 2 つのアクセサー関数も取得します。

headL :: ListItem l a -> a
headL (Cons a _) = a

tailL :: ListItem l a -> l a
tailL (Cons _ as) = as

レコード構文は構文糖衣です。ここでは、約 4 行のコードを節約できます。この段落のすぐ上のコードのように、通常の方法でパターン マッチを行うか、パターン マッチでレコード構文を使用することができます。

safeHeadL :: ListItem l a -> Maybe a
safeHeadL Nil                = Nothing
safeHeadL (Cons {headL = a}) = Just a

繰り返しますが、これは標準パターン マッチングに脱糖されます。

親切な署名

これ

class (MonadPlus l, Monad (ItemM l)) => List l where
  type ItemM l :: * -> *
  runList :: l a -> ItemM l (ListItem l a)
  joinL   :: ItemM l (l a) -> l a

  cons :: a -> l a -> l a
  cons = mplus . return

型ファミリー宣言です。この線

  type ItemM l :: * -> *

親切な署名です。何かが kind を持つと言うとき、それがor*のような基本型であることを意味します。何かが種類を持っているということは、それが型コンストラクターであることを意味します。つまり、型を取り、別の型を返します。IntFloat* -> *

型コンストラクターには、次のMaybe種類のシグネチャがあります。

Maybe :: * -> *

Maybeそれ自体はタイプではないことに注意してください。型を指定する必要があり、型を返します。それを与えるとInt、あなたは得るMaybe Int。それを与えるとDouble、あなたは得るMaybe Double

型コンストラクターItemM lは ( kind の) 型パラメーターを取り、 kind*の何かを返します*lは親切な*ので、あなたが持っていることに注意してください

ItemM :: * -> * -> *

つまりItemM、2 つの型を取り、1 つの型を返します (同様に、1 つの型を取り、単項型コンストラクタを返します)。

クラスに型宣言を含めることにより、クラスのすべてのインスタンスで、linがinItemM lと一致する必要があるという制約を課します。これらが一致しないクラスのインスタンスを作成することは不可能です。lList l

于 2012-08-02T04:30:58.463 に答える