演算子を使って WHERE 節を書いた方がいいと思いませんか

かなりどうでもいいネタを思いついたので。

SQL の WHERE 節を string で書くのが一般的なバインディングですが、それはどうも正しい文法を書いているのか、正しい型を書いているのかどうか怪しい。そんなあなたに・・・。

type exp =
  | BinOp of bin_op * exp * exp
  | Var of string
  | Bool of bool
  | Int of int
  | Float of float
  | Str of string
and bin_op =
  | And
  | Or
  | Eq

let make_bin op e1 e2 =
  BinOp(op, e1, e2)

let ( |=| ) = make_bin Eq
let ( |&| ) = make_bin And
let ( ||| ) = make_bin Or

let bin_op_to_str = function
  | And -> "AND"
  | Or -> "OR"
  | Eq -> "="

let rec to_str = function
  | BinOp(op, e1, e2) ->
      (to_str e1) ^ " " ^ (bin_op_to_str op) ^ " " ^ (to_str e2)
  | Var var ->
      var
  | Bool true ->
      "TRUE"
  | Bool false ->
      "FALSE"
  | Int n ->
      string_of_int n
  | Float f ->
      string_of_float f
  | Str s ->
      "\""^s^"\""

中置演算子にしているのがポイントだ。

let e = ((Var "name") |=| (Str "taro")) |&| ((Var "id") |=| (Int 3)) in
print_string(to_str e);;
name = "taro" AND id = 3

もちろんこのあと型チェックをしましょうねという寸法。それから、C++ みたいにオーバーロードできる言語なら、もっとすっきり書けるはず。書いてないけど。たぶんこんなん。

Exp e = Var("name") == "taro" && Var("id") == 3;
cout << e.to_str() << endl;

お、これスゴイじゃん。

そういえば、またどうでもいいことを思いついたんだが、ここで bool と int の exp を(というか、non-terminal)を bool_exp と int_exp に分けたいよね。でも、分けると a' -> a' -> bool な演算子とかが面倒だよね。だから、あとで型チェックプログラムに通すのが筋だよね。でも、そこで項間の関係(同じ型であるとか)いれればいいだけだよね。まるで I と am の人称をあわせるみたいに!