プログラミング言語の構文って大事だよね、という話

Twitterでこんなつぶやきをしました。

が、ちょっと違う感じに受け取られたかな、と思ったので補足します。

プログラミング言語としての」とは

つぶやきの中でいちいち「プログラミング言語としての」と書いたのは、 「普通の」SQLや「普通の」XSLTとは違って、ということを明示したかったからです。

普通はSQLは問合せのための言語だし、XSLTXMLを変換するための言語*1であって、JavaC#などのプログラミング言語と同じようなものと見ている人はほぼいないでしょう。 ただ、ちょっとしたテクニックを知っていると、これらを使って「普通の」プログラミング言語でやるような処理が書けてしまうのです。

XSLTでプログラミング

XSLTにはループや分岐が組み込まれているので、手続き型プログラミングであればどうとでもなります。 なりますが、それでは面白くないのでループを封印してみましょう。

ループを使わないXSLTプログラミング

XSLTでは template を関数とみなすことで、ループを使わなくても再帰呼び出しが使えます。 あとは高階関数が使えればざっくり関数プログラミング的なものが出来ますね。 詳しい説明は そのアイディアを形にした人が書いたドキュメント に譲るとして、 そのテクニックを使うことでFizzBuzzrange 関数と map 関数と fizzbuzz 関数を組み合わせることで書けてしまいます。

長いので折り畳みましたが、これと同じようなことを F# で書くとこうなります。

let rec range from ``to`` =
  if from <= ``to`` then from :: range (from + 1) ``to`` 
  else []

let rec map f = function
| [] -> []
| x::xs -> f x :: map f xs

let fizzbuzz n =
  if n % 15 = 0 then "FizzBuzz\n"
  elif n % 5 = 0 then "Buzz\n"
  elif n % 3 = 0 then "Fizz\n"
  else string n + "\n"

let xs = range 1 100
xs |> map fizzbuzz |> List.iter (printf "%s")

大体機械的に対応は取れると思います。

SQLでプログラミング

SQL再帰クエリーによって再帰が書けます。 しかし、XSLTとは違い関数に対応させられるようなものが(SELECT文の範疇では)ありませんし、高階関数など夢のまた夢です。 そのため、SQLでのプログラミングはXSLTよりも困難です。

ただ、再帰クエリーでも使う WITH を「入力固定の関数」のようなものとみなすことで、ある程度の構造化は可能です。

-- 最近PostgreSQLを使っているのでPostgreSQL想定
WITH RECURSIVE range_ (n_) AS (
  SELECT 1
  UNION ALL
  SELECT n_ + 1 FROM range_ WHERE n_ < 100
)
, fizzbuzz_ (n_, result_) AS (
  SELECT
      n_
    , CASE
        WHEN n_ % 15 = 0 THEN 'FizzBuzz'
        WHEN n_ % 5 = 0 THEN 'Buzz'
        WHEN n_ % 3 = 0 THEN 'Fizz'
        ELSE CAST(n_ AS varchar)
      END
  FROM
    range_
)
SELECT * FROM fizzbuzz_;

さらに複雑なことがしたい場合、空白区切りなどの文字列をリストと見立てて操作することでよりいろいろなことができるようになります*2

構文って大事

こんな感じで、XSLTでプログラミングしようとすると大量のノイズで本来書きたい処理はタグの中に埋もれてしまいますし、 SQL(select)でプログラミングしようとするとそもそも関数からしてないので考え方からして変えないといけません(そのための例としてはFizzBuzzは小さすぎたかも)。 SQLで複雑なやつだと、 SQL で数式を評価 (完全版 + α) - ぐるぐる~ あたりがオススメです。数式評価だけど、演算子の優先順位を設定可能な完全に頭おかしいやつです。

ということで、他の人にはあまりオススメできない、オススメの構文のありがたみが体感できる方法でした。 制約された環境であれこれ考えるのが好きな人であればあるいは・・・

*1:言語?ツール?

*2:配列型やJSONを使うという選択肢もあるけどそれだとツマラナイ