079: Prelude にて、Show より前に Num のクラス宣言を書けるようにする

↑up

概要

現状では、Show クラスの宣言より前に Num の宣言を書くとエラーしてしまう。 コンパイラがトポロジカルソートすべきと思われる。

調査ログ

2021-01-28 (Thu)

念のため、トポロジカルソートで強連結成分(循環)が生じないかチェック

もしかしたら、クラス定義についても循環をゆるす(関数は循環参照、相互再帰可能) んだったらどうしようと心配になって、しらべてみた:

class (B a) => A a where
  methodA :: a -> String
  methodY :: a -> String

class (A a) => B a where
  methodB :: a -> Int
  methodX :: a -> Int


instance A Int where
  methodA x = show x
  methodY x = show (methodX x)

instance B Int where
  methodB x = length (methodA x)
  methodX x = x^2

main = print $ methodB (8::Int)

これは、GHC でもエラーになる:

$ runhaskell mutual_recursion.hs

mutual_recursion.hs:1:1: error:
    • Superclass cycle for ‘A’
        one of whose superclasses is ‘B’
        one of whose superclasses is ‘A’
      Use UndecidableSuperClasses to accept this
    • In the class declaration for ‘A’
  |
1 | class (B a) => A a where
  | ^^^^^^^^^^^^^^^^^^^^^^^^...

mutual_recursion.hs:5:1: error:
    • Superclass cycle for ‘B’
        one of whose superclasses is ‘A’
        one of whose superclasses is ‘B’
      Use UndecidableSuperClasses to accept this
    • In the class declaration for ‘B’
  |
5 | class (A a) => B a where
  | ^^^^^^^^^^^^^^^^^^^^^^^^...

lib/Prelude.hs 変更

では、トポロジカルソートを実装するまえに、テストを Red にしておく。 lib/Prelude.hs の Show を unzip 定義の後ろにもっていく。期待通りエラーする:

$ bin/bunny tcompile --xno-implicit-prelude lib/Prelude.hs 
/home/unno/prj/bunny/compiler/bin/bunnyc -d ./jout/Prelude --xno-implicit-prelude /home/unno/prj/bunny/compiler/bin/../lib/Prelude.hs
bunnyc: addClass failed: ("Prelude.Num",ClassEnv {ceMap = fromList [("Prelude.Bounded",([],[])),("Prelude.Enum",([],[])),("Prelude.Eq",([],[])),("Prelude.Ord",(["Prelude.Eq"],[]))], defaults = [TCon (Tycon "Prelude.Integer" Star)]})
CallStack (from HasCallStack):
  error, called at src/Rename.hs:379:28 in main:Rename
CallStack (from -prof):
  Rename.renClassDecls.clsadd.ce' (src/Rename.hs:(379,11)-(380,39))
  Rename.renClassDecls.clsadd (src/Rename.hs:(374,5)-(384,53))
  Rename.renClassDecls.\ (src/Rename.hs:(362,37)-(364,53))
  Rename.renClassDecls (src/Rename.hs:(360,1)-(403,31))
  Semant.renProg (src/Semant.hs:(21,1)-(52,11))
  Main.doCompile.(...) (app/Main.hs:83:7-70)
  Main.doCompile (app/Main.hs:(77,1)-(114,33))
  Main.main (app/Main.hs:(117,1)-(132,42))
tcompile: failed to compile lib/Prelide.hs

対処

Rename モジュールの scanDecl で class 宣言をトポロジカルソートするようにした。 解決。