017: Show クラスでないものでも show できてしまう

↑up

概要

クラスの継承について未実装だったときの仮実装として、Prim.show の型が (a -> [Prelude.Char]) になっているため、Show のサブクラスでないものまで show できてしまう。

調査ログ

2020-04-16 (Thu)

Show Bool

まず、いちばん単純な Bool から。

Prim.show が a -> [Char] なこと自体は、それでよかった。 ソース中に show があると、問答無用で Prim.show にリネームされていたのがよくなかった。

そこで、lib/Prelude.hs に以下を追加、それにあうよう Predefined を変更。

class Show a where
  show :: a -> [Char]

data Bool = False | True

instance Show Bool where
  show = Prim.show

Bool や Ordering など kind が * なものはうまくいったが、Show [a] や Show (,), Eq [a] など難航。

make check では 6 つ fail している状況:

kind が * -> * (またはそれ以上)のケースに対応する必要がある。少なくとも、以下の考慮が必要。

138 と139 は、deriving Show に未対応であるのが原因と思われる。

なお、deriving Show なコンテナに、show できないオブジェクトを「いれて」も、 その時点ではエラーしない。show しようとすると(実行時ではなく静的にだが)、エラー。

たとえば、↓のコードはエラーしないが、print c しようとするとコンパイルエラーになる。

f x = x * x

a = [f]

data B a = B a
         deriving Show

b = B f

data C a = C a

c = C f

instance (Show a) => Show (C a) where
  show (C a) = "C " ++ show a

main = do print $ (head a) 5
          let B g = b
          print $ g 4
          let C h = c
          print $ h 3

          -- print c

2020-04-18 (Sat)

いくつか問題があるが、小さいものからひとつずつ片付けていこう。

  1. インスタンスメソッドがまた多相になるケースの辞書渡し: いまは IO.>> がうまく動かない問題として露見
  2. [Integer] や (Integer, Integer) に対する dictionary not found
  3. Prim.(,) を Prelude.(,) に移し、かつ、lib/Prelude.hs 中で Show のインスタンス定義

(1) インスタンスメソッドがまた多相になるケースの辞書渡し

昨日までの改造による状況を整理:

いまの(動いていない)変換例は以下(sample100 の --ddump-core):

(Main.main :: (Prelude.IO ())) =
    ((((Prelude.>> :: ([Prelude.Monad TVar (Tyvar "t0" (Kfun Star Star))] :=> ((TVar (Tyvar "t0" (Kfun Star Star)) t1) -> ((TVar (Tyvar "t0" (Kfun Star Star)) t2) -> (TVar (Tyvar "t0" (Kfun Star Star)) t2))))) 
          ${Prelude.IO Prelude.Monad}) 
        ((Prim.putStrLn :: ([Prelude.Char] -> (Prelude.IO ()))) 
          "Hello!")) 
      ((Prim.putStrLn :: ([Prelude.Char] -> (Prelude.IO ()))) 
        "World!"))

改造しようとして、まずリファクタして、trace を仕込みつつみていて気付いたのだけど、 辞書を適用した結果がまた辞書わたし形式になるような再帰は、 静的に解決できるとは限らない。ランタイムにまた辞書探してくるなんてことはできないので、 これは変だ。

この型がまちがっている >Prelude.IO%I.>> :: Forall [Kfun Star Star,Star,Star] ([Prelude.Monad a] :=> ((a b) -> ((a c) -> (a c))))

そちらを直そう。そうすると、tcBind に [Assump] を渡すのは不要だったな、 忘れないうちに戻そう。⇒ done.

trCDecl を変更して、DictDef に型シグネチャも格納するようにした。

今度は、renInstDecls でこの情報をもちいて、インスタンスメソッドの型シグネチャを出力するようにする。

DictDef に格納される型情報の例いくつか:

TypeSigDecl [Name {origName = ">>", namePos = (51,4), isConName = False}] (Just (AppTy (Tycon (Name {origName = "Monad", namePos = (48,7), isConName = True})) (Tyvar (Name {origName = "m", namePos = (48,13), isConName = False}))),FunTy (AppTy (Tyvar (Name {origName = "m", namePos = (51,13), isConName = False})) (Tyvar (Name {origName = "a", namePos = (51,15), isConName = False}))) (FunTy (AppTy (Tyvar (Name {origName = "m", namePos = (51,20), isConName = False})) (Tyvar (Name {origName = "b", namePos = (51,22), isConName = False}))) (AppTy (Tyvar (Name {origName = "m", namePos = (51,27), isConName = False})) (Tyvar (Name {origName = "b", namePos = (51,29), isConName = False})))))

 TypeSigDecl [Name {origName = "show", namePos = (7,3), isConName = False}] (Just (AppTy (Tycon (Name {origName = "Show", namePos = (5,7), isConName = True})) (Tyvar (Name {origName = "a", namePos = (5,12), isConName = False}))),FunTy (Tyvar (Name {origName = "a", namePos = (7,11), isConName = False})) (ListTy (Tycon (Name {origName = "Char", namePos = (7,17), isConName = True}))))

各インスタンスメソッドに型宣言がつくようにすることで、IO.>> の型が多相になってしまっていた問題は解決。

以前、テストのうち6つ通らないが、それらは別の issue として、本件はクローズする。

sample149 を追加