(defprofile lagénorhynque
:id @lagenorhynque
:readings ["/laʒenɔʁɛ̃k/" "ラジェノランク"]
:aliases ["カマイルカ" "🐬"]
:languages [Java Japanese ; native languages
Clojure Haskell ; functional languages
English français] ; European languages
:interests [programming
language-learning
law politics
mathematics])
-
改めて関数型プログラミング(言語)とは?
-
JavaとClojureのコレクションを調べてみよう
-
Clojureコレクションの全体像
4. 局所的にミュータビリティがほしいとき
5. 独自コレクションを実装しよう
-
関数型プログラミング(functional programming; FP)
- 副作用を可能な限り避ける(局所化する)プログラミングスタイル
-
関数型プログラミング言語(functional programming language; FPL)
- 関数型プログラミングが実践しやすいように設計されている言語
-
再代入不可な変数が定義可能であればFPL?
const
,final
,val
, etc.- それを活用していればFPしていることになる?
-
ラムダ式があり高階関数が豊富に提供されていればFPL?
filter
,map
,reduce
, etc.- それを活用していればFPしていることになる?
確かにFP/FPLに近づいている感じがする。
でも、それで満足?
- 私🐬が期待する実用的なFPLのイメージ:
-
不変で永続的なデータ構造(immutable persistent data structures) が豊富に提供され、容易に定義可能
-
そうでないデータ構造は非中心的な位置付け
- i.e. immutableでpersistentがデフォルト
-
FP も できる言語をFPLと呼びたくはない
-
- 関数型データ構造を更新する際には、更新前と更新後のバージョンが両方ともその後の処理で利用できることを期待する
- 複数のバージョンをサポートするデータ構造は永続的(persistent)
- ←→ 同時に1つのバージョンしか許さないデータ構造は刹那的(ephemeral)
- FPLはすべてのデータ構造が自然と永続的になるという興味深い性質を持つ
- 命令型データ構造は概して刹那的
『純粋関数型データ構造』(p. 13)
- オブジェクト指向・非関数型言語
- 静的型付き言語
- JVM言語
- 関数型プログラミング関連機能が充実してきた
// メソッドの定義
jshell> void hello(Object x) {
...> System.out.println("Hello, %s!".formatted(x));
...> }
| created method hello(Object)
// メソッドの呼び出し
jshell> hello("Java")
Hello, Java!
- 非オブジェクト指向・関数型言語
- 動的型付き言語
- JVM言語
- オブジェクト指向を嫌い関数型を志向したLisp
;; 関数の定義
user=> (defn hello [x]
(println (str "Hello, " x "!")))
#'user/hello
;; 関数の呼び出し(適用)
user=> (hello "Clojure")
Hello, Clojure!
nil
- オブジェクト指向・関数型言語
- 静的型付き言語
- JVM言語
- オブジェクト指向に関数型が溶け込んだ言語
// メソッドの定義
scala> def hello(x: Any): Unit =
| println(s"Hello, $x!")
def hello(x: Any): Unit
// メソッドの呼び出し
scala> hello("Scala")
Hello, Scala!
[Java] java.util.ArrayList
user=> (def xs (java.util.ArrayList.))
#'user/xs
user=> xs
[]
user=> (def ys ; ysに束縛
#_=> (doto xs
#_=> (.add 1) ; 破壊的に要素追加
#_=> (.add 2)
#_=> (.add 3)))
#'user/ys
user=> ys
[1 2 3]
user=> (identical? xs ys) ; オブジェクトの同一性を確認
true
java.util.List
の実装のひとつ- mutableでephemeralな可変長配列
user=> (def xs
#_=> (doto (java.util.ArrayList.)
#_=> (.add 1)
#_=> (.add 2)
#_=> (.add 3)))
#'user/xs
user=> xs
[1 2 3]
user=> (def ys (java.util.Collections/unmodifiableList xs)) ; 変更不可能リスト
#'user/ys
user=> (.add ys 4) ; 破壊的更新操作はできない
Execution error (UnsupportedOperationException) at java.util.Collections$Un
modifiableCollection/add (Collections.java:1067).
null
user=> (.add xs 4) ; もとのリストは破壊的更新操作ができる
true
user=> xs
[1 2 3 4]
user=> ys ; 変更不可能なビューにも波及する
[1 2 3 4]
- コレクション(ここではList)に対する変更不可能なビュー
- もとのコレクションやその要素がmutableならimmutableではない
- もとのコレクションが(実質的に) immutableである、もしくはこのビューを介してのみ操作できる状態であれば実質的にimmutable
- cf. java.util.Collection
[Clojure] ベクター(vector)
user=> (def xs []) ; 初期化
#'user/xs
user=> xs
[]
user=> (def ys (conj xs 1 2 3)) ; 要素追加してysに束縛(破壊的更新操作はない)
#'user/ys
user=> ys
[1 2 3]
user=> (identical? xs ys) ; オブジェクトの同一性を確認
false
clojure.lang.IPersistentVector
の実装- immutableでpersistentな可変長配列
- HAMT (hash array mapped trie)の一種
ref. Clojure Applied (p. 38)
ref. 可変コレクションおよび不変コレクション | Collections | Scala Documentation
ref. Clojure Performance Guarantees
ref. 性能特性 | Collections | Scala Documentation
書籍 Clojure Applied を読もう
- Clojure
- Clojure Applied: From Practice to Practitioner
- Clojure Performance Guarantees [Stefan Tilkov’s Blog]
- clojure/core.rrb-vector: RRB-Trees in Clojure