Genericで生成された型を見る
つまり、「代数データ型」をASTで表現したようなものである
具体的に見ていくとわかりやすい
例としてこういう型を考える
code:hs
data Hoge a = H2 Int a | H1 Bool | H0
直和、直積、Unitが含まれていて、かつシンプルなので例に良い
例としてこういう型を考える
code:purs(hs)
data Hoge a = H2 Int a | H1 Bool | H0
Genericによって以下のような型が構成される
code: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で見る方法
こういう定義を書いておく
code: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名を省略して軽く整形したもの
code: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.icon
以下の省略の過程も別に重要ではない
まだノイズが多いので更に省略していく
型のエイリアスを展開する
コレの意味については後述
code:hs
type D1 = M1 D
type C1 = M1 C
type S1 = M1 S
type Rec0 = K1 R
D, C, Sのあとの冗長な部分を適当に命名する
かなり削ぎ落とした結果こうなる
code: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))
元のやつとの対応
https://gyazo.com/ca69a4cd33ee80be7aabad28ecfc047a
pursよりやや複雑だが大まかには同じことやっているのがわかる
他の例
これが
code:hs
data UserTree a = Leaf | Node a (UserTree a) (UserTree a)
こう
code: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
上では途中で省略しているが、M1 S <ここ> (K1 R Int)の<ここ>の部分に、
コンストラクタ名や定義されている場所のようなメタ情報が含まれているのがわかる
('MetaData "Hoge" "Fuga.Hoge" "main" 'False)は、それぞれ
型名、module名、(mainは知らん)、newtypeかどうかを表す
newtypeならTrue, dataならFalseになる
K1
値コンストラクタの引数に当たる型を表す
docsにkindが*とあるが、値コンストラクタの引数がそもそも*しかなくない?
この説明不要では?と思うmrsekut.icon
上の例では、H2 Int aのIntやaがこれになっているのがわかる
一方で、H0は引数部分ではないので、*だが、K1で表されていないこともわかる
ここにはK1 Pがあるけどdeprecatedになった