Clojure

Clojure文字の読み取り

このページでは、「Google検索」が難しいClojureの構文について説明します。セクションは特定の順序ではありませんが、関連する項目は理解しやすくするためにグループ化されています。Clojureリーダーに関する権威あるリファレンスとして、リーダーリファレンスページを参照してください。このガイドは、James Hughesの元のブログ投稿に基づいており、著者の許可を得て更新および拡張されています。

( …​ ) - リスト

リストは、連結リストとして実装された順序付けられた異種コレクションです。

3つの値のリスト

(1 "two" 3.0)

[ …​ ] - ベクトル

ベクトルは、順序付けられた、インデックス付きの、異種コレクションです。インデックスは0ベースです。

3つの値のベクトルのインデックス1の値を取得する例

user=> (get ["a" 13.7 :foo] 1)
13.7

{ …​ } - マップ

マップは、交互にキーと値を指定した異種コレクションです。

user=> (keys {:a 1 :b 2})
(:a :b)

# - ディスパッチ文字

この文字は、別の文字の横に表示されます(例:#(または#")。

#は、Clojureリーダー(Clojureソースを取り込み、Clojureデータとして「読み取る」コンポーネント)に、*読み込みテーブル*を使用して次の文字をどのように解釈するかを指示する特殊文字です。一部のLispでは、読み込みテーブルをユーザーが拡張できますが、Clojureではできません

#は、シンタックスクォート内で生成されたシンボルを作成する場合、シンボルの*末尾*にも使用されます。

#{ …​ } - セット

詳細は#を参照してください。

#{…​}は、セット(一意の値のコレクション)、具体的にはhash-setを定義します。以下は同等です。

user=> #{1 2 3 4}
#{1 2 3 4}
user=> (hash-set 1 2 3 4)
#{1 2 3 4}

セットには重複を含めることができないため、setリーダーは、無効なリテラルであるため、この場合例外をスローします。アイテムがセットに追加されると、値が既に存在する場合は単に削除されます。

user=> #{1 2 3 4 1}
Syntax error reading source at (REPL:83:13).
Duplicate key: 1

#_ - 破棄

詳細は#を参照してください。

#_は、リーダーに次のフォームを完全に無視するように指示します。

user=> [1 2 3 #_ 4 5]
[1 2 3 5]

#_の後のスペースは省略可能です。

user=> [1 2 3 #_4 5]
[1 2 3 5]

も機能します。また、破棄文字はednでも機能します。

便利なテクニックとして、複数の#_を積み重ねて複数のフォームを省略することができます。

user=> {:a 1, #_#_ :b 2, :c 3}
{:a 1, :c 3}

ドキュメントでは、「#_の後のフォームはリーダーによって完全にスキップされます(これは、nilを生成するcommentマクロよりも完全な削除です)。」と示唆されています。これは、デバッグ状況や複数行コメントに役立ちます。

#"…​" - 正規表現

詳細は#を参照してください。

#"は正規表現の開始を示します。

user=> (re-matches #"^test$" "test")
"test"

このフォームは、*読み込み時*にホスト固有の正規表現機構にコンパイルされますが、ednでは使用できません。Clojureで正規表現を使用する場合、Java文字列のエスケープは必要ありません。

#(…​) - 匿名関数

詳細は#を参照してください。

#(は、インライン関数定義の簡略構文の開始です。次の2つのコードスニペットは似ています。

; anonymous function taking a single argument and printing it
(fn [line] (println line))

; anonymous function taking a single argument and printing it - shorthand
#(println %)

リーダーは、匿名関数を関数定義に展開します。そのアリティ(取る引数の数)は、%プレースホルダーの宣言方法によって定義されます。アリティに関する説明については、%文字を参照してください。

user=> (macroexpand `#(println %))
(fn* [arg] (clojure.core/println arg)) ; argument names shortened for clarity

#' - Varクォート

#'は、var関数の呼び出しに展開されるVarクォートです。

user=> (read-string "#'foo")
(var foo)
user=> (def nine 9)
#'user/nine
user=> nine
9
user=> (var nine)
#'user/nine
user=> #'nine
#'user/nine

使用されると、参照されたVarを返すことを試みます。これは、それが表す値ではなく、参照/宣言について話したい場合に役立ちます。メタデータ(^)の議論におけるmetaの使用を参照してください。

Varクォートはednでは使用できません。

## - シンボリック値

Clojureは、シンボリック値##Inf##-Inf##NaNを読み書きできます。これらはednでも使用できます。

user=> (/ 1.0 0.0)
##Inf
user=> (/ -1.0 0.0)
##-Inf
user=> (Math/sqrt -1.0)
##NaN

#inst#uuid#jsなど - タグ付きリテラル

タグ付きリテラルはednで定義され、ClojureおよびClojureScriptリーダーによってネイティブにサポートされています。#instおよび#uuidタグはednによって定義されますが、#jsタグはClojureScriptによって定義されます。

Clojureのread-stringを使用してタグ付きリテラルを読み取ることができます(または直接使用することもできます)。

user=> (type #inst "2014-05-19T19:12:37.925-00:00")
java.util.Date ;; this is host dependent
user=> (read-string "#inst \"2014-05-19T19:12:37.925-00:00\"")
#inst "2014-05-19T19:12:37.925-00:00"

タグ付きリテラルは、リーダーにリテラル値をどのように解析するかを指示します。他の一般的な用途には、UUIDを表す#uuidが含まれ、ClojureScriptの世界では、タグ付きリテラルの非常に一般的な用途は#jsであり、これを使用してClojureScriptデータ構造をJavaScript構造に直接変換できます。#jsは再帰的に変換されないため、ネストされたデータ構造がある場合は、clj->jsを使用してください。

#inst#uuidはednで使用できますが、#jsは使用できません。

%%n%& - 匿名関数引数

%は、#(* % %)のような匿名関数#(...)の引数です。

匿名関数が展開されると、fnフォームになり、%引数はgensymされた名前に置き換えられます(ここでは、可読性のためにarg1などを使用します)。

user=> (macroexpand `#(println %))
(fn* [arg1] (clojure.core/println arg1))

引数の位置(1ベース)を示すために、数字を%の直後に配置できます。匿名関数のアリティは、最も高い番号の%引数に基づいて決定されます。

user=> (#(println %1 %2) "Hello " "Clojure")
Hello Clojure ; takes 2 args
user=> (macroexpand `#(println %1 %2))
(fn* [arg1 arg2] (clojure.core/println arg1 arg2)) ; takes 2 args

user=> (#(println %4) "Hello " "Clojure " ", Thank " "You!!")
You!! ; takes 4 args, doesn't use first 3 args
user=> (macroexpand `#(println %4))
(fn* [arg1 arg2 arg3 arg4] (clojure.core/println arg4)) ; takes 4 args doesn't use 3

引数を使用する必要はありませんが、外部の呼び出し元が渡すことを期待する順序で宣言する必要があります。

%%1は互換性があります。

user=> (macroexpand `#(println % %1)) ; use both % and %1
(fn* [arg1] (clojure.core/println arg1 arg1)) ; still only takes 1 argument

可変長匿名関数で「残りの」引数(最も高い名前付き匿名引数の後)を表すために使用される%&もあります。

user=> (#(println %&) "Hello " "Clojure " ", Thank " "You!!")
(Hello Clojure , Thank You!! ) ; takes n args
user=> (macroexpand '#(println %&))
(fn* [& rest__11#] (println rest__11#))

匿名関数と%はednの一部ではありません。

@ - Deref

@deref関数の呼び出しに展開されるため、この2つのフォームは同じです。

user=> (def x (atom 1))
#'user/x
user=> @x
1
user=> (deref x)
1
user=>

@は、参照の現在の値を取得するために使用されます。上記の例では、@を使用してatomの現在の値を取得していますが、計算を強制し、場合によってはブロックするために、@futuredelaypromisesなどにも適用できます。

@はednでは使用できません。

^ (および#^) - メタデータ

^はメタデータマーカーです。メタデータは、Clojureのさまざまなフォームに添付できる値のマップ(省略記法付き)です。これはこれらのフォームに追加情報を提供し、ドキュメント、コンパイル時の警告、タイプヒント、その他の機能に使用できます。

user=> (def ^{:debug true} five 5) ; meta map with single boolean value
#'user/five

meta関数を使用してメタデータにアクセスできます。これは、返された値ではなく、宣言自体に対して実行する必要があります。

user=> (def ^{:debug true} five 5)
#'user/five
user=> (meta #'five)
{:ns #<Namespace user>, :name five, :column 1, :debug true, :line 1, :file "NO_SOURCE_PATH"}

ここでは単一の値を使用しているため、メタデータ^:nameを宣言するための省略記法を使用できます。これは、値がtrueに設定されるフラグに役立ちます。

user=> (def ^:debug five 5)
#'user/five
user=> (meta #'five)
{:ns #<Namespace user>, :name five, :column 1, :debug true, :line 1, :file "NO_SOURCE_PATH"}

^のもう1つの用途は、タイプヒントです。これらは、コンパイラに値の型を伝え、型固有の最適化を実行できるようにするため、結果のコードを高速化できます。

user=> (def ^Integer five 5)
#'user/five
user=> (meta #'five)
{:ns #<Namespace user>, :name five, :column 1, :line 1, :file "NO_SOURCE_PATH", :tag java.lang.Integer}

その例では、:tagプロパティが設定されていることがわかります。

省略記法を積み重ねることもできます。

user=> (def ^Integer ^:debug ^:private five 5)
#'user/five
user=> (meta #'five)
{:ns #<Namespace user>, :name five, :column 1, :private true, :debug true, :line 1, :file "NO_SOURCE_PATH", :tag java.lang.Integer}

元々は、メタデータは#^で宣言されていましたが、現在は非推奨になっています(ただし、まだ機能します)。その後、これは単なる^に簡素化され、ほとんどのClojureでそれが使用されますが、古いコードでは時折#^構文に出くわすことがあります。

メタデータはednで使用できますが、タイプヒントは使用できません。

' - クォート

クォートは、次のフォームを読み込むが評価しないことを示すために使用されます。リーダーは'quote特殊フォームへの呼び出しに展開します。

user=> (1 3 4) ; fails as it tries to invoke 1 as a function

Execution error (ClassCastException) at myproject.person-names/eval230 (REPL:1).
class java.lang.Long cannot be cast to class clojure.lang.IFn

user=> '(1 3 4) ; quote
(1 3 4)

user=> (quote (1 2 3)) ; using the longer quote method
(1 2 3)
user=>

; - コメント

;は行コメントを開始し、その開始点から行末までのすべての入力を無視します。

user=> (def x "x") ; this is a comment
#'user/x
user=> ; this is a comment too
<returns nothing>

Clojureでは、可読性や強調のために複数のセミコロンを使用することが一般的ですが、Clojureにとってはすべて同じです。

;; This is probably more important than

; this

: - キーワード

:はキーワードを示す指標です。キーワードはマップのキーとして頻繁に使用され、文字列よりも高速な比較と低いメモリオーバーヘッドを提供します(インスタンスがキャッシュされ、再利用されるため)。

user=> (type :test)
clojure.lang.Keyword

あるいは、keyword関数を使用して、文字列からキーワードを作成することもできます。

user=> (keyword "test")
:test

キーワードは、マップ内のキーとして自分自身を検索する関数として呼び出すこともできます。

user=> (def my-map {:one 1 :two 2})
#'user/my-map
user=> (:one my-map) ; get the value for :one by invoking it as function
1
user=> (:three my-map) ; it can safely check for missing keys
nil
user=> (:three my-map 3) ; it can return a default if specified
3
user => (get my-map :three 3) ; same as above, but using get
3

:: - 自動解決キーワード

::は、現在の名前空間でキーワードを自動解決するために使用されます。修飾子が指定されていない場合、現在の名前空間に自動解決されます。修飾子が指定されている場合、現在の名前空間のエイリアスを使用することがあります。

user=> :my-keyword
:my-keyword
user=> ::my-keyword
:user/my-keyword
user=> (= ::my-keyword :my-keyword)
false

これはマクロを作成する場合に役立ちます。マクロ名前空間内の別の関数を呼び出すマクロが、関数を正しく呼び出すように展開されるようにしたい場合、::my-functionを使用して完全修飾名を参照できます。

::はednでは使用できないことに注意してください。

#:#:: - 名前空間マップ構文

名前空間マップ構文はClojure 1.9で追加され、マップ内のキーやシンボルが共通の名前空間を共有する場合のデフォルトの名前空間コンテキストを指定するために使用されます。

#:ns構文は、完全修飾された名前空間マッププレフィックスnを、名前空間マッププレフィックスでエイリアスを指定します。ここで、nsは名前空間の名前であり、プレフィックスはマップの開始中括弧{の前に置かれます。

たとえば、次の名前空間構文を持つマップリテラル

#:person{:first "Han"
         :last "Solo"
         :ship #:ship{:name "Millennium Falcon"
                      :model "YT-1300f light freighter"}}

は、次のように読み取られます。

{:person/first "Han"
 :person/last "Solo"
 :person/ship {:ship/name "Millennium Falcon"
               :ship/model "YT-1300f light freighter"}}

これらのマップは同一のオブジェクトを表していることに注意してください。これらは単なる代替構文です。

#::は、現在の名前空間を使用して、マップ内のキーワードまたはシンボルのキーの名前空間を自動解決するために使用できます。

これらの2つの例は同等です。

user=> (keys {:user/a 1, :user/b 2})
(:user/a :user/b)
user=> (keys #::{:a 1, :b 2})
(:user/a :user/b)

自動解決キーワードと同様に、#::aliasを使用して、nsフォームで定義された名前空間エイリアスで自動解決することもできます。

(ns rebel.core
  (:require
    [rebel.person :as p]
    [rebel.ship   :as s] ))

#::p{:first "Han"
     :last "Solo"
     :ship #::s{:name "Millennium Falcon"
                :model "YT-1300f light freighter"}}

は、次と同じように読み取られます。

{:rebel.person/first "Han"
 :rebel.person/last "Solo"
 :rebel.person/ship {:rebel.ship/name "Millennium Falcon"
                     :rebel.ship/model "YT-1300f light freighter"}}

/ - 名前空間セパレータ

/は除算関数clojure.core//になることもありますが、シンボルの名前と名前空間修飾子を分離するシンボル名でのセパレータとしても機能します(例:my-namespace/utils)。したがって、名前空間修飾子は、単純な名前の命名衝突を防ぐことができます。

\ - 文字リテラル

\は、次のように文字リテラルを示します。

user=> (str \h \i)
"hi"

特殊なASCII文字を指定するための特殊文字も少量あります。\newline\space\tab\formfeed\backspace\returnです。

\の後に、\uNNNN形式のUnicodeリテラルを付けることもできます。たとえば、\u03A9はΩのリテラルです。

$ - 内部クラス参照

Javaの内部クラスとインターフェースを参照するために使用されます。コンテナクラス名と内部クラス名を区切ります。

(import (basex.core BaseXClient$EventNotifier)

(defn- build-notifier [notifier-action]
  (reify BaseXClient$EventNotifier
    (notify [this value]
      (notifier-action value))))

EventNotifierは、インポートされたJavaクラスであるBaseXClientクラスの内部インターフェースです。

->->>some->cond->as->など - スレッディングマクロ

これらはスレッディングマクロです。Clojure公式ドキュメントを参照してください。

` - シンタックスクォート

`はシンタックスクォートです。シンタックスクォートはクォート(評価を遅延させる)に似ていますが、いくつかの追加効果があります。

基本的なシンタックスクォートは、通常のクォートと似ているように見えます。

user=> (1 2 3)
Execution error (ClassCastException) at myproject.person-names/eval232 (REPL:1).
class java.lang.Long cannot be cast to class clojure.lang.IFn
user=> `(1 2 3)
(1 2 3)

ただし、シンタックスクォート内で使用されるシンボルは、現在の名前空間に関して完全に解決されます。

user=> (def five 5)
#'user/five
user=> `five
user/five

シンタックスクォートは、マクロ内の「テンプレート」メカニズムとして最もよく使用されます。今すぐ1つ書くことができます。

user=> (defmacro debug [body]
  #_=>   `(let [val# ~body]
  #_=>      (println "DEBUG: " val#)
  #_=>      val#))
#'user/debug
user=> (debug (+ 2 2))
DEBUG:  4
4

マクロは、コードをデータとして受け取るコンパイラによって呼び出される関数です。コード(データとして)を返し、さらにコンパイルして評価できることが期待されています。このマクロは単一の本体式を受け取り、本体を評価し、その値を出力し、その値を返すletフォームを返します。ここでは、シンタックスクォートはリストを作成しますが、評価しません。そのリストは実際にはコードです。

~@~を参照して、シンタックスクォート内でのみ許可される追加構文を確認してください。

~ - アンォート

`を参照して、追加情報を確認してください。

~はアンクォートです。シンタックスクォートはクォートと同様に、シンタックスクォートされたフォーム内では評価が行われません。アンクォートはクォートをオフにし、シンタックスクォートされた式内の式を評価します。

user=> (def five 5) ; create a named var with the value 5
#'user/five
user=> five ; the symbol five is evaluated to its value
5
user=> `five ; syntax quoting five will avoid evaluating the symbol, and fully resolve it
user/five
user=> `~five ; within a syntax quoted block, ~ will turn evaluation back on just for the next form
5
user=> `[inc ~(+ 1 five)]
[clojure.core/inc 6]

シンタックスクォートとアンクォートは、コードを受け取ってコードを返すコンパイル時に呼び出される関数であるマクロを作成するための不可欠なツールです。

~@ - アンォートスプライシング

`~を参照して、追加情報を確認してください。

~@はアンクォートスプライシングです。アンクォート(~)はフォームを評価して結果をクォートされた結果に配置しますが、~@は評価された値がコレクションであることを期待し、そのコレクションの内容をクォートされた結果にスプライスします。

user=> (def three-and-four (list 3 4))
#'user/three-and-four
user=> `(1 ~three-and-four) ; evaluates `three-and-four` and places it in the result
(1 (3 4))
user=> `(1 ~@three-and-four) ;  evaluates `three-and-four` and places its contents in the result
(1 3 4)

これもまた、マクロを作成するための強力なツールです。

<symbol># - ゲンシン

シンボルの末尾にある#は、新しいシンボルを自動的に生成するために使用されます。これは、マクロ内で、マクロ固有のものがユーザー空間へ漏れるのを防ぐために役立ちます。通常のletはマクロ定義では失敗します。

user=> (defmacro m [] `(let [x 1] x))
#'user/m
user=> (m)
Syntax error macroexpanding clojure.core/let at (REPL:1:1).
myproject.person-names/x - failed: simple-symbol? at: [:bindings :form :local-symbol]
  spec: :clojure.core.specs.alpha/local-name
myproject.person-names/x - failed: vector? at: [:bindings :form :seq-destructure]
  spec: :clojure.core.specs.alpha/seq-binding-form
myproject.person-names/x - failed: map? at: [:bindings :form :map-destructure]
  spec: :clojure.core.specs.alpha/map-bindings
myproject.person-names/x - failed: map? at: [:bindings :form :map-destructure]
  spec: :clojure.core.specs.alpha/map-special-binding

これは、シンタックスクォート内のシンボルは、ここのローカルバインディングxを含む、完全に解決されるためです。

代わりに、変数名の末尾に#を追加し、Clojureが一意の(非修飾)シンボルを生成させます。

user=> (defmacro m [] `(let [x# 1] x#))
#'user/m
user=> (m)
1
user=>

重要なのは、特定のx#が単一のシンタックスクォート内で使用されるたびに、同じ生成名が使用されることです。

このマクロを展開すると、gensymで生成された名前を確認できます。

user=> (macroexpand '(m))
(let* [x__681__auto__ 1] x__681__auto__)

#? - リーダー条件

リーダー条件は、異なるClojureの方言が共通のコードを共有できるように設計されています。リーダー条件は、従来のcondと同様に動作します。使用構文は#?であり、次のようになります。

#?(:clj  (Clojure expression)
   :cljs (ClojureScript expression)
   :cljr (Clojure CLR expression)
   :default (fallthrough expression))

#?@ - スプライシングリーダー条件

スプライシングリーダー条件の構文は#?@です。これはリストを包含するフォームにスプライスするために使用されます。そのため、Clojureリーダーはこれを次のように読み取ります。

(defn build-list []
  (list #?@(:clj  [5 6 7 8]
            :cljs [1 2 3 4])))

これはこれと同じです。

(defn build-list []
  (list 5 6 7 8))

*var-name* - 「イヤーマフ」

イヤーマフ(アスタリスクのペアでvar名を囲む)は、多くのLISPで使用される命名規則であり、特殊varを示します。Clojureでは、最も一般的に動的var、つまり動的スコープによって変化する可能性のあるvarを示すために使用されます。イヤーマフは、「ここにドラゴンがいる」という警告として機能し、varの状態を絶対に想定しないようにします。これは規則ではなく規約であることを忘れないでください。

Clojureのコア例には、Clojureの標準入出力ストリームを表す*out**in*が含まれています。

>!!<!!>!<! - core.asyncチャンネルマクロ

これらのシンボルは、チャンネルベースの非同期プログラミング(特にCSP - Communicating Sequential Processes)のためのClojure/ClojureScriptライブラリであるcore.asyncのチャンネル操作です。

仮に、チャンネルが物事を追加したり削除したりできるキューのようなものだとすると、これらのシンボルはその単純なAPIをサポートします。

  • >!!<!!はそれぞれブロッキングputtakeです。

  • >!<!は、単にputtakeです。

違いは、ブロッキングバージョンはgoブロックの外で動作し、動作しているスレッドをブロックすることです。

user=> (def my-channel (chan 10)) ; create a channel
user=> (>!! my-channel "hello")   ; put stuff on the channel
user=> (println (<!! my-channel)) ; take stuff off the channel
hello

非ブロッキングバージョンはgoブロック内で実行する必要があります。それ以外の場合、例外がスローされます。

user=> (def c (chan))
#'user/c
user=> (>! c "nope")
AssertionError Assert failed: >! used not in (go ...) block
nil  clojure.core.async/>! (async.clj:123)

これらの違いは、このガイドの範囲外ですが、基本的にgoブロックは独自の資源を管理し、スレッドをブロックせずにコードの実行を一時停止します。これにより、非同期で実行されるコードが同期的に見えるようになり、コードベースから非同期コードを管理する負担を取り除きます。

<symbol>? - 述語サフィックス

シンボルの末尾に?を付けることは、シンボル名に特殊文字をサポートする多くの言語で一般的な命名規則です。これは、それが述語であること、つまり質問を提起することを示すために使用されます。たとえば、バッファ操作を扱うAPIを使用しているとします。

(def my-buffer (buffers/create-buffer [1 2 3]))
(buffers/empty my-buffer)

一見して、この場合の関数emptyが、

  • 渡されたバッファが空の場合はtrueを返し、

  • バッファをクリアします。

作成者はemptyis-emptyに名前変更できたかもしれませんが、Clojureのシンボル命名の豊富さにより、意図をより象徴的に表現できます。

(def my-buffer (buffers/create-buffer [1 2 3]))
(buffers/empty? my-buffer)
false

これは単なる推奨される規約であり、必須ではありません。

<symbol>! - 安全でない操作

STMトランザクションで安全ではない関数/マクロの名前は、感嘆符で終わる必要があります(例:reset!)。

これは、データストアへの接続、アトムの更新、ファイルストリームのクローズなど、状態を変化させる目的の関数名に付加されているのを最も頻繁に見かけます。

user=> (def my-stateful-thing (atom 0))
#'user/my-stateful-thing
user=> (swap! my-stateful-thing inc)
1
user=> @my-stateful-thing
1

これは単なる推奨される規約であり、必須ではありません。

感嘆符はしばしば「bang」と発音されることに注意してください。

_ - 使用されていない引数

下線文字が関数引数またはletバインディングで使用されている場合、_は、この引数を使用しないことを示す一般的な命名規則です。

これは、アトムの値が変化したときにコールバックスタイルの動作を追加するために使用できるadd-watch関数の例です。アトムが与えられた場合、それが変化するたびに新しい値を出力したいとします。

(def value (atom 0))

(add-watch value nil (fn [_ _ _ new-value]
                       (println new-value))

(reset! value 6)
; prints 6
(reset! value 9)
; prints 9

add-watchは4つの引数を取りますが、私たちの場合は最後の引数、つまりアトムの新しい値だけが重要なので、他の引数には_を使用します。

, - 空白文字

Clojureにおいて、,はスペース、タブ、改行と全く同じように空白文字として扱われます。そのため、リテラルコレクションではコンマは必須ではありませんが、可読性を高めるために頻繁に使用されます。

user=>(def m {:a 1, :b 2, :c 3}
{:a 1, :b 2, :c 3}

アイデアと[大量の]スペルチェックにご協力いただいた皆様に心から感謝いたします(スペルミスが多いので、Michael R. Mayneさん、lobsang_luddさん、本当にありがとうございます)。特定の機能をリクエストしてくださった方々には、名前を挙げさせていただきました。もし抜けている方がいたら申し訳ありません。

原著者: James Hughes