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

レガシーコード生産ガイド

私に教えられることなら

SchemeでOCaml風if/let

clojure Scheme

といっても大層な話ではありません。

Early Returnがしたい

JavaScript(ES6)書いてる時なんかによく使うのがEarly Returnです。

function foo (bar) {
  if (barをチェック) { return だめだよ; }

  let x = baz(bar);
  if (xをチェック) { return だめだよ; }

  let y = yo(x);
  if (yをチェック) { return だめだよ; }

  return よくぞここまで来た……;
}

ネストしたifを見ると脳の短期記憶領域を使いきって気絶するので、ほぼ100%こういう書き方をします。

OCamlでEarly Returnがしたい

最近(Elm経由で)興味を持ってちょくちょくOCamlを触っています。

OCamlでも上記のような書き方をしたいと思っていたところ、インデントから魂を解放すればいい、という素晴らしいアイディアを見つけました。

stackoverflow.com

上記のJavaScript擬似コードは、次のように書けます。

let foo bar =
  if barをチェック then だめだよ else
  let x = baz bar in
  if xをチェック then だめだよ else
  let y = yo x in
  if yをチェック then だめだよ else
  よくぞここまで来た……

インデントと式の構造が全く対応していないように思えて、少し気持ち悪さがあるかもしれません。しかし、let ... in ではinの後の式(の環境)に影響すること、if ... then ... else ... でelseの後を重視してることを考えると、むしろ「後」が「コードの後ろ」で表現されているこの書き方の方がより素直だ、と思い込むこともできます。できました。綺麗な構文です。

LispでEarly Returnがしたい

最近、仕事ではClojure(Script)を書いています。(Elm使ったプロジェクトは中断してます……)

特にサーバーサイドでEarly Returnをしたい関数が頻出します。

このif-let*を少し改造して使っていますが、単純にfalsyな値かどうかだけで検査するのは面倒でした。やはり普通のif式が欲しい。

というわけで、まず振り回しやすいScheme(gauche)とdefine-macroとutil.matchで書いてみました。

(use util.match)


(define-macro (o- . exprs)
  (match exprs
    [('let 'rec var '= val 'in . rest)
     `(letrec ([,var ,val])
        (o- ,@rest))]
    [('let var '= val 'in . rest)
     `(let ([,var ,val])
        (o- ,@rest))]
    [('if condi 'then ct 'else . rest)
     `(if ,condi ,ct (o- ,@rest))]
    [(expr) expr]))


(define (fizzbuzz n)
  (o- let divisible = (lambda [x n] (= (mod x n) 0)) in
      if (divisible n 15) then "FizzBuzz" else
      if (divisible n 3)  then "Fizz" else
      if (divisible n 5)  then "Buzz" else
      n))

(define (iota start end i)
  (o- let rec loop =
      (lambda [r-acc n]
        (o- if (>= n end) then (reverse r-acc) else
            (loop (cons n r-acc) (+ n i))))
      in
      (loop '() start)))


(print
 (map fizzbuzz (iota 1 100 1)))

なんとも邪悪な気配を感じるコードですね。

ClojureでもEarly Returnがしたい

で、どうせ一人で書いてるコードだからええやろ!ということで、Clojureでもこの邪悪なマクロを導入しようと目論見ました。

が、シンボルの名前空間の関係で、:refer [if let]みたいなとんでもないことをしないと使いづらいです。(多分)

現実的には、:let:ifなどのキーワードを前置することで識別したほうが良さそうです。シンタックスハイライトも楽勝です。

他に良いアイディアあったらコメント欄で教えてください。

広告を非表示にする