clojure.main名前空間

clojure.main の名前空間はJavaのアプリケーションランチャーツール java からClojureプログラムやインタラクティブなセッションを起動するための関数を提供している。

clojure.main --help

clojure.main/main のエントリーポイントは多様な引数とフラグを受けとる:

  • オプションと引数の指定がない場合、インタラクティブな REPL (Read-Eval-Print Loop) を実行する

  • initオプション:

    • -i, --init パス ファイル、もしくはリソースをロードする

    • -e, --eval 文字列 文字列中の式を評価する; 非nilな値をプリントする

    • --report target Report uncaught exception to "file" (default), "stderr", or "none", overrides System property clojure.main.report (added in 1.10.1)

  • mainオプション:

    • -r, --repl replを実行する

    • path ファイル、もしくはリソースからスクリプトを実行する

    • - 標準入力からスクリプトを実行する

    • -m, --main 実行対象の-main関数を見つけるための名前空間

    • -h, -?, --help このメッセージをプリントして終了する

  • 操作:

    • 一般的に利用されるset!操作が可能なvarに対してスレッドローカルな束縛を行う

    • user名前空間に切り替える

    • Binds *command-line-args* to a seq of strings containing command line args that appear after any main option

    • 全てのinitオプションを順に実行する

    • repl、もしくは指定があればスクリプトを実行する

initオプションは繰り返しても良いし任意に組み合せることができるが、mainオプションよりも前に指定する必要がある。replの実行前にevalのオプションが指定されている場合、通常のreplのグリーティングメッセージが表示されなくなる: "Clojure ~(clojure-version)"

パスは絶対パス、もしくはファイルシステムの相対パス、クラスパスに対する相対パスを指定できる。クラスパスに対する相対パスは @ もしくは @/ でプリフィックスされる。

REPLの起動

The simplest way to launch a Clojure repl is to use the clj command tool, which invokes clojure.main:

$ clj
Clojure 1.10.0
user=>

REPLプロンプトは現在の名前空間の名前 (*ns*) を表示し、デフォルトは user である。

REPLを利用している際には、特別なvarをいくつか利用できる:

  • *1, *2, *3 - 評価済みの最後の3つの式の結果を保持する

  • *e - 最後に発生した例外の結果を保持する

clojure.repl 名前空間には、利用可能な関数のソースやドキュメントの閲覧に有用な関数がいくつもある:

  • doc

    • 指定された名前のvarのdocstringをプリントする

  • find-doc

    • 指定されたパターンに一致するdocか名前を持つ全てのvarのdocstringをプリントする。

  • apropos

    • regexにマッチする定義のseqを返す

  • source

    • シンボルのソースをプリントする

  • pst

    • 指定された例外、もしくは *eのスタックトレースをプリントする (print stack trace)

スクリプトの起動

Clojureコードが書かれたファイルをスクリプトとして実行するには、スクリプトのパスを clojure.main の引数として渡す:

clj /path/to/myscript.clj

スクリプトに引数を渡す

スクリプトの引数は clojure.main を起動する際の追加の引数として渡す:

clj /path/to/myscript.clj arg1 arg2 arg3

これらの引数はプログラム内のvar *command-line-args* に束縛された文字列のseqとしてプログラムに提供される:

*command-line-args* => ("arg1" "arg2" "arg3")

Error printing

At REPL

As of Clojure 1.10, Clojure errors categorized into one of several phases:

  • :read-source - an error thrown while reading characters at the REPL or from a source file.

  • :macro-syntax-check - a syntax error found in the syntax of a macro call, either from spec or from a macro throwing IllegalArgumentException, IllegalStateException, or ExceptionInfo.

  • :macroexpansion - all other errors thrown during macro evaluation are categorized as macroexpansion errors.

  • :compile-syntax-check - a syntax error caught during compilation.

  • :compilation - non-syntax errors caught during compilation.

  • :execution - any errors thrown at execution time.

  • :read-eval-result - any error thrown while reading the result of execution (only applicable for REPLs that read the result).

  • :print-eval-result - any error thrown while printing the result of execution.

Exceptions thrown during all phases (exception :execution) will have ex-data attached with one or more the following keys:

  • :clojure.error/phase - phase indicator

  • :clojure.error/source - file name (no path)

  • :clojure.error/line - integer line number

  • :clojure.error/column - integer column number

  • :clojure.error/symbol - symbol being expanded/compiled/invoked

  • :clojure.error/class - cause exception class symbol

  • :clojure.error/cause - cause exception message

  • :clojure.error/spec - explain-data for a spec error

The clojure.main REPL includes the categorization and printing of errors by default, but the individual steps of this process are exposed as well for other REPLs to use, specifically the functions:

  • Throwable->map

    • converts an Exception chain into Clojure data

  • ex-triage

    • analyzes Clojure exception data to pull relevant information from the top and bottom of the exception chain into a map describing just the set of data needed to format an exception string

  • ex-str

    • produces a phase-appropriate message given a set of exception data

The clojure.main REPL combines these functions in a pipeline to produce the printed exception message: (-> ex Throwable->map clojure.main/ex-triage clojure.main/ex-str). Other REPLs can use one or more pieces of this pipeline as necessary when building or customizing their exception printing.

As launcher

Up to Clojure 1.10.0, clojure.main when used as a program launcher (with -m, -e, or with a script), uncaught exceptions would be automatically printed along with the full nested stack trace. In this case, the error triage and printing process above was not applied.

As of Clojure 1.10.1, uncaught exceptions will now be caught and printed according to the same error triage and printing functionality as the Clojure REPL. The full stack trace, ex-info, and other information will be printed to a target specified by the configuration.

The three available error targets are:

  • file - write to a temp file (default, falls back to stderr)

  • stderr - write to stderr stream

  • none - don’t write

These error targets can be specified either as options to clojure.main, or as Java system properties (flags take precedence). When invoking clojure.main (or using the clj tool), use --report <target>. For Java system property, use -Dclojure.main.report=<target>.

Other programs may wish to take advantage of this functionality, and it is available in report-error, which takes a Throwable and optionally the :target.

tap

tap is a shared, globally accessible system for distributing a series of informational or diagnostic values to a set of (presumably effectful) handler functions. It can be used as a better debug prn, or for facilities like logging etc.

tap> sends a value to the set of taps. Taps can be added with add-tap and will be called with any value sent to tap>. The tap function may (briefly) block (e.g. for streams) and will never impede calls to tap>, but blocking indefinitely may cause tap values to be dropped. If no taps are registered, tap> discards. Remove taps with remove-tap.

ソケットサーバーの起動

現在のClojureランタイムではシステムプロパティを基に、初期化時にソケットサーバーを起動することが可能となっている。想定される活用方法の一つはソケットベースのREPLだが、他にも既存のプログラムのコードの変更を行わずに動的にサーバー機能を追加する様々なことに利用可能なポテンシャルがある。

"clojure.server.<server-name>" のようなJVMシステムプロパティ全てに対してソケットサーバーが起動される。このプロパティの値には、ソケットサーバーの設定を表現する、下記のプロパティを持つednのマップを指定する:

  • server-daemon - デフォルトでtrue、ソケットサーバーのスレッドがexitをブロックしない

  • address - ホストもしくはアドレス、デフォルトはループバック

  • port - 正の整数、必須

  • accept - ソケットの受理時に呼び出される関数の名前空間付きのシンボル、必須

  • args - acceptに渡す引数のシーケンシャルなコレクション

  • bind-err - デフォルトtrue、 *err* をソケットの出力ストリームに束縛する

  • client-daemon - デフォルトでtrue、ソケットのクライアントのスレッドがexitをブロックしない

加えて、ソケットサーバーでの利用のために少々カスタマイズされているrepl関数が clojure.core.server/repl にある。

以下はソケットサーバーをreplリスナーとともに起動する例だ。これを任意のClojureプログラムに追加することで、外部からポート5555のローカルコネクションを経由したREPLクライアントを受理することが可能になる。

-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"

このソケットreplには、例えばtelnetをクライアントとして接続することができる:

$ telnet 127.0.0.1 5555
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
user=> (println "hello")
hello

特別なコマンド :repl/quit を使用することで、サーバーに対してクライアントのソケットreplセッションのクローズを指示することができる:

user=> :repl/quit
Connection closed by foreign host.

以下も参照:

関連する関数

Mainエントリーポイント: clojure.main/main

再利用可能なREPL: clojure.main/repl

一般的なREPL varへのset!を許可: clojure.main/with-bindings

ソケットrepl: clojure.core.server/repl