Haskell98可変長引数ハックにみる,各処理系のcontext reductionの違い
Olegさんの Generic polyvariadic printf in Haskell98 は、プレーンなHaskell98で可変長引数関数をつくり,printfを実現しています. 関数の型(a->r)は型構築子(->)と型引数a rからなる型 (->) a r なわけで,型クラスのインスタンスでうまいこと回せば、こんなこともできるんですねーというお話. すごい.
(OlegさんによるHaskellの型安全なprintfについては、ここにちょっと追記)
このprintfは Haskell98 なので codepad の Hugsでも動きます. ここに貼りました:
http://codepad.org/xY2quxH0#comment-C6lfIn51
context reduction の違いを見てみる
ところでこいつは,HugsとGHCの各バージョンにおける context reduction の違いを見るのにちょうど良さげな例になってるみたいですので、みてみましょう. (昨日の ToPS の後の飲み会でも似たような話を振ったので,ちょうど復習になったのですが,あと新しい発見が!)
Hugs (March 2005)
hugs> :t printf "%s%s%s" True True True printf "%s%s%s" True True True :: SPrintF a => a
期待通り. あとは unR :: RString -> String を使って 型変数 a を RString にしてやれば インスタンス SPrintF RString の方が選択されて=> がなくなります.もちろん引数を追加してもOK (フォーマットは%s%s%sなので表示されないけど).目出度い.
GHC (6.6)
ghci-6.6> :t printf "%s%s%s" True True True printf "%s%s%s" True True True :: (SPrintF (Bool -> Bool -> Bool -> t)) => t
コイツ全然やる気ないですね. SPrintF (a->r) なので インスタンスはHugsのように簡約されてもよさそうなもんですが,やってくれません.
GHC (6.8, 6.10)
ghci-6.10> :t printf "%s%s%s" True True True printf "%s%s%s" True True True :: (SPrintF (Bool -> t)) => t
!?
要約
Hugs が一番 eager(?) に文脈を簡約しています. GHC6.6は lazy(?) ですね.
しかしおどろくべきことに,我らがHaskell型レの星・GHC 6.8と6.10 はなんだか中途半端に SPrintF (Bool -> t) を SPrintf t へ簡約できずにいます.
なぜなんでしょう。。。 GHC 6.8のchangelogを読んでもこのへんの変更はよくわからない.よく見ると associated typesが入ったのが GHC6.8か.うーむ.
いつもの-fglasgow-exts (つまり-XMultiParamTypeClasses -XFunctionalDependencies -XFlexibleInstances -XFlexibleContextsとか) あと -XUndecidableInstances や -XNoMonomorphismRestriction とかを加えてみたのですが,変わりませんでした.(実のところghci の :t で型を見るときは単相性制限はないので -XNoMonomorphismRestrictionは冗長)
haskell-cafeに投げればわかりそうだけど