Haskellでmoduleを循環importする方法
http://d.hatena.ne.jp/ABA/20060626
我が愛してやまないゲームを作りまくっているabaさんがHaskellに挑戦して悩んでいるので、ぜひとも応援すべく一緒に悩んでみました。
- Main.hs
module Main( main )where import CycleA main = interact $ ("Haskell is "++).unlines.map (hogeA.read).words
- CycleA.hs
module CycleA( hogeA )where import CycleB hogeA x | x <= 0 = "cool!" | otherwise = "ultra " ++ hogeB (x-1)
- CycleB.hs
module CycleB( hogeB )where import CycleA hogeB f x | x<=0 = "great!" | otherwise = "super " ++ hogeA (x-1)
を作ってmakeしようとすると
>ghc Main.hs --make Chasing modules from: Main.hs Module imports form a cycle for modules: CycleA imports: CycleB CycleB imports: CycleA
循環があると言われてできない。
で、考えた解法はこう。
module CycleA( hogeA )where import CycleB hogeA x | x <= 0 = "cool!" | otherwise = "ultra " ++ hogeB hogeA (x-1)
module CycleB( hogeB )where hogeB f x | x<=0 = "great!" | otherwise = "super " ++ f (x-1)
旧版のプログラムだとhogeBのパートナーはhogeAに固定で、しかもそれをimportしてhogeBからグローバルスコープ(?)で見えるように置いとかなきゃ動かなかった。hogeBを、任意の関数を取れるように抽象化しておけば、import循環を起こす必要自体がなくなる。さすが関数型言語。
今回もだけど、Haskellで書いてて不自由だなと思ったときは、実は抽象化(?)が足りてなくて、Haskell様の御意向に沿うように書こうと工夫すると、自分が書きたかったものよりも良い書き方がみつかる、ことが何回かあったんだ。Haskellは駄目を出すだけでどこが駄目なのかは生徒に考えさせる名教師だったんだよ!
というのが少ししかないHaskell経験による妄想で、実際にはhs-bootファイルを手で書くという方法で循環importは作れるのであった。
http://www.haskell.org/ghc/docs/latest/html/users_guide/separate-compilation.html#mutual-recursion
しかし余りエレガントじゃないように見える。cのプロトタイプ宣言みたいだ。循環importの解決を自動化しない事情があるのかもしれないが、何だろう。
Haskell Game Programming Minimum
僕が新しいプログラミング言語を学ぶときは、初めて作ったプログラムに覚えた機能を少しづつ足していって、その結果Hello Worldがいつの間にかゲームに化けてる、という方法をとります。
monadiusが実はそれで、試行錯誤の結果が堆積しており肥大化してあまりに読みにくいので、重力レンズシミュレータを作ったときは、また1からやり直して書きました。
その中間産物で
だけのシンプルなプログラムをここに置いときます。
http://www.geocities.jp/takascience/haskell/glKeyMouse.zip
これもあんまり読みやすくないけど・・・