generated at
Genericで生成された型を見る
GHC.Genericsは代数データ型を抽象化したような構造を与える
つまり、「代数データ型」をASTで表現したようなものである
具体的に見ていくとわかりやすい


例としてこういう型を考える
hs
data Hoge a = H2 Int a | H1 Bool | H0
直和、直積、Unitが含まれていて、かつシンプルなので例に良い


Haskellに比べて、PureScriptのData.Generic.Repの定義の方がシンプルなので、先に見ておくとイメージが掴みやすい
例としてこういう型を考える
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)
ddump-derivoprionを付けてghciを起動する
$ stack ghci --ghci-options -ddump-deriv
読み込む
> :l src/Fuga/Hoge.hs
するとバババっと出力される


実際の出力からPackage名を省略して軽く整形したもの
hs
type 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))
この時点では理解する必要はないmrsekut
以下の省略の過程も別に重要ではない
まだノイズが多いので更に省略していく
型のエイリアスを展開する
コレの意味については後述
hs
type D1 = M1 D type C1 = M1 C type S1 = M1 S type Rec0 = K1 R
D , C , S のあとの冗長な部分を適当に命名する
表記はこれに合わせている



かなり削ぎ落とした結果こうなる
hs
type 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よりやや複雑だが大まかには同じことやっているのがわかる




他の例
ここに載っているやつ
これが
hs
data UserTree a = Leaf | Node a (UserTree a) (UserTree a)
こう
hs
type 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
S : Record Selector #??
上では途中で省略しているが、 M1 S <ここ> (K1 R Int) <ここ> の部分に、
コンストラクタ名や定義されている場所のようなメタ情報が含まれているのがわかる
('MetaData "Hoge" "Fuga.Hoge" "main" 'False) は、それぞれ
型名、module名、(mainは知らん)、newtypeかどうかを表す
newtypeならTrue, dataならFalseになる
K1
値コンストラクタの引数に当たる型を表す
docsにkindが * とあるが、値コンストラクタの引数がそもそも * しかなくない?
この説明不要では?と思うmrsekut
上の例では、 H2 Int a Int a がこれになっているのがわかる
一方で、 H0 は引数部分ではないので、 * だが、 K1 で表されていないこともわかる
ここには K1 P があるけどdeprecatedになった
他のものについては、GHC.Genericsの提供する型を見るに書いた