1696


たまに Emacs Lisp を書くのですが、一つだけの束縛を持つ `let` 式で、

[code]

    (let ((x 1)) x)
    
[/code]

と書くべきところをつい

[code]

    (let (x 1) x)
    
[/code]

と書いてしまい、実行時エラーで苦しんでいます。もちろんデバッガを使えばわかることなのですが、実際にコードを走らせて見る前にこのような間違いを見つける方法はないでしょうか。たとえば、このような怪しい束縛はハイライトしてくれるととても助かります。(
`(let (x) x)` で `x` が `nil` になることは理解しています… )


CC BY-SA 3.0
2014-12-16T04:31:11.443
1664
2014-12-17T17:31:42.310
2014-12-16T05:28:17.973
440
898
question
emacs lisp elisp
let 束縛の括弧を忘れてつい (let (x 1) e) と書いてしまいます。静的なチェッカはありませんか？


`let`の方がfunctionではなくて "special form" です。

必要な関数の引数は [(def list), (begin exps)]:

[code]

    (define (z x)
     (let ((q 1)
           (y 2))
         (do-stuff x q y))
    
[/code]

これで (def list) の長さは2: `((q 1) (y 2))`

長さは1だけでも `((q 1))` を書かないと意味が違いますね。 〔 `((q 1))` VS `(q 1)` 〕

emacs lispでは`(let (x) x)` の意味は `(define x ())` -> `nil`


CC BY-SA 3.0
2014-12-16T05:00:55.940
1669
2014-12-16T05:09:06.633
2014-12-16T05:09:06.633
2371
2371
1664
answer


Emacs に標準で `elint.el` が同梱されています。

今ポイントしている関数には `elint-defun` を使うことができます。 他にも、 `elint-current-buffer`, `elint-
file` や `elint-directory` があります。

例えば以下のようなバッファで `elint-current-buffer` すると、 `*Elint*` バッファに結果が示されます。

[code]

    (let (x 1) x)
    
    (defun my-test ()
      (let (x '(1 2 3)) x))
    
[/code]

結果は、

[code]

    Linting /tmp/test.el
    
    In top level expression:
    test.el:1:Error: Malformed `let' declaration: 1
    
    In function my-test:
    test.el:3:Warning: Suspect varlist: (let (x (quote (1 2 3))) x)
    
    Linting finished.
    
[/code]


CC BY-SA 3.0
2014-12-16T06:19:58.593
1691
2014-12-16T06:19:58.593
3383
1664
answer


`elint`の他にバイトコンパイルする手段があります.

[code]

    (let (x 1) x)
    
[/code]

というファイルをバイトコンパイルすると, 以下のようにエラーが出ます.

[code]

    Compiling file /home/syohei/junk/elisp/test2.el at Tue Dec 16 15:41:42 2014
    test2.el:1:1:Error: Wrong type argument: listp, 1
    
[/code]

また `lexical-bindings`を有効にすると

[code]

    ;;; some-package.el --- This is some package. -*- lexical-binding: t; -*-
    
    (let (x y) x)
    
[/code]

のような内容でも警告されます(未使用変数という形になってしまいますが).

[code]

    Compiling file /home/syohei/junk/elisp/test.el at Tue Dec 16 15:43:19 2014
    test.el:3:1:Warning: Unused lexical variable `y'
    
[/code]

バイトコンパイルは diredで **B** をタイプすると行えます. (依存関係をチェックする際などは, `emacs
-Q`で起動してバイトコンパイルすると望ましいです)

`lexical-bindings`は Emacs 24から利用できます. これからリリースするような パッケージは
Emacs23をサポートする必要はないと思いますので, 常に有効にしておいて よいと思います(クロージャも書きやすくなりますし).

バイトコンパイルはその他 deprecated, obsoletedな変数, 関数等の指摘も行って くれるので,
パッケージをリリースする際は必ず行うことが望ましいと考えています.

参考

  * <http://www.emacswiki.org/emacs/LexicalBinding>


CC BY-SA 3.0
2014-12-16T06:51:47.813
1696
2014-12-16T06:51:47.813
3521
1664
answer


静的なチェッカーでは無いのですが、 macroで新しい構文を作ってしまえば、良いのではないですかね?

[code]

    (defmacro elet (binds &rest body)
      (let ((new-binds (make-new-binds binds)))
        `(let ,new-binds ,@body)))
    
    (defun make-new-binds (binds)
      (if (null binds)
          nil
        (let ((var (car binds))
              (init-form (cadr binds)))
          (cons `(,var ,init-form) (make-new-binds (cddr binds))))))
    
[/code]

とすれば、

[code]

    (elet (x (+ 1 2) y (+ 1 2 (- 3 4))) (+ x y)) 
    ;;  => -5
    
[/code]

となります。

これで、楽に掛けますね!


CC BY-SA 3.0
2014-12-17T17:31:42.310
1944
2014-12-17T17:31:42.310
2474
1664
answer