(defprofile lagénorhynque
:name "Kent OHASHI"
:account @lagenorhynque
:company 株式会社オプト
:languages [Clojure Haskell Python Scala
English français Deutsch русский]
:interests [プログラミング 語学 数学])
-
関数型プログラミング(FP)サポートの不足
- もっとFPしたい! (OOP要素も静的型付けも必須ではない)
-
冗長なシンタックス
- もっとシンプルに書きたい!
-
柔軟性/拡張性の不足
- もっと自由に書きたい!
factor | Java | Groovy | Scala | Kotlin | Clojure |
---|---|---|---|---|---|
FP support | × | △ | ◯ | △ | ◯ |
simplicity | × | ◯ | △ | ◯ | ◎ |
flexibility | × | ◯ | ◯ | ◯ | ◎ |
Clojure is pronounced exactly like closure, where the s/j has the zh sound as in azure, pleasure etc.
The name was chosen to be unique. I wanted to involve c (c#), l (lisp) and j (java).
Once I came up with Clojure, given the pun on closure, the available domains and vast emptiness of the googlespace, it was an easy decision.
― Rich Hickey, creator of Clojure
cf. meaning and pronunciation of Clojure
※ NOT /ˈkloʊd͡ʒɚ/
element | meaning |
---|---|
/ˈkloʊʒɚ/ |
クロージャ(closure), 関数型プログラミング |
C |
C#(.NET) as a platform, .NET言語 |
l |
Lisp方言 |
j |
Java as a platform, JVM言語 |
-
FP言語としてのClojure
-
Lisp方言としてのClojure
-
JVM言語としてのClojure
user=> '(1 2 3)
(1 2 3)
user=> [1 2 3]
[1 2 3]
user=> {:a 1 :b 2 :c 3}
{:a 1, :b 2, :c 3}
user=> #{1 2 3}
#{1 3 2}
user=> (def xs [1 2 3])
#'user/xs
user=> (filter odd? xs)
(1 3)
user=> (map #(* % %) xs)
(1 4 9)
user=> (reduce + 0 xs)
6
user=> (reduce + 0 (map #(* % %) (filter odd? xs)))
10
user=> (->> xs
#_=> (filter odd?)
#_=> (map #(* % %))
#_=> (reduce + 0))
10
#( )
は
#(* % %)
↓↓↓
(fn [x] (* x x))
に相当するリーダマクロ
->>
は
(->> a
(f x)
(g y)
(h z))
↓↓↓
(h z (g y (f x a)))
に展開されるマクロ(threading macroの一種)
user=> (def nats (iterate inc 0))
#'user/nats
user=> (take 10 nats)
(0 1 2 3 4 5 6 7 8 9)
user=> (take-while #(< % 10) nats)
(0 1 2 3 4 5 6 7 8 9)
S式(S-expressions)
(f a b c ...)
f
: 関数, マクロ, 特殊形式a, b, c, ...
: 引数
cf. Java
f(a, b, c, ...)
関数/メソッドの定義も
// Java
public void greet(String name) {
System.out.println("Bonjour, " + name + " !");
}
S式
;; Clojure
(defn greet [name]
(println (str "Bonjour, " name " !")))
名前空間/パッケージの宣言もインポートも
// Java
package demo_app;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
S式
;; Clojure
(ns demo-app.core
(:import (java.io IOException)
(java.util ArrayList List)))
user=> (def xs [1 2 3])
#'user/xs
user=> (first xs)
1
user=> (rest xs)
(2 3)
user=> (cons 0 xs)
(0 1 2 3)
(code as data; homoiconicity)
user=> (first '(def xs [1 2 3]))
def
user=> (rest '(def xs [1 2 3]))
(xs [1 2 3])
user=> (cons 'def '(xs [1 2 3]))
(def xs [1 2 3])
user=> (eval (cons 'def '(xs [1 2 3])))
#'user/xs
強力なコンパイル時メタプログラミング機構
user=> (defmacro unless ; clojure.core/if-not マクロを再実装しただけ
#_=> ([test then]
#_=> `(unless ~test ~then nil))
#_=> ([test then else]
#_=> `(if (not ~test)
#_=> ~then
#_=> ~else)))
#'user/unless
user=> (unless (= 1 2) :ok)
:ok
user=> (macroexpand '(unless (= 1 2) :ok))
(if (clojure.core/not (= 1 2)) :ok nil)
関数定義の defn
は
user=> (macroexpand
#_=> '(defn greet [name]
#_=> (println (str "Bonjour, " name " !"))))
(def greet (clojure.core/fn ([name] (println (str "Bonjour, " name " !")))))
特殊形式 def
, fn
を利用したマクロ
⇒ Lisp編集用プラグインがあれば非常に快適
jarとして実行可能
$ lein new app demo-app
Generating a project called demo-app based on the 'app' template.
$ cd demo-app/
$ lein uberjar
Compiling demo-app.core
Created /Users/lagenorhynchus/code/demo-app/target/uberjar/demo-app-0.1.0-SNAPSHOT.jar
Created /Users/lagenorhynchus/code/demo-app/target/uberjar/demo-app-0.1.0-SNAPSHOT-standalone.jar
$ java -jar target/uberjar/demo-app-0.1.0-SNAPSHOT-standalone.jar
Hello, World!
staticメソッド
// Java
Integer.parseInt("123")
;; Clojure
(Integer/parseInt "123")
インスタンスメソッド
// Java
"a b c".split("\\s")
;; Clojure
(.split "a b c" "\\s")
破壊的な初期化/設定
// Java
java.util.Map<String, Integer> m = new java.util.HashMap<>();
m.put("a", 1);
m.put("b", 2);
m.put("c", 3);
return m;
;; Clojure
(doto (java.util.HashMap.)
(.put "a" 1)
(.put "b" 2)
(.put "c" 3))
メソッドチェーン
// Java
new StringBuilder()
.append("a")
.append("b")
.append("c")
.toString()
;; Clojure
(.. (StringBuilder.)
(append "a")
(append "b")
(append "c")
toString)
;; または
(-> (StringBuilder.)
(.append "a")
(.append "b")
(.append "c")
.toString)
Javaのコレクション → Clojureの関数
user=> (def xs (doto (java.util.ArrayList.)
#_=> (.add 1)
#_=> (.add 2)
#_=> (.add 3)))
#'user/xs
user=> (class xs)
java.util.ArrayList
user=> xs
[1 2 3]
user=> (map inc xs)
(2 3 4)
Clojureのコレクション → Javaのメソッド
user=> (def xs [1 2 3 4 5])
#'user/xs
user=> (class xs)
clojure.lang.PersistentVector
user=> (instance? java.util.List xs)
true
user=> (.subList xs 1 4)
[2 3 4]
cf. Google Sheets API (Java版)の利用例
// Java
public Integer duplicateWorksheet(Sheets sheets, String spreadsheetId, Integer worksheetId, String worksheetName) {
List<Request> reqs = Arrays.asList(
new Request().setDuplicateSheet(
new DuplicateSheetRequest().setSourceSheetId(worksheetId)
.setNewSheetName(worksheetName)
.setInsertSheetIndex(1)
)
);
return executeUpdate(sheets, spreadsheetId, worksheetId, reqs)
.getReplies()
.get(0)
.getDuplicateSheet()
.getProperties()
.getSheetId();
}
;; Clojure
(defn duplicate-worksheet [sheets spreadsheet-id worksheet-id worksheet-name]
(let [reqs [(-> (Request.)
(.setDuplicateSheet
(-> (DuplicateSheetRequest.)
(.setSourceSheetId worksheet-id)
(.setNewSheetName worksheet-name)
(.setInsertSheetIndex (int 1)))))]]
(-> (execute-update sheets spreadsheet-id worksheet-id reqs)
.getReplies
first
.getDuplicateSheet
.getProperties
.getSheetId)))
-
core.async
-
transducers
-
clojure.spec
-
ClojureScript
etc.
-
Clojure: Clojure公式
-
Leiningen: Clojureのビルドツール
-
Boot: Clojureのビルドツール
-
Try Clojure: ClojureのオンラインREPLのひとつ
-
Replumb REPL: ClojureScriptのオンラインREPLのひとつ
-
- 第7章 Clojure