Slide 1

Slide 1 text

Java から Scala 、そして Clojure へ 実務で活きる関数型プログラミング

Slide 2

Slide 2 text

lagénorhynque 🐬 カマイルカ (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])

Slide 3

Slide 3 text

1. 私と仙台 2. 私と関数型言語 3. Opt Technologies と関数型言語 4. 関数型プログラミングの実践例 5. 関数型プログラミング的な発想

Slide 4

Slide 4 text

私と仙台

Slide 5

Slide 5 text

プライベート 岐阜出身 2012 年春〜: 東京 2022 年春〜: 千葉 仙台/ 宮城/ 東北に接点は( たぶん) なさそう が以前から気になっている🐬 仙台うみの杜水族館

Slide 6

Slide 6 text

仕事( オプト) オプトの は主に仙台拠点で 開発運用されていた 2017 年頃から東京の開発部門も関わり始める そのタイミングで東京所属の私もジョイン 2018 年には仙台拠点へ出張する機会も 2021 年にフルリモートワーク前提で東京と仙台 の開発部門が統合された 現在も仙台在住のメンバーと日常的に一緒に仕事 している 広告運用支援ツール群

Slide 7

Slide 7 text

私と関数型言語

Slide 8

Slide 8 text

year event 2011 大学( 法学部) 4 年で初めてプログラミングに 少し触れる: SQL, Java 2012 前職の会社に新卒入社し、 Java での業務システム開発に携わり始める 2014 趣味で関数型言語に傾倒する: Haskell, Clojure, Erlang, Scala, OCaml, etc. 2015 Clojure, Haskell, Scala の勉強会に参加する ようになり、のちの同僚とも出会う

Slide 9

Slide 9 text

year event 2016 オプトに中途入社し、 大規模なScala 開発を経験する 2017 開発中のプロダクトの小さなバッチに Clojure を社内初導入する 2018 新規プロダクトのREST API 実装にClojure を 採用する 2019 新規プロダクトのGraphQL API 実装に Clojure を採用する 2021 開発チームを離れ、開発組織横断的な技術 マネジメント業務へ

Slide 10

Slide 10 text

発表 : at on 2017/01/28 Java プログラマこそClojure を始めようという悪魔 の誘い😈 Java から Clojure へ 第十八回 # 渋谷java

Slide 11

Slide 11 text

発表 : at on 2019/07/25 オプトでのClojure 採用から普及の歴史⚔️について ジョーク成分多めに紹介 Clojurian Conquest Shibuya.lisp lispmeetup #78

Slide 12

Slide 12 text

記事 : Clojure をプロダクトに導入した話

Slide 13

Slide 13 text

記事 : サービス間連携のための GraphQL API を Clojure で開発している話

Slide 14

Slide 14 text

Opt Technologies と関数型言語

Slide 15

Slide 15 text

社内での関数型言語利用の歴史

Slide 16

Slide 16 text

発足(2016 年) 以前から前身とな った開発会社でメイン開発言語だった 近年はバックエンド開発の利用言語が多様化して いるが、引き続き主要言語のひとつ 2017 年の導入からシェアが拡大し、重要なプロ ダクトを支える言語のひとつになった 当初は一人しか経験者がいなかったが、継続的に 開発可能な体制に成熟してきた Scala Opt Technologies Clojure

Slide 17

Slide 17 text

2018 年頃に導入を試みたが、プロダクト開発が 諸事情により中止になり現存しない😇 開発者向け管理画面のために小さく使われている 例がある 上記Haskell プロダクトのWeb フロントエンドに も採用されていた cf. Haskell Elm Opt Technologies の主な利用技術

Slide 18

Slide 18 text

関数型プログラミングの実践例

Slide 19

Slide 19 text

Java オブジェクト指向・非関数型言語 静的型付き言語 JVM 言語 関数型プログラミング関連機能が充実してきた cf. // メソッドの定義 jshell> void hello(Object x) { ...> System.out.println("Hello, %s!".formatted(x)); ...> } | created method hello(Object) // メソッドの呼び出し jshell> hello("Java") Hello, Java! jshell コマンド

Slide 20

Slide 20 text

Scala オブジェクト指向・関数型言語 静的型付き言語 JVM 言語 オブジェクト指向に関数型が溶け込んだ言語 cf. // メソッドの定義 scala> def hello(x: Any): Unit = | println(s"Hello, $x!") | def hello(x: Any): Unit // メソッドの呼び出し scala> hello("Scala") Hello, Scala! scala コマンド

Slide 21

Slide 21 text

Clojure 非オブジェクト指向・関数型言語 動的型付き言語 JVM 言語 オブジェクト指向を嫌い関数型を志向したLisp cf. + ;; 関数の定義 user=> (defn hello [x] #_=> (println (str "Hello, " x "!"))) #'user/hello ;; 関数の呼び出し ( 適用 ) user=> (hello "Clojure") Hello, Clojure! nil clojure コマンド rebel-readline

Slide 22

Slide 22 text

とあるプロダクトの Java コード ( 抜粋 ) return mediaProcessLogDao.selectMediaProcessLogs(baseDate, modifiedEntities).stream() .map(this::normalizeTargetIndexIfAdvertise) .collect(Collectors.groupingBy(MediaProcessLogEntity::getKey)) .entrySet().stream().collect(toMap( Map.Entry::getKey, group -> { List entities = group.getValue(); if (entities.stream() .allMatch(MediaProcessLogEntity::isEmpty)) { return true; } return entities.stream() .filter(e -> !e.isEmpty()) .allMatch(MediaProcessLogEntity::isImported); }));

Slide 23

Slide 23 text

問題を単純化すると エンティティのリストをその要素のキーごとにグルー ピングし、個々のグループの値が特定の条件を満たす かどうかを表す対応表( マップ) がほしい。 どのようなプログラムに落とし込む?

Slide 24

Slide 24 text

Java: サンプルデータ jshell> record Entity(int key, String x) {} | created record Entity jshell> final var entities = List.of( ...> new Entity(3, "a"), ...> new Entity(1, "b"), ...> new Entity(2, "c"), ...> new Entity(1, "d"), ...> new Entity(1, "e") ...> ) entities ==> [Entity[key=3, x=a], Entity[key=1, x=b], Entity[k ... x=d], Entity[key=1, x=e]]

Slide 25

Slide 25 text

Java: 命令型 (imperative) のアプローチ jshell> final var keyToEntities = ...> new HashMap>(); ...> for (final var e : entities) { ...> final var es = keyToEntities.getOrDefault(e.key(), ...> new ArrayList()); ...> es.add(e); ...> keyToEntities.put(e.key(), es); ...> } keyToEntities ==> {} jshell> keyToEntities keyToEntities ==> {1=[Entity[key=1, x=b], Entity[key=1, x=d], Entity[key=1, x=e]], 2=[Entity[key=2, x=c]], 3=[Entity[key=3, x=a]]}

Slide 26

Slide 26 text

jshell> final var result = new HashMap(); ...> for (final var entry : keyToEntities.entrySet()) { ...> result.put(entry.getKey(), ...> entry.getValue().size() > 1); ...> } result ==> {} jshell> result result ==> {1=true, 2=false, 3=false}

Slide 27

Slide 27 text

Java: 関数型 (functional) のアプローチ ※ REPL での行継続のため行末に . を置いている jshell> entities.stream(). ...> collect(Collectors.groupingBy(Entity::key)). ...> entrySet().stream(). ...> collect(Collectors.toMap( ...> Map.Entry::getKey, ...> group -> group.getValue().size() > 1 ...> )) $3 ==> {1=true, 2=false, 3=false}

Slide 28

Slide 28 text

Scala: サンプルデータ scala> case class Entity(key: Int, x: String) // defined case class Entity scala> val entities = Seq( | Entity(3, "a"), | Entity(1, "b"), | Entity(2, "c"), | Entity(1, "d"), | Entity(1, "e"), | ) val entities: Seq[Entity] = List(Entity(3,a), Entity(1,b), Entity(2,c), Entity(1,d), Entity(1,e))

Slide 29

Slide 29 text

Scala: 関数型 (functional) のアプローチ scala> entities. | groupBy(_.key). | view. | mapValues(_.length > 1). | toMap val res0: Map[Int, Boolean] = Map(1 -> true, 2 -> false, 3 -> false)

Slide 30

Slide 30 text

Clojure: サンプルデータ user=> (def entities [#:entity{:key 3 #_=> :x "a"} #_=> #:entity{:key 1 #_=> :x "b"} #_=> #:entity{:key 2 #_=> :x "c"} #_=> #:entity{:key 1 #_=> :x "d"} #_=> #:entity{:key 1 #_=> :x "e"}]) #'user/entities

Slide 31

Slide 31 text

Clojure: 関数型 (functional) のアプローチ user=> (update-vals (group-by :entity/key entities) #_=> #(> (count %) 1)) {3 false, 1 true, 2 false}

Slide 32

Slide 32 text

考察 : 命令型 (imperative) のアプローチ 2 種類の変数とfor 文によるループ処理 変数やメソッドの命名、レイアウトなどの工夫を しないとコードの意図が埋もれがち 文(statement) が登場し、命令( コマンド) の並びとし て表現されている マップやリストが破壊的に更新されている: 可変 (mutable) データ 変数への再代入を封じる(Java では final を付ける) だけでも安心感が高まる

Slide 33

Slide 33 text

考察 : 関数型 (functional) のアプローチ リストをグルーピングし、マップの値を変換すると いう意図が関数/ メソッドで表されている 引数で振る舞いを指定している: 高階関数 (higher-order function) 与えているのは無名関数(anonymous function)/ ラムダ式(lambda expression) cf. オブジェクト指向のStrategy パターン 関数型言語では汎用的で高機能な関数/ メソッド が標準で充実している

Slide 34

Slide 34 text

全体が式(expression) で構成され、データの変換と して表現されている 今回の例では途中過程にローカル変数もない 関数型言語では簡潔に関数を組み合わせ加工する 手段が豊富に用意されている コード上で更新される変数やデータは見当たらない 調べてみると初期化以降にデータが更新されてい ないことが分かる: 不変(immutable) データ 関数型言語ではデフォルトで変数は再代入でき ず、不変データを利用しやすくなっていることが 多い

Slide 35

Slide 35 text

関数型プログラミング的な発想

Slide 36

Slide 36 text

「イミュータビリティ」と「コンポーザビリティ」を 重視する

Slide 37

Slide 37 text

イミュータビリティ (immutability; 不変性 ) 形容詞形: イミュータブル(immutable; 不変) 対義語: ミュータビリティ(mutability; 可変性) 、 ミュータブル(mutable; 可変) もとのまま変化しない( させられない) 性質 凍結するイメージ? 🧊 破壊的な更新操作( 再代入、更新、削除) を提供せ ず、作成( 初期化) し、取得する( 読み取る) ことに 徹する

Slide 38

Slide 38 text

主なメリット 可読性や変更容易性、コンポーザビリティが向上 しやすくなる デバッグやテストも容易になる 並行プログラミング、分散システムと相性が良い プログラミング言語に限らない例 イミュータブルインフラストラクチャ 台帳データベース、追記型のRDB テーブル設計 バージョン管理システム

Slide 39

Slide 39 text

コンポーザビリティ (composability; 合成可能性 ) 形容詞: コンポーザブル(composable; 合成可能) 要素同士が組み合わせられる性質 LEGO ブロックのイメージ? 🧱

Slide 40

Slide 40 text

主なメリット 再利用性や拡張性が向上する 高凝集で疎結合な「モジュール」( ソフトウェア コンポーネント) に繋がる プログラミング言語に限らない例 Pipes & Filters Ports & Adapters ( ヘキサゴナルアーキテクチャ) Single Responsibility Principle (SRP) ( によるプレゼン) UNIX 哲学 Simple Made Easy Rich Hickey

Slide 41

Slide 41 text

関数型プログラミングは楽しい😆 関数型言語は怖くない( Java プログラマこそScala や Clojure を試してみよう! ) 。 思考のリソースを節約し、扱いやすいソフトウェアを 設計するために、その発想を活かそう。

Slide 42

Slide 42 text

Further Reading

Slide 43

Slide 43 text

コミュニティイベント : Haskell : Lisp 系言語(Clojure, Common Lisp な ど) : Elixir : Scala Haskell-jp Shibuya.lisp fukuoka.ex/kokura.ex/ElixirImp rpscala

Slide 44

Slide 44 text

書籍 : Scala, Erlang, Clojure, Haskell cf. ( 原書続 編): Elixir, Elm, Idris Scala 『7 つの言語 7 つの世界』 Seven More Languages in Seven Weeks 『実践Scala 入門』 『Scala スケーラブルプログラミング 第4 版』 『Scala 関数型デザイン&プログラミング』

Slide 45

Slide 45 text

Clojure cf. ( 原書第3 版) Haskell 『プログラミングClojure 第2 版』 Programming Clojure, Third Edition Getting Clojure Clojure Applied 『[増補改訂]関数プログラミング実践入門』 『プログラミングHaskell 第2 版』 『すごいHaskell たのしく学ぼう!』 『Haskell 入門 関数型プログラミング言語の基礎と 実践』

Slide 46

Slide 46 text

OCaml Erlang Elixir 『プログラミングの基礎』 『プログラミング in OCaml 』 『プログラミングErlang 』 『すごいErlang ゆかいに学ぼう!』 『プログラミングElixir (第2 版)』