The Common Lisp Cookbook – LispWorks レビュー

Table of Contents

The Common Lisp Cookbook – LispWorks レビュー

📢 🎓 ⭐ Learn Common Lisp efficiently in videos, by the Cookbook's main contributor. Learn more.

🖊️ Discover a new Common Lisp and Coalton editor for beginners: mine and a new VSCode extension for Common Lisp: OLIVE.

LispWorks は Common Lisp の処理系で、独自の統合開発環境(IDE)と、CAPI GUI ツールキットのような独自機能を備えています。これはプロプライエタリで、無料の制限版も提供されています。

ここでは主にその IDE を見ていきます。Emacs と Slime に慣れた Lisp ユーザに何を提供できるのか、という観点です。短く言えば、扱いやすいグラフィカルステッパ、トレーサ、コードカバレッジブラウザ、クラスブラウザといった、より多くのグラフィカルツールがあります。ブレークポイントの設定と利用も Slime より簡単でした。

LispWorks はさらに統合されたツールも提供します。たとえば Process browser は Lisp イメージ内で実行中の全プロセスを一覧し、それらを停止、break、debug できます。また、多くの情報をグラフ形式で表示します。たとえば関数呼び出しのグラフや、作成された全ウィンドウのグラフです。

Mate デスクトップ環境での LispWorks の listener と editor

LispWorks の機能

LispWorks の機能をエディションとプラットフォーム別にまとめた表はここで見られます: http://www.lispworks.com/products/features.html

注目点:

そしてもちろん、組み込み IDE があります。

LispWorks は産業界のさまざまな分野で使われています。開発元は success stories の一覧を管理しています。私たち自身が使えるソフトウェアとしては、ScoreCloud(楽器を弾いたり、歌ったり、口笛を吹いたりすると楽譜を書いてくれる音楽記譜ソフトウェア)や OpenMusic(オープンソースの作曲環境)が見事です。

Free edition の制限

ダウンロード手順と制限はダウンロードページに示されています。

制限は次のとおりです。

何ができなくなるのでしょうか。例として、同じイメージ内で次のライブラリ群をまとめてロードすることはできません。

(ql:quickload '("alexandria" "serapeum" "bordeaux-threads"
    "lparallel" "dexador" "hunchentoot" "quri"
    "cl-ppcre" "mito"))

参考までに、startup file に入れるために Quicklisp が提供する snippet は次のとおりです。

;; quicklisp を ~/quicklisp/ にインストールしている場合
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
                                       (user-homedir-pathname))))
  (when (probe-file quicklisp-init)
    (load quicklisp-init)))

これを listener window に貼り付ける必要があります(C-y キー、y は “yank” の y です)。

インストール手順では、ダウンロードリンクを受け取るために HTML フォームへ入力し、次に terms と licence への同意を求める最初の script を実行し、さらにソフトウェアをインストールする 2 つ目の script を実行します。

ライセンスモデル

LispWorks には実際には 4 つの有料エディションがあります。ここで自分たちで説明しています: http://www.lispworks.com/products/lispworks.html。要約すると次のとおりです。

執筆時点では、hobbyist edition のライセンスは 750 USD、pro version はその倍です。これらは LW のバージョンごと、プラットフォームごとに購入します。時間制限はありません。

NB: upstream のリソースを必ず再確認し、遠慮なく問い合わせてください。

LispWorks IDE

LispWorks IDE は自己完結していますが、Emacs と Slime から LispWorks という処理系を使うこともできます(下記参照)。IDE は Common Lisp イメージの内部で動作します。これは、Swank と Slime を通じて Lisp イメージと通信する外部プログラムである Emacs とは異なります。ユーザコードは同じ process で実行されます。

エディタ

エディタは期待どおりの機能を提供します。TAB 補完の pop-up、syntax highlighting、Emacs 風 keybindings(M-x extended command を含む)です。メニューは機能発見を助けます。

個人的には、編集体験はやや「素朴」だと感じました。たとえば:

また、M-. に割り当てられた go-to-source 関数が組み込み Lisp symbols ではうまく動かないという問題もありました。どうやら LispWorks はあまり多くの source code を提供しておらず、主に editor のコードだけのようです。Allegro CL のような別の commercial Lisps は、より多くの source code を提供しています。

エディタには興味深いタブがあります: Changed Definitions です。これは、選択に応じて、セッションの最初の編集、最後の保存、最後の compile 以降に再定義された functions と methods を一覧します。

関連項目:

Keybindings

ほとんどの keybindings は Emacs と似ていますが、すべてではありません。いくつかの違いを挙げます。

似ているものには次があります。

便利な関数の中には、デフォルトでは keybinding がないものもあります。たとえば:

KDE/Gnome 風のclassical keybindingsを使うこともできます。Preferences menu、Environment、Emulation tab へ進みます。

Vim layer はありません

名前で keybindings を検索する

関数に対応する keybinding、または keybinding から関数名を検索できます。メニュー(Help -> Editing -> Key to Command / Command to Key)か、Emacs と同じく C-h に続いてキーを押します。たとえば C-h k と入力してから keybinding を入力すると、command name が得られます。詳しくは C-h ? を見てください。

IDE を調整する

keybindings は変更できます。エディタの状態は editor package からアクセスでき、エディタは CAPI framework で構築されているため、capi interface も使えます。便利な関数には次があります。

`
editor:bind-key
editor:defcommand
editor:current-point
editor:with-point  ;; point location を保存
editor:move-point
editor:*buffer-list*
editor:*in-listener* ;; REPL 内にいるとき T を返す
…

キーを bind する方法は次のとおりです。

;; 新しい行を indent する。
;; デフォルトでは、Return 後に point は indent されない。
(editor:bind-key "Indent New Line" #\Return :mode "Lisp")

;; pair を挿入する。
(editor:bind-key "Insert Parentheses For Selection" #\( :mode "Lisp")
(editor:bind-key "Insert Double Quotes For Selection"
   #\"
  :mode "Lisp")

新しい command を定義する方法は次のとおりです。) キーで次の閉じ括弧を越えるようにします。

(editor:defcommand "Move Over ()" (p)
  "Move past the next close parenthesis.
Any indentation preceeding the parenthesis is deleted."
  "Move past the next close parenthesis."
  ;; Thomas Hermann に感謝
  ;; https://github.com/ThomasHermann/LispWorks/blob/master/editor.lisp
  (declare (ignore p))
  (let ((point (editor:current-point)))
    (editor:with-point ((m point))
      (cond ((editor::forward-up-list m)
	     (editor:move-point point m)
             (editor::point-before point)
             (loop (editor:with-point ((back point))
                     (editor::back-to-indentation back)
                     (unless (editor:point= back point)
                       (return)))
                   (editor::delete-indentation point))
	     (editor::point-after point))
	    (t (editor:editor-error))))))

(editor:bind-key "Move Over ()" #\) :mode "Lisp")

special forms の indent を変更する方法は次のとおりです。

(editor:setup-indent "if" 1 4 1)

関連項目:

Listener

listener は期待どおりの REPL ですが、Slime とは少し違いがあります。

入力を行ごと、または form ごとに評価するのではなく、入力中に parse します。そのため、いくつかの error は即座に得られます。たとえば (abc と入力します。ここまでは問題ありません。次に colon を入力して (abc: にすると、入力のすぐ上に error message が表示されます。

Error while reading: Reader cannot find package ABC.

CL-USER 1 > (abc:

実際、ここで abc: は package を参照していますが、そのような package は存在しません。

対話的デバッガは主に text ベースですが、グラフィカルな要素でも操作できます。たとえば menu bar の Abort button を使うと top level に戻ります。graphical debugger を呼び出して stacktraces を見て操作することもできます。toolbar の一番端にある Debugger button を参照してください。

stacktraces に自分の関数名が表示されている場合(コードを REPL に直接書いたのではなく、ファイルに書いて compile していれば表示されます)、その名前を double-click すると editor に戻り、error を引き起こしたコードの部分が highlight されます。

NB: これは Slime で M-v を押すのと同等です。

textual debugger の代わりに、graphical debugger がデフォルトで現れるよう選ぶこともできます。

listener は、Slime の comma , で始まるものに少し似た helper commands を提供します。

CL-USER 1 > :help

:bug-form <subject> &key <filename>
         Print out a bug report form, optionally to a file.
:get <variable> <command identifier>
         Get a previous command (found by its number or a symbol/subform within it) and put it in a variable.
:help    Produce this list.
:his &optional <n1> <n2>
         List the command history, optionally the last n1 or range n1 to n2.
:redo &optional <command identifier>
         Redo a previous command, found by its number or a symbol/subform within it.
:use <new> <old> &optional <command identifier>
         Do variant of a previous command, replacing old symbol/subform with new symbol/subform.

Stepper。Breakpoints。

stepper は、LispWorks が光る領域の一つです。

editor window でコードを書いているとき、大きな赤い “Breakpoint” button(または M-x Stepper Breakpoint)で breakpoints を設定できます。これによりコードに赤い mark が付きます。

次にそのコードが実行されると、包括的な Stepper pop-up window が表示されます。内容は次のとおりです。

それだけではありません。visual ではない、REPL 向け stepper も便利です。評価中の forms とその results を表示します。

この例では、:s を使って現在の form と subforms を “step” します。通常の listener を使っており、prompt(ここでは小さな ` -> )の後に任意の Lisp code を書けます。また local variables(X`)にもアクセスできます。

CL-USER 4 > (defun my-abs (x)
              (cond ((> x 0) x) ((< x 0) (- x)) (t 0)))
CL-USER 5 > (step (my-abs -5))
(MY-ABS -5) -> :s
   -5 -> :s
   -5
   (COND ((> X 0) X) ((< X 0) (- X)) (T 0)) <=> (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0)))
   ;; local variables へのアクセス:
   (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0))) -> (format t "Is X equal to -5? ~a~&" (if (equal x -5) "yes" "no"))
Is X equal to -5? yes
   (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0))) -> :s
      (> X 0) -> :s
         X -> :s
         -5
         0 -> :s
         0
      NIL
      (IF (< X 0) (- X) (PROGN 0)) -> :s
         (< X 0) -> :s
            X -> :s
            -5
            0 -> :s
            0
         T
         (- X) -> :s
            X -> :s
            -5
         5
      5
   5
5

利用可能な stepper commands は次のとおりです(:? を参照)。

:s       Step this form and all of its subforms (optional +ve integer arg)
:st      Step this form without stepping its subforms
:si      Step this form without stepping its arguments if it is a function call
:su      Step up out of this form without stepping its subforms
:sr      Return a value to use for this form
:sq      Quit from the current stepper level
:bug-form <subject> &key <filename>
         Print out a bug report form, optionally to a file.
:get <variable> <command identifier>
         Get a previous command (found by its number or a symbol/subform within it) and put it in a variable.
:help    Produce this list.
:his &optional <n1> <n2>
         List the command history, optionally the last n1 or range n1 to n2.
:redo &optional <command identifier>
         Redo a previous command, found by its number or a symbol/subform within it.
:use <new> <old> &optional <command identifier>
         Do variant of a previous command, replacing old symbol/subform with new symbol/subform.

Class browser

class browser を使うと、class の slots、parent classes、利用可能な methods などを調べられます。

単純な class を作りましょう。

(defclass person ()
  ((name :accessor name
         :initarg :name
         :initform "")
   (lisper :accessor lisperp
           :initform t)))

次に class browser を呼び出します。

これはいくつかの panes で構成されています。

Functions pane は、その class に適用可能なすべての methods を一覧するため、CLOS object system が提供する public methods を見つけられます。たとえば initialize-instanceprint-objectshared-initialize などです。double-click すると source に移動できます。継承された methods を含めないよう選ぶこともできます(”include inherited” checkbox を参照)。

toolbar には buttons(たとえば generic function を Inspect するもの)があり、Methods menu にはさらに actions があります。たとえば function calls を見る方法、function を undefine または trace する menu などです。

さらに読む:

Function call browser

function call browser を使うと、ある function の callers と callees の graph を見られます。表示情報を filter したり、call stack をさらに inspect したりする方法がいくつか用意されています。

NB: そのような cross-references を探す Slime の関数は slime-who-[calls, references, binds, sets, depends-on, specializes, macroexpands] です。

いくつかの packages をロードした後、string-trim 関数を誰が呼んでいるかを示す単純な例です。

The function call browser

すべての packages から functions を表示しますが、たとえば “current and used” や current packages のみに制限する select box があります。

graph に表示された function を double click すると source に移動します。多くの LispWorks views と同様に、Function menu では選択した functions をさらに操作できます。trace、undefine、listen(object を Listener に貼り付ける)などです。

Text tab は同じ情報を textually に、callers と callees を横並びで表示します。

compiled code について cross references を見ることができ、その機能が有効になっていることを確認する必要があります。code を compile すると、LispWorks は次のような compilation output を表示します。

;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 1
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is  on
;;; Cross referencing is on

cross referencing が on であることが分かります。そうでなければ (toggle-source-debugging t) で有効にします。

さらに読む:

Process Browser

Process Browser は実行中の全 threads の一覧を表示します。入力欄では name で filter できます。regular expressions を受け付けます。その後、これらの processes を stop、inspect、listen、break into できます。

"The process browser"

さらに読む:

イメージの保存

LispWorks で image を保存する方法は SBCL とは異なります。

つまり実質的に、image を保存すれば開発環境を同じ状態に戻せます。現在の作業の snapshots を取り、離れた場所から続けられるわけです。

たとえば REPL からゲームを開始し、その window で少し遊び、image を保存すると、復元時にそのゲームと状態が戻ってきます。

その他

Search Files 機能は気に入っています。recursive grep のようなものですが、結果を表示し、double-click でき、さらにいくつかの actions を提供する典型的な LispWorks graphical window が得られます。

最後に、compilation conditions browser も見てください。system を compile すると、LispWorks はすべての warnings と errors を特別な browser に入れます。それ以降は、それらを修正し、browser から消えていくのを見ながら作業できます。開発中に warnings と errors を追跡し続ける助けになります。

Emacs と Slime から LispWorks を使う

これには 2 つの方法があります。1 つ目は、LispWorks を通常どおり起動し、Swank server を開始して Emacs から接続する方法です(Swank は Slime の backend 部分です)。

まず dependencies をロードします。

(ql:quickload "swank")
;; or
(load "~/.emacs.d/elpa/slime-20xx/swank-loader.lisp")

server を開始します。

(swank:create-server :port 9876)
;; Swank started at port: 9876.
9876

Emacs から M-x slime-connect を実行し、host に localhost、port に 9876 を選びます。

接続されているはずです。(lisp-implementation-type) で確認してください。これで LispWorks の機能を使えます。

(setq button
      (make-instance 'capi:push-button
                     :data "Button"))
(capi:contain button)

2 つ目の方法は、Swank をロードした non-GUI LispWorks image を作成し、この image を SLIME または SLY から実行することです。たとえば multiprocessing を有効にした、いわゆる console image を作るには:

(in-package "CL-USER")
(load-all-patches)
(save-image "~/lw-console"
            :console t
            :multiprocessing t
            :environment nil)

そして新しい image ~/lw-console を作成するため、LispWorks を次のように実行します。

lispworks-7-0-0-x86-linux -build /tmp/resave.lisp

ただし、consoleWindows と Mac でのみ実装されています。

LispWorks の documentation を参照してください。

アプリケーションを配布する

LispWorks の delivery method は delivery 関数を中心にしています。よい documentation があります: https://www.lispworks.com/documentation/lw80/deliv/deliv.htm

他の open-source Lisps と違い、LispWorks は tree-shaker を提供します。これにより配布アプリケーションから packages を取り除き、7MB 前後の小さな binaries を作れます。

Delivery の制限

LispWorks の delivery は、配布アプリケーションに compile-file含みませんsave-imagedeliver、IDE も含みません)。そのため、配布済み image 上で動的に code を変更することはできません。Swank server もなく、ql:quickload を使う可能性もありません。

ただし remote debugging を可能にするために、LW は独自の debugger client を提供します。backend 側では次を実行します。

(require "remote-debugger-client")
(dbg:start-client-remote-debugging-server :announce t)

IDE 側では次を実行します。

(require "remote-debugger-full")
(dbg:ide-connect-remote-debugging "host" :open-a-listener t)

関連項目

Page source: ja/lispworks.md

T
O
C