関数型言語のmodule

id:ABA:20060627 で循環importができないという話題。

Haskellの場合、classはjavaのクラス的にラベル名での差は意識せず、モジュールのためのメンバーのように考えたほうがいいんじゃないだろうか。なんというか、あるフレームワークへのコネクタインタフェースみたいな感じでとらえるのがいいのだろうか。C++template的というか、xxxというメソッドを持ったなにかみたいな感じでしょうか。

SMLのモジュール構造が直接signature、structure、functorだったのを考えると、classとmodule分割の基本的な基準は、特にそういう抽象データ型的な感じになる思う(MLでも循環importはできなかった(最近はできるのかな))。利用視点からはとくにそうだと思う。C#のpartial typeみたいなのはプリプロセッサ的なコンパイラ処理であっても仕方がないかもしれない。しかし、C#とかと違ってユニオンタイプがあるので、その要求は多いかもしれない(classなどを駆使しして循環しないよう分割していくのが、Haskellerの標準レベル要求なのかもしれないけど)。


ところでABAさんの例ですが、Java的に考えてもそれがなぜ再帰的にmoduleを読む必要があるのか、そもそもユニオンを使う必要があるのかがわからない。

もし自分が設計するなら

module Token where
class Token t where
  update :: t -> t 

-- 以下、中でupdateを使うような公開関数とか
module Enemy where
import Token

data Enemy = Enemy {x :: Double, y :: Double }
instance Token Enemy where
  update enemy = enemy { x = (x emeny) + 1 }
instance Show Enemy where
  show (Enemy x y) = "enemy(" ++ (show x) ++ ", " ++ (show y) ++ ")"

Bulletも同様

module Main where
import Token
import Bullet
import Enemy

main = do
  putStrLn (show ts)
  putStrLn (show (map Token.update ts))

となる。

Javaでたとえるなら以下のような感じかな

  • interface Token { t update(); }
  • (interface Show { String show()})
  • class Enemy implements Token, Show
  • class Bullet implements Token, Show
  • class Main { static void main(..){...}}

もしupdateでBulletがEnemyに、EnemyがBulletに変わるなら...まずはサイクリック回避テクニックをとることになるんでしょうかね。dataだけを分割するとかclassにしてしまうとか。