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

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

私に教えられることなら

JavaScript、CommonLisp、Parenscriptでの引数確認

JavaScript CommonLisp Parenscript 備忘録

JavaScript

JavaScriptで引数が渡されているか確認するときは、暗黙の型変換に気をつけなければいけない。

function needX (x) {
    if (!x) {
        console.error("error!");
        return;
    }

    console.log("x is " + x);
}

// 渡したと判定されて欲しい
needX("");        // => error!
needX(0);         // => error!
needX(false);     // => error!
needX(null);      // => error!
needX(undefined); // => error!

// 渡してない
needX();          // => error!

残念ながら全てerror!になる。 undefinedと比較すると

function needX (x) {
    if (x === undefined) {
        console.error("error!");
        return;
    }

    console.log("x is " + x);
}

// 渡したと判定されて欲しい
needX("");        // => x is
needX(0);         // => x is 0
needX(false);     // => x is false
needX(null);      // => x is null
needX(undefined); // => error!

// 渡してない
needX();          // => error!

この方法ではundefinedを故意に渡すことはできない。(それが設計としてどうかは置いといて) 厳密な判定をするならargumentsのlengthを調べないといけない。

また、引数の初期値を次のようなコードで設定するときも、同様の問題が生じる。

function optionalY (y) {
    y = y || "default";

    console.log(y);
}

// 全てdefaultが出力される
optionalY(0);
optionalY("");
optionalY(null);
optionalY(undefined);
optionalY();

Common Lispでは型変換どうなるんだろう?

(defun need-x (x)
  (when x 
    (format t "x is ~A" x)
    x))

(need-x 0)  ;; => "x is 0"
(need-x "") ;; => "x is "

(need-x nil) ;; => NIL
(need-x '()) ;; => NIL

(need-x) ;; => エラー

特に驚くような事は無い。 &optionalではどうなるんだろう?

(defun optional-y (&optional (y 'y))
  (when y
    (format t "y is~A" y)
    y))

(optional-y 0)   ;; => "y is 0"
(optional-y "")  ;; => "y is "
(optional-y nil) ;; => "y is NIL"
(optional-y '()) ;; => "y is NIL"
(optional-y)     ;; => "y is Y"

理想の挙動だ。

Parenscriptではどうなるんだろう?

console.logのショートカットでpを作っておく。

  (defun p (obj)
    (chain console (log obj)))
  (defun need-x (x)
    (if x
      (p (+ "x is " x))
      (p "none")))

  (need-x 0)   ;; none
  (need-x "")  ;; none
  (need-x nil) ;; none
  (need-x '()) ;; x is
  (need-x)     ;; none

リストは配列に翻訳されるので、空の配列を判定、表示しようとしている。

&optionalはこうなる

  (defun optional-y (&optional (y 1))
    (if y
      (p (+ "x is " y))
      (p "none")))

  (optional-y 0)   ;; none
  (optional-y "")  ;; none
  (optional-y nil) ;; none
  (optional-y '()) ;; x is
  (optional-y)     ;; x is 1

Common LispのコードがJavaScriptコンパイルされるんじゃなくて、S式JavaScriptを書いてる、と常に注意しておかないとここらへんで泣きを見そうだ。

感想

調べていくうちにちょっと辛いかなと思ったけど、JavaScriptでは結局意識しなきゃいけないことだし、マクロで判定なんかの記述をどうにかできればプラスになるかなと思い直した。

まだ学び始めたばかりだから何とも言えないけど、マクロが、一番自分が解決したかった問題を解決してくれそうなので、しばらくしがみついて行きたい。

広告を非表示にする