続、関数型でのmodule分割
id:ABA:20060627 で循環importができないという話題の続き。
コメント欄でいただいたABAさんからのコメントで気づいたのですが、Listで使うための、ユニオン型だったんですね。たしかにmapってはいってた。
そうであれば、以下のようなのはどうでしょうか。
module TokenT where class TokenT t where update :: t -> t -- 以下、中でupdateを使うような公開関数とか
これは同じ
module SubEnemy where import TokenT data Enemy = Enemy {x :: Double, y :: Double } instance TokenT Enemy where update enemy = enemy { x = (x emeny) + 1 } instance Show Enemy where show (Enemy x y) = "enemy(" ++ (show x) ++ ", " ++ (show y) ++ ")"
これはパッケージ名を変えただけ。SubBulletも同様
module Token import TokenT import SubBullet as SB import SubEnemy as SE data Token = Enemy {x :: Double, y :: Double } | Bullet {x :: Double, y :: Double } instance TokenT Token where update (Bullet {x = ix, y = iy}) = let (SB.Bullet {SB.x = ox, SB.y = oy}) = update (SB.Bullet {SB.x = ix, SB.y = iy}) in Bullet { x = ox, y = oy} update (Enemy {x = ix, y = iy}) = let (SE.Enemy {SE.x = ox, SE.y = oy}) = update (SE.Enemy {SE.x = ix, SE.y = iy}) in Enemy { x = ox, y = oy}
単純に変換して渡すだけだが、レコード型なので量がちょっと多い。
パラメータにレコードではなく、タプルとかVertexとか使うのであれば、ここはかなり短くなる。レコードでも順番が同じなら同じ内容でコピーできたりするが:
update (Bullet vx vy) = let (SB.Bullet nx ny) = update (SB.Bullet vx vy) in Bullet nx ny
しかし、パッケージをまたぐ場合、順序に依存させるのはよくないと思ったので、いちいちパターンを使っている。同じ変換が何度も出るなら変換を関数やクラスにする。
という感じでしょうか。実のところSubのほうでTokenTのinstanceにして、同じupdateを使う効果はないですけど。
ただし、Haskellのmoduleも言語上はjavaのpackage同様、公開先を限定できないはずなので、moduleにする場合はやはりjavaでのpackageのような単位で分けたほうがうまくいきそうです。