# 023: lib/Prelude.hs の Ord クラス定義を完成させる [↑up](bunny_notes) - issued: 2020-04-13 - 分類: B 機能追加 - status: Closed (2020-04-15) ## 概要 lib/Prelude.hs の Ord クラス定義を完成させる。 ## 調査結果 ### 2020-04-13 現状では、以下のように一部をコメントアウトしないとエラーする。 (以下、min の型宣言を忘れているような…) $$
{ class (Eq a) => Ord a where (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>=) :: a -> a -> Bool (>) :: a -> a -> Bool {- compare :: a -> a -> Ordering max :: a -> a -> a -- Minimal complete definition: -- (<=) or compare compare x y | x == y = EQ | x <= y = LT | otherwise = GT x <= y = compare x y /= GT x < y = compare x y == LT x >= y = compare x y /= LT x > y = compare x y == GT max x y | x <= y = y | otherwise = x mix x y | x <= y = x | otherwise = y -} $$} そこで、未実装機能だけを確認する mycompare.hs をつくって、 まずは、そちらで調査を継続する。 ### mycompare.hs mycompare.hs では、以下の GuardedRhs をリネームする部分が未実装なのでコケてる: $${ GuardedRhs [([ExpStmt (InfixExp (VarExp (Name {origName = "x", namePos = (1,17), isConName = False})) (Name {origName = "==", namePos = (1,19), isConName = False}) (VarExp (Name {origName = "y", namePos = (1,22), isConName = False})))],VarExp (Name {origName = "EQ", namePos = (1,29), isConName = True})),([ExpStmt (InfixExp (VarExp (Name {origName = "x", namePos = (2,17), isConName = False})) (Name {origName = "<=", namePos = (2,19), isConName = False}) (VarExp (Name {origName = "y", namePos = (2,22), isConName = False})))],VarExp (Name {origName = "LT", namePos = (2,29), isConName = True})),([ExpStmt (VarExp (Name {origName = "otherwise", namePos = (3,17), isConName = False}))],VarExp (Name {origName = "GT", namePos = (3,29), isConName = True}))] []) $$} mycompare.hs シリーズのうち、まず、mycompare4.hs を [009](bissue009) で扱うことにする。 ⇒ 009 の対処で nodup0.hs は通るようになったのだけど、mycompare4.hs はランタイムにて abend! するようになってしまったので、このテストケースはこちらに戻します。 ### 2020-04-14 ### 辞書の多重継承化 jout/mycompare4 を jout/mycompare4e にコピーし、まずは、手で書き直して実験してみる。 Eq, Ord, Num などは interface にし、各辞書の実態は、たとえば Int の Ord 辞書は、Eq と Ord の2つのインターフェイスを implement する。 一部の書き換えを以下に例示する。 Dict_36_Prelude_46_Eq.java: $${ import jp.ne.sakura.uhideyuki.brt.brtsyn.*; import jp.ne.sakura.uhideyuki.brt.runtime.*; interface Dict_36_Prelude_46_Eq { abstract public Expr mk_61__61_(); abstract public Expr mk_47__61_(); } $$} Dict_36_Prelude_46_Ord.java: $${ import jp.ne.sakura.uhideyuki.brt.brtsyn.*; import jp.ne.sakura.uhideyuki.brt.runtime.*; interface Dict_36_Prelude_46_Ord { abstract public Expr mk_60_(); abstract public Expr mk_60__61_(); abstract public Expr mk_62__61_(); abstract public Expr mk_62_(); } $$} Dict_36_Prelude_46_Integer_64_Prelude_46_Eq.java: $${ import jp.ne.sakura.uhideyuki.brt.brtsyn.*; import jp.ne.sakura.uhideyuki.brt.runtime.*; public class Dict_36_Prelude_46_Integer_64_Prelude_46_Eq extends Dictionary implements Dict_36_Prelude_46_Eq { public Expr mk_61__61_(){ return Prelude.mkI_37_Integer_46__61__61_(); } public Expr mk_47__61_(){ return Prelude.mkI_37_Integer_46__47__61_(); } } $$} Dict_36_Prelude_46_Integer_64_Prelude_46_Ord.java: $${ import jp.ne.sakura.uhideyuki.brt.brtsyn.*; import jp.ne.sakura.uhideyuki.brt.runtime.*; public class Dict_36_Prelude_46_Integer_64_Prelude_46_Ord extends Dictionary implements Dict_36_Prelude_46_Eq, Dict_36_Prelude_46_Ord { private Dict_36_Prelude_46_Integer_64_Prelude_46_Eq dictEq; public Dict_36_Prelude_46_Integer_64_Prelude_46_Ord(){ this.dictEq = new Dict_36_Prelude_46_Integer_64_Prelude_46_Eq(); } /* methods of Eq */ public Expr mk_61__61_(){ return this.dictEq.mk_61__61_(); } public Expr mk_47__61_(){ return this.dictEq.mk_61__61_(); } /* methods of Ord */ public Expr mk_60_(){ return Prelude.mkI_37_Integer_46__60_(); } public Expr mk_60__61_(){ return Prelude.mkI_37_Integer_46__60__61_(); } public Expr mk_62__61_(){ return Prelude.mkI_37_Integer_46__62__61_(); } public Expr mk_62_(){ return Prelude.mkI_37_Integer_46__62_(); } } $$} このようにしておけば、あらゆる辞書は Dictionary 型変数に格納することができ、 たとえば、Integer Ord 辞書は、Dict_36_Prelude_46_Ord にも Dict_36_Prelude_46_Eq にもキャストできる。 …が、このようにしても、また、別のランタイムエラーが生じてしまった。 今度は、Eq 辞書の実態を Ord にキャストしようとして abend しており、これは、確かに変。 ${--ddump-core} で確認してみると、mycompare の型は $ {[Prelude.Eq t15,Prelude.Ord t15] :=> (t15 -> (t15 -> Prelude.Ordering)))} となっていて、辞書は Eq, Ord の順でわたされることを期待しているのに、 呼び出し側で逆順にわたしてしまっている。 …が、そもそも、mycompre の型は $ {Eq a, Ord a => a -> a -> Ordering} ではなく、$ {Ord a => a -> a -> Ordering} ではないのか。 というわけで、問題は3つあることになる。 - Runtime における辞書の実装を変える(interface による多重継承) - 推論された、各項の型における述語リストを最も単純な形にする (simplify が使えそう) - 定義側と呼び出し側で辞書渡しの順序がくいちがっていた ### 推論された、各項の型における述語リストを最も単純な形にする (simplify が使えそう) 述語リストが単純になっていなかったのは、simplify が呼ばれてないとかではなく、 ClassEnv に親子関係が正しく登録されていなかったため。 Rename.renClassDecls を修正して、Class 定義から super class 情報を読み取って、 ClassEnv に追加するようにした。 ただし、現状の実装はかなり特殊なので、要改善([026](bissue026))。 これにて、mycompare の型はただしく $ {Forall [Star] ([Prelude.Ord a] :=> (a -> (a -> Prelude.Ordering)))} となった。 次に、DictPass における lookupDictArgs をこれに対応。 そのため、Sement から ClassEnv 情報を DictPass まで引き回して利用。 ClassEnv には defaults も含まれるため、DictPass で defaulting しなければいけなくなった場合に使えそう。 ### Runtime における辞書の実装を変える(interface による多重継承) これは、すでに手で書いてためしていたような出力になるよう、CodeGen を書き換えて完了。 mycompare4.hs は通ったので sample143 とした。 ### 定義側と呼び出し側で辞書渡しの順序がくいちがっていた これを試すために、↓のような関数を用いればいいかなと思ったのですが、 $$ { Prelude> let f x y a b = (x + y) == (a * b) Prelude> :t f f :: (Eq a, Num a) => a -> a -> a -> a -> Bool $$} Haskell 2010 では、Eq が Num のスーパークラスなので ([参考](https://blog.miz-ar.info/2016/06/haskell-num-class/#Eq_Show))、 述語はひとつに単純化されてしまう。 そこで、Num と Monad が述語に現れるようなプログラムを用意した([028](bissue028))。 ### 2020-04-15 008が解決したので、mycompare*.hs 系も通るようになった。通ったものは test/sample 入り。 mycompare2.hs は Prim.() の扱いに難があってエラー、 これを回避した mycompare2x.hs は通った。 - mycompare.hs ok -> sample145 - mycompare2.hs NG - mycompare2x.hs ok -> sample146 - mycompre3.hs ok -> sample147 これをうけて、lib/Prelude.hs を修正、Ord クラスの定義を完全にした。 そのテストのためにつくった ordtest.hs では単項マイナスの未実装が露見。 単項マイナスをつかわない ordtest2.hs は通った (sample148)。 mycompare2.hs, ordtest.hs の件はそれぞれ独立のイシューをたてる。 本件は、これでクローズする。