Genericで生成された型を見る
つまり、「代数データ型」をASTで表現したようなものである
具体的に見ていくとわかりやすい
例としてこういう型を考える
hsdata Hoge a = H2 Int a | H1 Bool | H0
直和、直積、Unitが含まれていて、かつシンプルなので例に良い
例としてこういう型を考える
purs(hs)data Hoge a = H2 Int a | H1 Bool | H0
Genericによって以下のような型が構成される
purs(hs)Sum (Constructor "H2" (Product (Argument Int) (Argument a)))
(Sum (Constructor "H1" (Argument Boolean))
(Constructor "H0" NoArguments))
直積 Product a b
と、直和 Sum a b
によって構造が表現され、
Constructor name a
や、 Argument a
や、 NoArguments
によってコンストラクタなどが表現されていることがわかる
Haskellで見る方法
こういう定義を書いておく
src/Fuga/Hoge.hs{-# LANGUAGE DeriveGeneric #-}
module Fuga.Hoge where
import GHC.Generics
data Hoge a = H2 Int a | H1 Bool | H0 deriving (Generic)
$ stack ghci --ghci-options -ddump-deriv
読み込む
> :l src/Fuga/Hoge.hs
するとバババっと出力される
実際の出力からPackage名を省略して軽く整形したもの
hstype Rep (Hoge a) = D1
('MetaData "Hoge" "Fuga.Hoge" "main" 'False)
(C1
('MetaCons "H2" 'PrefixI 'False)
(S1
('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy)
(Rec0 Int)
:*: S1
('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy)
(Rec0 a))
:+: (C1
('MetaCons "H1" 'PrefixI 'False)
(S1
('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy)
(Rec0 Bool))
:+: C1
('MetaCons "H0" 'PrefixI 'False) U1))
この時点では理解する必要はない

以下の省略の過程も別に重要ではない
まだノイズが多いので更に省略していく
型のエイリアスを展開する
コレの意味については後述
hstype D1 = M1 D
type C1 = M1 C
type S1 = M1 S
type Rec0 = K1 R
D
, C
, S
のあとの冗長な部分を適当に命名する
かなり削ぎ落とした結果こうなる
hstype Rep (Hoge a) = M1 D Data_Hoge
(M1 C Con_H1 (M1 S Selector (K1 R Int)
:*: M1 S Selector (K1 R a))
:+: (M1 C Con_H1 (M1 S Selector (K1 R Bool))
:+: M1 C Con_H0 U1))
元のやつとの対応
pursよりやや複雑だが大まかには同じことやっているのがわかる
他の例
これが
hsdata UserTree a = Leaf | Node a (UserTree a) (UserTree a)
こう
hstype RealRepUserTree a = M1 D Data_UserTree (
M1 C Con_Leaf U1
:+: M1 C Con_Node ( M1 S NoSelector ( K1 P a)
:*: M1 S NoSelector (K1 R (UserTree a))
:*: M1 S NoSelector (K1 R (UserTree a))))
じゃっかん古い
例えば、 K1 P a
とあるが、この P
はdeprecatedになってる
構造がわかった上で中身を見ていく
:*:
は直積
:+:
は直和
M1
第1引数は、 D
, C
, S
がある
D
: Datatype
C
: Constructor
上では途中で省略しているが、 M1 S <ここ> (K1 R Int)
の <ここ>
の部分に、
コンストラクタ名や定義されている場所のようなメタ情報が含まれているのがわかる
('MetaData "Hoge" "Fuga.Hoge" "main" 'False)
は、それぞれ
型名、module名、(mainは知らん)、newtypeかどうかを表す
newtypeならTrue, dataならFalseになる
K1
値コンストラクタの引数に当たる型を表す
docsにkindが *
とあるが、値コンストラクタの引数がそもそも *
しかなくない?
この説明不要では?と思う

上の例では、 H2 Int a
の Int
や a
がこれになっているのがわかる
一方で、 H0
は引数部分ではないので、 *
だが、 K1
で表されていないこともわかる
ここには
K1 P
があるけどdeprecatedになった