読者です 読者をやめる 読者になる 読者になる

関係と代数的データ型との相互変換についての妄想 その1

OR マッパーみたいな、関係*1とデータ型との相互変換が機械的にできたら嬉しいなという話。

シンプルなのは簡単だしすでにできる。例えば次のような関係がある場合*2

var user base relation {
  id integer,
  name string,
}
key { id };

var friends base relation {
  user1 integer,
  user2 integer
};

これを単に次のようなデータ型に変換するのはいくらかのライブラリーで実現できる。

data User = User { id: Int, name: String }

data Friends = Friends { user1: Int, user2: Int }

しかし、実際のところ欲しい型というのは次の通りでこれは機械的にできるのはなさそう。(ちゃんとは調べてないのであったら教えてほしい。)

data User = User { id: Int, name: String, friends: [User] }

よくあるクラスベースのオブジェクト指向言語なら Friends ゲッターの中でクエリー発行すればいいんだけど、同じことを Haskell ですると IO まみれになるしできれば純粋な函数にしたいよなー。

public class User {
  public readonly int id;
  public readonly string name;
  readonly IList<User> friends;
  public IList<User> Friends {
    get {
      if (friends == null) friends = …;
      return friends;
    }
  }
  public User query(int id) { … }
}

妄想としては、そのフィールドが使われてるかの情報を型に乗っけることで、DB からの取得時にどれを取ってくればいいか分からないかなーと。

do
  user <- queryUser 1 -- 何らかの queryUser :: Int -> MonadDB User みたいな函数があるとして
  return $ friends user

friends 函数の型を GetFriends user => user -> [User] みたいにしてやることで型推論によって queryUser 函数の型クラス制約にも GetFriends が付いて実装を変えられないかなぁと。

うーんでも型クラスのありなしでは実装は変えられないかー。なんとか型に情報乗せないとダメかな。

続きはまた後日。

*1:ここでいう関係は関係代数の用語としての関係です。

*2:Tutorial D の記法に従ってますが、パーサーにかけたことがないのでまちがってるかもしれない。