# 079: Prelude にて、Show より前に Num のクラス宣言を書けるようにする [↑up](bunny_notes) - issued: 2020-05-17 - 分類: B 機能追加 - status: Closed (2021-01-28) ## 概要 現状では、Show クラスの宣言より前に Num の宣言を書くとエラーしてしまう。 コンパイラがトポロジカルソートすべきと思われる。 ## 調査ログ ## 2021-01-28 ### 念のため、トポロジカルソートで強連結成分(循環)が生じないかチェック もしかしたら、クラス定義についても循環をゆるす(関数は循環参照、相互再帰可能) んだったらどうしようと心配になって、しらべてみた: $$
{
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 宣言をトポロジカルソートするようにした。
解決。