Upgrade to Pro — share decks privately, control downloads, hide ads and more …

From Java through Scala to Clojure

From Java through Scala to Clojure

JavaからScala、そしてClojureへ: 実務で活きる関数型プログラミング
JavaプログラマこそScalaやClojureを試してみよう!

6dd9394072d0d29aef3692f80298c36d?s=128

Kent OHASHI

June 24, 2022
Tweet

More Decks by Kent OHASHI

Other Decks in Programming

Transcript

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

  2. 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])
  3. 1. 私と仙台 2. 私と関数型言語 3. Opt Technologies と関数型言語 4. 関数型プログラミングの実践例

    5. 関数型プログラミング的な発想
  4. 私と仙台

  5. プライベート 岐阜出身 2012 年春〜: 東京 2022 年春〜: 千葉 仙台/ 宮城/

    東北に接点は( たぶん) なさそう が以前から気になっている🐬 仙台うみの杜水族館
  6. 仕事( オプト) オプトの は主に仙台拠点で 開発運用されていた 2017 年頃から東京の開発部門も関わり始める そのタイミングで東京所属の私もジョイン 2018 年には仙台拠点へ出張する機会も

    2021 年にフルリモートワーク前提で東京と仙台 の開発部門が統合された 現在も仙台在住のメンバーと日常的に一緒に仕事 している 広告運用支援ツール群
  7. 私と関数型言語

  8. year event 2011 大学( 法学部) 4 年で初めてプログラミングに 少し触れる: SQL, Java

    2012 前職の会社に新卒入社し、 Java での業務システム開発に携わり始める 2014 趣味で関数型言語に傾倒する: Haskell, Clojure, Erlang, Scala, OCaml, etc. 2015 Clojure, Haskell, Scala の勉強会に参加する ようになり、のちの同僚とも出会う
  9. year event 2016 オプトに中途入社し、 大規模なScala 開発を経験する 2017 開発中のプロダクトの小さなバッチに Clojure を社内初導入する

    2018 新規プロダクトのREST API 実装にClojure を 採用する 2019 新規プロダクトのGraphQL API 実装に Clojure を採用する 2021 開発チームを離れ、開発組織横断的な技術 マネジメント業務へ
  10. 発表 : at on 2017/01/28 Java プログラマこそClojure を始めようという悪魔 の誘い😈 Java

    から Clojure へ 第十八回 # 渋谷java
  11. 発表 : at on 2019/07/25 オプトでのClojure 採用から普及の歴史⚔️について ジョーク成分多めに紹介 Clojurian Conquest

    Shibuya.lisp lispmeetup #78
  12. 記事 : Clojure をプロダクトに導入した話

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

  14. Opt Technologies と関数型言語

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

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

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

    Haskell Elm Opt Technologies の主な利用技術
  18. 関数型プログラミングの実践例

  19. 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 コマンド
  20. Scala オブジェクト指向・関数型言語 静的型付き言語 JVM 言語 オブジェクト指向に関数型が溶け込んだ言語 cf. // メソッドの定義 scala>

    def hello(x: Any): Unit = | println(s"Hello, $x!") | def hello(x: Any): Unit // メソッドの呼び出し scala> hello("Scala") Hello, Scala! scala コマンド
  21. Clojure 非オブジェクト指向・関数型言語 動的型付き言語 JVM 言語 オブジェクト指向を嫌い関数型を志向したLisp cf. + ;; 関数の定義

    user=> (defn hello [x] #_=> (println (str "Hello, " x "!"))) #'user/hello ;; 関数の呼び出し ( 適用 ) user=> (hello "Clojure") Hello, Clojure! nil clojure コマンド rebel-readline
  22. とあるプロダクトの Java コード ( 抜粋 ) return mediaProcessLogDao.selectMediaProcessLogs(baseDate, modifiedEntities).stream() .map(this::normalizeTargetIndexIfAdvertise)

    .collect(Collectors.groupingBy(MediaProcessLogEntity::getKey)) .entrySet().stream().collect(toMap( Map.Entry::getKey, group -> { List<MediaProcessLogEntity> entities = group.getValue(); if (entities.stream() .allMatch(MediaProcessLogEntity::isEmpty)) { return true; } return entities.stream() .filter(e -> !e.isEmpty()) .allMatch(MediaProcessLogEntity::isImported); }));
  23. 問題を単純化すると エンティティのリストをその要素のキーごとにグルー ピングし、個々のグループの値が特定の条件を満たす かどうかを表す対応表( マップ) がほしい。 どのようなプログラムに落とし込む?

  24. 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]]
  25. Java: 命令型 (imperative) のアプローチ jshell> final var keyToEntities = ...>

    new HashMap<Integer, List<Entity>>(); ...> for (final var e : entities) { ...> final var es = keyToEntities.getOrDefault(e.key(), ...> new ArrayList<Entity>()); ...> 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]]}
  26. jshell> final var result = new HashMap<Integer, Boolean>(); ...> for

    (final var entry : keyToEntities.entrySet()) { ...> result.put(entry.getKey(), ...> entry.getValue().size() > 1); ...> } result ==> {} jshell> result result ==> {1=true, 2=false, 3=false}
  27. 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}
  28. 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))
  29. Scala: 関数型 (functional) のアプローチ scala> entities. | groupBy(_.key). | view.

    | mapValues(_.length > 1). | toMap val res0: Map[Int, Boolean] = Map(1 -> true, 2 -> false, 3 -> false)
  30. 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
  31. Clojure: 関数型 (functional) のアプローチ user=> (update-vals (group-by :entity/key entities) #_=>

    #(> (count %) 1)) {3 false, 1 true, 2 false}
  32. 考察 : 命令型 (imperative) のアプローチ 2 種類の変数とfor 文によるループ処理 変数やメソッドの命名、レイアウトなどの工夫を しないとコードの意図が埋もれがち

    文(statement) が登場し、命令( コマンド) の並びとし て表現されている マップやリストが破壊的に更新されている: 可変 (mutable) データ 変数への再代入を封じる(Java では final を付ける) だけでも安心感が高まる
  33. 考察 : 関数型 (functional) のアプローチ リストをグルーピングし、マップの値を変換すると いう意図が関数/ メソッドで表されている 引数で振る舞いを指定している: 高階関数

    (higher-order function) 与えているのは無名関数(anonymous function)/ ラムダ式(lambda expression) cf. オブジェクト指向のStrategy パターン 関数型言語では汎用的で高機能な関数/ メソッド が標準で充実している
  34. 全体が式(expression) で構成され、データの変換と して表現されている 今回の例では途中過程にローカル変数もない 関数型言語では簡潔に関数を組み合わせ加工する 手段が豊富に用意されている コード上で更新される変数やデータは見当たらない 調べてみると初期化以降にデータが更新されてい ないことが分かる: 不変(immutable)

    データ 関数型言語ではデフォルトで変数は再代入でき ず、不変データを利用しやすくなっていることが 多い
  35. 関数型プログラミング的な発想

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

  37. イミュータビリティ (immutability; 不変性 ) 形容詞形: イミュータブル(immutable; 不変) 対義語: ミュータビリティ(mutability; 可変性)

    、 ミュータブル(mutable; 可変) もとのまま変化しない( させられない) 性質 凍結するイメージ? 🧊 破壊的な更新操作( 再代入、更新、削除) を提供せ ず、作成( 初期化) し、取得する( 読み取る) ことに 徹する
  38. 主なメリット 可読性や変更容易性、コンポーザビリティが向上 しやすくなる デバッグやテストも容易になる 並行プログラミング、分散システムと相性が良い プログラミング言語に限らない例 イミュータブルインフラストラクチャ 台帳データベース、追記型のRDB テーブル設計 バージョン管理システム

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

    🧱
  40. 主なメリット 再利用性や拡張性が向上する 高凝集で疎結合な「モジュール」( ソフトウェア コンポーネント) に繋がる プログラミング言語に限らない例 Pipes & Filters

    Ports & Adapters ( ヘキサゴナルアーキテクチャ) Single Responsibility Principle (SRP) ( によるプレゼン) UNIX 哲学 Simple Made Easy Rich Hickey
  41. 関数型プログラミングは楽しい😆 関数型言語は怖くない( Java プログラマこそScala や Clojure を試してみよう! ) 。 思考のリソースを節約し、扱いやすいソフトウェアを

    設計するために、その発想を活かそう。
  42. Further Reading

  43. コミュニティイベント : Haskell : Lisp 系言語(Clojure, Common Lisp な ど)

    : Elixir : Scala Haskell-jp Shibuya.lisp fukuoka.ex/kokura.ex/ElixirImp rpscala
  44. 書籍 : Scala, Erlang, Clojure, Haskell cf. ( 原書続 編):

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

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

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