関係代数(2)

属性、タプル、リレーショナルを以下のように実装した。いまのところ、5つの属性からなるタプルまでしか実装していない。

type Attr = String
-- | Attribute
data A a = A Attr a deriving (Eq, Show)
-- | Tuple
data T1 a = T1 (A a) deriving (Eq, Show)
data T2 a b = T2 (A a) (A b) deriving (Eq, Show)
data T3 a b c = T3 (A a) (A b) (A c) deriving (Eq, Show)
data T4 a b c d = T4 (A a) (A b) (A c) (A d) deriving (Eq, Show)
data T5 a b c d e = T5 (A a) (A b) (A c) (A d) (A e) deriving (Eq, Show)
-- | Relational
data R a = R (Set a) deriving (Eq)

このデータ型に対する関係演算(restrict, join)は以下のように素直に定義できた。

restrict :: (a -> Bool) -> R a -> R a
restrict f (R s) = R (S.filter f s)
rJoin :: (Eq a, Eq b, Eq c) => (a -> b -> Maybe c) -> R a -> R b -> R c
rJoin f (R x) (R y) = R (S.fromList (catMaybes [f a b | a <- elems x, b <- elems y]))

例えば、(部署番号、部署名、予算) からなる関係 DEPTS と、(従業員番号、氏名、部署番号、給与)からなる関係 EMPS に対して、以下のような演算が行える。DNO は DNO String で定義されるデータ型であるため、単純に文字列との比較は出来ない点に注意。このような型に厳密な演算は、 Tutorial D では(煩雑、ではなく)長所と考えられているようだ。

*Relational> :t depts
depts :: R (T3 DNO NAME BUDGET)
*Relational> depts
DNO DNAME BUDGET
----------------
D1 Marketing 10M
D2 Development 12M
D3 Research 5M

-- SELECT * FROM DEPTS WHERE DNO = "D3" に相当

*Relational> restrict (\ (T3 dno _ _) -> dno == (A "DNO" (DNO "D3"))) depts
DNO DNAME BUDGET
----------------
D3 Research 5M

*Relational> emps
ENO ENAME DNO SALARY
--------------------
E1 Lopez D1 40K
E2 Cheng D1 42K
E3 Finzi D2 30K
E4 Saito D2 35K

-- SELECT D.DNAME, E.ENAME FROM DEPTS D, EMPS E WHERE D.DNO = E.DNO に相当
*Relational> rJoin deptJoin depts emps
DNAME ENAME
-----------
Marketing Lopez
Marketing Cheng
Development Finzi
Development Saito

ここで、deptJoin 関数は以下のように定義している。

deptJoin (T3 dno dname budget) (T4 eno ename dno' salary)
    | dno == dno' = Just (T2 dname ename)
    | otherwise = Nothing

TODO

  • Tutorial D では、ナチュラルジョインは属性名で自動的に行われるため、 EMPS JOIN DEPTS のような簡潔な表記ができる。
  • Tutorial D では、上記の実現のため、属性名を RENAME する機能を持つ。