なんでも比較出来たよー −> ザ・車輪 −> /(^o^)\

コンストラクタを幾つも持つデータ型を比較したくなった.
例えばこんなの.

:: Data = ABC | DEF | GHI | JKL | MNO

これを真面目にOrdクラスに入れようとすると,
延々とコンストラクタの組み合わせを列挙して大小関係(?)を定義する必要があるです.

instance Ord Data
where
    (<) ABC ABC = False
    (<) ABC _   = True
    (<) DEF ABC = False
    (<) DEF DEF = False
    (<) DEF _   = True
    ... 以下略

途中でイヤになったので解決策を考える.

…「!」
Genericsを使えばどんな型でも適当に比較できるハズ!
ということで作りました…?

generic gLess a  :: a a -> Bool

gLess{|UNIT|} _ _                               = False
gLess{|PAIR|} fx fy (PAIR x1 y1) (PAIR x2 y2)   = fx x1 x2 || (not (fx x2 x1) && fy y1 y2)
gLess{|EITHER|} fl fr (RIGHT x)  (RIGHT y)      = fr x y
gLess{|EITHER|} fl fr (LEFT x)   (RIGHT y)      = True
gLess{|EITHER|} fl fr (RIGHT x)  (LEFT y)       = False
gLess{|CONS|}   f     (CONS x)   (CONS y)       = f x y
gLess{|OBJECT|} f     (OBJECT x) (OBJECT y)     = f x y
gLess{|FIELD|}  f     (FIELD x)  (FIELD y)      = f x y
gLess{|Int|}    x y                             = x < y
gLess{|Char|} x y                               = x < y
gLess{|Bool|} False y                           = y
gLess{|Bool|} x y                               = False
gLess{|Real|} x y                               = x < y
gLess{|String|} x y                             = x < y

(-<-) infix 4 :: !a !a -> Bool | gLess{|*|} a
(-<-) x y = gLess{|*|} x y

使ってみる.

derive gLess Data
Start = sortBy (-<-) [ABC, GHI, MNO, DEF, JKL, GHI, JKL, MNO, ABC]

// > [ABC,ABC,DEF,GHI,GHI,JKL,JKL,MNO,MNO]

このように, コンストラクタの定義が左にあるほど小さくなる(gLess{|EITHER|}のところ)関係が定義出来ました.
Dataのコンストラクタを入れ替えるとsort結果も入れ替わります.
素晴らしい!

で, 悦に入っていたワケですが,
実はGenericsではレコード型のフィールド名にもアクセスできるという反則ワザを備えています.
これを使えば,コンストラクタを辞書順でソートとかも出来るんじゃないかと思い始めて泥沼に嵌りました.*1


救いを求めてライブラリやExampleを漁っていると,見つけてはいけない物を発見….
Gast/genLibTest.icl この中になんと!gLessの全く同じ定義が…/(^o^)\ *2

GastというのはCleanの自動テストシステムです.
で, なんでこんなとこにgLessの定義があるんですか!!??(^^);;;

まとめ

Cleanはいろんなところに定義を書き散らかし過ぎです!!
多分凄い人が作ってるんだと思いますけど, 凡人用にファイル分けるぐらいしてくれてもいいじゃないですかっ!?

*1:複数(2つ)の引数を取るジェネリックス関数の引数のフィールド名にアクセスする方法が分かりません><

*2:上に書いた(-<-)の定義はそこから持ってきました