Slide 1

Slide 1 text

関数型言語テイスティング Haskell, Scala, Clojure, Elixir を 比べて味わう関数型プログラミングの旨さ #fp_matsuri #fp_matsuri_c 1

Slide 2

Slide 2 text

のシニアエンジニア スタートアップの起業家と投資家のための業務効 率化/ 連携プラットフォームを開発している 主要技術スタック: Kotlin & TypeScript の運営にも協力 関数型プログラミング( 言語) とLisp の熱烈な愛好者 ( 特に) と がエレガントで好き の運営スタッフ( 座長のひとり) lagénorhynque 🐬カマイルカ 株式会社スマートラウンド Server-Side Kotlin Meetup Clojure Haskell 関数型まつり2025 2

Slide 3

Slide 3 text

[ 参考] 🐬の関数型言語学習/ 利用経験 Most Used Languages Clojure 42.14% Haskell 19.94% Scala 10.82% Python 5.10% TypeScript 5.03% Java 4.95% Elixir 4.10% Ruby 2.98% Common Lisp 2.56% Kotlin 2.38% GitHub でのコード量 × リポジトリ数 3

Slide 4

Slide 4 text

関数型言語経験: 約12 年 cf. プログラミング経験: 約13 年 プログラミング学習初期から関数型言語と戯れて きた( そして非関数型言語に未だに馴染めない😇) 仕事で: Clojure, Scala 趣味で: Clojure, Haskell, Erlang/Elixir, etc. 4

Slide 5

Slide 5 text

初期(2013 年〜) の学びの主な機会 関数型プログラミング( 言語) に関する技術書/ 記事 コミュニティイベント 勉強会: , , , , , etc. もくもく会: , 読書会: , カンファレンス: Haskell Day, ScalaMatsuri, Erlang & Elixir Fest Tamachi.clj Shibuya.lisp 渋谷java tokyo.ex rpscala Haskell もくもく会 Laketown.clj 関数型プログラミング勉強会 (Scala ) SICP 読書会 5

Slide 6

Slide 6 text

オーム社の より 『7 つの言語 7 つの世界』 書籍詳細ページ 6

Slide 7

Slide 7 text

( すごいH 本) オーム社の より 『すごいHaskell たのしく学ぼう!』 書籍詳細ページ 7

Slide 8

Slide 8 text

オーム社の より 『プログラミングHaskell 』 書籍詳細ページ 8

Slide 9

Slide 9 text

オーム社の より 『プログラミングClojure 第2 版』 書籍詳細ページ 9

Slide 10

Slide 10 text

( すごいE 本) 達人出版会の より 『すごいErlang ゆかいに学ぼう!』 書籍詳細ページ 10

Slide 11

Slide 11 text

(FP in Scala) インプレスブックスの より 『Scala 関数型デザイン&プログラミング』 書籍詳細ページ 11

Slide 12

Slide 12 text

(SICP, 魔術師本) Wikipedia 英語版の より Structure and Interpretation of Computer Programs Structure and Interpretation of Computer Programs 12

Slide 13

Slide 13 text

1. きっかけ 2. 「関数型プログラミング」 3. 🍷 テイスティング 1. 4 つの関数型言語の基本 2. 関数 3. データ 4. 評価 5. ポリモーフィズム 4. 関数型プログラマのメンタルモデル 13

Slide 14

Slide 14 text

1. きっかけ 14

Slide 15

Slide 15 text

関数型プログラミング( 言語) に関する解説で 🐬は以下のような表現をよく目にする: 近年、関数型プログラミング( 言語) の エッセンスが多くの主流言語に取り 入れられてきました。 15

Slide 16

Slide 16 text

関数型プログラミング( 言語) の 「エッセンス」??? 🤔 16

Slide 17

Slide 17 text

エッセンス(essence) 語源: 🇬🇧 essence < essentia < esse + -ia (≒ 🇬🇧 beingness, あるということ) 語義: 「本質( 的に重要なもの) 」 抽出物( エキス) という派生的/ 比喩的な意味も 本来、単なる成分/ 要素のような意味ではない cf. 🇫🇷 哲学の文脈で関連する表現 l'existence précède l'essence ( 実存は本質に先立 つ) L'essentiel est invisible pour les yeux. ( 🦊「大切 なものは目に見えないんだよ。 」 ) 17

Slide 18

Slide 18 text

ワイン🍷テイスティングでは色( 視覚) ・香り( 嗅覚) ・ 味( 味覚) から吟味するように、 4 つの関数型言語を例にその味わいを多角的に探り、 「エッセンス」(= 本質) を見出したい → 関数型言語テイスティング 18

Slide 19

Slide 19 text

2. 「関数型プログラミング」 19

Slide 20

Slide 20 text

( 現在の) 🐬によるFP とFPL の定義 関数型プログラミング := 純粋関数を基本要素とし てその組み合わせによってプログラムを構成してい くプログラミングスタイル → 言語を問わず実践可能( 実践しやすさは異なる) 関数型言語 := 関数型プログラミングが言語/ 標準ラ イブラリレベルで十分に支援される( そして関数型 プログラミングスタイルがユビキタスな) 言語 → 例えばJavaScript/TypeScript やJava 、Kotlin 、 古典的なLisp 方言は含めない 20

Slide 21

Slide 21 text

重視する もの (values) ⾔語 (languages) 処理系/ 実装 (implementations) パターン (patterns) Lisp 式指向 (expression-oriented) 不変性 (immutability) 宣⾔型プログラミング (declarative programming) ( 型) 安全性 ((type) safety) 合成可能性 (composability) 永続性 (persistence) 純粋性 (purity) Clojure Erlang Elixir Haskell OCaml Standard ML ML 適⽤ (apply) 評価 (eval) 抽象構⽂⽊ (abstract syntax tree; AST) マクロ (macro) 参照透過性 (referential transparency) 再帰 (recursion) 遅延評価 (lazy evaluation) メモ化 (memoization) 並⾏プログラミング (concurrent programming) アクターモデル (actor model) STM (software transactional memory) CSP (communicating sequential processes) 継続 (continuation) ⾼階関数 (higher-order function) 関数合成 (function composition) カリー化 (currying) 部分適⽤ (partial application) 関数型 プログラミング (functional programming) 理論 (theories) 数学 (mathematics) 圏論 (category theory) 型システム (type system) ラムダ計算 (lambda calculus) メタプログラミング (metaprogramming) 形式⼿法 (formal methods) 定理証明⽀援系 (theorem prover) 評価 (evaluation) 制御 (control) 関数 (function) パイプ演算⼦ (pipe operator) メソッドチェーン (method chaining) 代数的データ型 (algebraic data type; ADT) パターンマッチ (pattern matching) クロージャー/ 関数閉包 (closure) オブジェクト (object) Idris Elm パーサーコンビネーター (parser combinator) 離散数学 (discrete mathematics) 契約プログラミング (contract programming) 抽象データ型 (abstract data type) データ (data) 直和型 (sum type) 直積型 (product type) 篩型 (refinement type) 依存型 (dependent type) カプセル化 (encapsulation) ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相 (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) カリー= ハワード同型対応 (Curry–Howard correspondence) 分配束縛 (destructuring) 純粋関数型データ構造 (purely functional data structure) 永続データ構造 (persistent data structure) Coq (Rocq) Scala 構⽂解析 (parse) オーバーロード/ 多重定義 (overloading) 命令型プログラミング (imperative programming) ⽂指向 (statement-oriented) 副作⽤ (side effect) 破壊的更新 (mutation) 可変性 (mutability) pipes & filters goroutines & channels プロパティベーステスト (property-based testing) ⼊出⼒ (I/O) データ指向プログラミング (data-oriented programming) ファンクター (functor) モナド (monad) リスト (list) 遅延リスト/ ストリーム (lazy list/stream) 同図像性 (homoiconicity) 超循環評価器 (meta-circular evaluator) セルフホスティング (self-hosting) ベクター (vector) 全域関数 (total function) 部分関数 (partial function) オブジェクト指向プログラミング (object-oriented programming) アプリカティブ (applicative) トレイト (trait) Agda 必要呼び (call by need) F# ジェネリクス (generics) Lean Gleam 末尾再帰 (tail recursion) 意味論 (semantics) 型推論 (type inference) 再帰型 (recursive type) GoF デザインパターン (GoF Design Patterns) ※ 🐬が思い浮かぶ概念/ 用語を連想的に列挙したもの( 網羅的でも体系的でもない) 🐬の「関数型プログラミング」コンセプトマップ 21

Slide 22

Slide 22 text

重視する もの (values) 式指向 (expression-oriented) 不変性 (immutability) 宣⾔型プログラミング (declarative programming) ( 型) 安全性 ((type) safety) 合成可能性 (composability) 永続性 (persistence) 純粋性 (purity) 参照透過性 (referential transparency) 命令型プログラミング (imperative programming) ⽂指向 (statement-oriented) 副作⽤ (side effect) 破壊的更新 (mutation) 可変性 (mutability) ⼊出⼒ (I/O) 22

Slide 23

Slide 23 text

⾔語 (languages) Lisp Clojure Erlang Elixir Haskell OCaml Standard ML ML Idris Elm Coq (Rocq) Scala Agda F# Lean Gleam 23

Slide 24

Slide 24 text

理論 (theories) 数学 (mathematics) 圏論 (category theory) 型システム (type system) ラムダ計算 (lambda calculus) 形式⼿法 (formal methods) 定理証明⽀援系 (theorem prover) 離散数学 (discrete mathematics) 契約プログラミング (contract programming) 篩型 (refinement type) 依存型 (dependent type) カリー= ハワード同型対応 (Curry–Howard correspondence) プロパティベーステスト (property-based testing) 意味論 (semantics) 型推論 (type inference) 24

Slide 25

Slide 25 text

処理系/ 実装 (implementations) 適⽤ (apply) 評価 (eval) 抽象構⽂⽊ (abstract syntax tree; AST) マクロ (macro) メタプログラミング (metaprogramming) パーサーコンビネーター (parser combinator) 構⽂解析 (parse) 同図像性 (homoiconicity) 超循環評価器 (meta-circular evaluator) セルフホスティング (self-hosting) 25

Slide 26

Slide 26 text

パターン (patterns) 再帰 (recursion) 遅延評価 (lazy evaluation) メモ化 (memoization) 並⾏プログラミング (concurrent programming) アクターモデル (actor model) STM (software transactional memory) CSP (communicating sequential processes) 継続 (continuation) ⾼階関数 (higher-order function) 関数合成 (function composition) カリー化 (currying) 部分適⽤ (partial application) 評価 (evaluation) 制御 (control) 関数 (function) パイプ演算⼦ (pipe operator) メソッドチェーン (method chaining) 代数的データ型 (algebraic data type; ADT) パターンマッチ (pattern matching) クロージャー/ 関数閉包 (closure) オブジェクト (object) 抽象データ型 (abstract data type) データ (data) 直和型 (sum type) 直積型 (product type) カプセル化 (encapsulation) ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相 (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) 分配束縛 (destructuring) 純粋関数型データ構造 (purely functional data structure) 永続データ構造 (persistent data structure) オーバーロード/ 多重定義 (overloading) pipes & filters goroutines & channels データ指向プログラミング (data-oriented programming) ファンクター (functor) モナド (monad) リスト (list) 遅延リスト/ ストリーム (lazy list/stream) ベクター (vector) 全域関数 (total function) 部分関数 (partial function) オブジェクト指向プログラミング (object-oriented programming) アプリカティブ (applicative) トレイト (trait) 必要呼び (call by need) ジェネリクス (generics) 末尾再帰 (tail recursion) 再帰型 (recursive type) GoF デザインパターン (GoF Design Patterns) 26

Slide 27

Slide 27 text

3. 🍷 テイスティング 1. 4 つの関数型言語の基本 2. 関数 3. データ 4. 評価 5. ポリモーフィズム 27

Slide 28

Slide 28 text

3-1. 🍷 4 つの関数型言語の基本 Haskell, Scala, Clojure, Elixir 28

Slide 29

Slide 29 text

⾔語 (languages) Lisp Clojure Erlang Elixir Haskell OCaml Standard ML ML Idris Elm Coq (Rocq) Scala Agda F# Lean Gleam 29

Slide 30

Slide 30 text

paradigm FP OOP, FP FP FP typing static static dynamic dynamic first appeared 1990 2004 2007 2012 related ML ML Lisp Lisp 🐬's note lazy/pure FPL standard OOPL in FPL's skin modern functional Lisp Erlang + Ruby + Clojure Haskell Scala Clojure Elixir 30

Slide 31

Slide 31 text

モジュール定義とトップレベル変数 {- Haskell -} -- モジュールとそのエクスポートリスト module SomeModule(a) where -- パブリック変数 a :: Int a = 1 -- プライベート変数 b :: Int b = 2 /* Scala */ // ( シングルトン) オブジェクト object SomeModule: // パブリック変数 val a: Int = 1 // プライベート変数 private val b: Int = 2 31

Slide 32

Slide 32 text

;;; Clojure ;; 名前空間 (ns some-module) ;; パブリック変数 (def a 1) ;; プライベート変数 (def ^:private b 2) ## Elixir # モジュール defmodule SomeModule do # パブリックな0 引数関数 def a, do: 1 # プライベートな0 引数関数 defp b, do: 2 end 32

Slide 33

Slide 33 text

トップレベル関数 {- Haskell -} module SimpleMath(square) where -- パブリック関数 square :: Int -> Int square n = n * n -- プライベート関数 double :: Int -> Int double n = n * 2 /* Scala */ object SimpleMath: // パブリックメソッド def square(n: Int): Int = n * n // プライベートメソッド private def double(n: Int): Int = n * 2 33

Slide 34

Slide 34 text

;;; Clojure (ns simple-math) ;; パブリック関数 (defn square [n] (* n n)) ;; プライベート関数 (defn- double' [n] (* n 2)) ## Elixir defmodule SimpleMath do # パブリック関数 def square(n), do: n * n # プライベート関数 defp double(n), do: n * 2 end 34

Slide 35

Slide 35 text

モジュールの利用 {- Haskell: ghci コマンドによるREPL -} λ> :l SimpleMath ... λ> import qualified SimpleMath as M -- モジュールを別名で参照 λ> M.square 3 9 it :: Int λ> map M.square [0..10] [0,1,4,9,16,25,36,49,64,81,100] it :: [Int] /* Scala: scala コマンドによるREPL */ scala> :l SimpleMath.scala // defined object SimpleMath scala> import SimpleMath as M // オブジェクトを別名で参照 scala> M.square(3) val res0: Int = 9 scala> (0 to 10).map(M.square) val res1: IndexedSeq[Int] = Vector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100) 35

Slide 36

Slide 36 text

;;; Clojure: clj コマンドによるREPL user=> (clojure.main/load-script "simple_math.clj") #'simple-math/double' user=> (require '[simple-math :as m]) ; 名前空間を別名で参照 nil user=> (m/square 3) 9 user=> (map m/square (range 0 (inc 10))) (0 1 4 9 16 25 36 49 64 81 100) ## Elixir: iex コマンドによるREPL # `iex simple_math.ex` でファイルを読み込んで起動 iex(1)> alias SimpleMath, as: M # モジュールを別名で参照 SimpleMath iex(2)> M.square(3) 9 iex(3)> Enum.map(0..10, &M.square/1) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 36

Slide 37

Slide 37 text

ローカル束縛( 変数) {- Haskell -} λ> :{ -- REPL での複数行入力の開始 λ| -- ML 系言語でお馴染み(?) のlet 式 λ| let x = 5 λ| y = M.square(x) λ| in "square " ++ show x ++ " = " ++ show y λ| :} -- REPL での複数行入力の終了 "square 5 = 25" it :: [Char] /* Scala */ scala> val x = 5 val x: Int = 5 scala> val y = M.square(x) val y: Int = 25 scala> s"square($x) = $y" val res2: String = square(5) = 25 37

Slide 38

Slide 38 text

;;; Clojure ;; Lisp 系言語でお馴染み(?) のlet 式 user=> (let [x 5 y (m/square x)] (str "(square " x ") = " y)) "(square 5) = 25" ## Elixir iex(4)> x = 5 5 iex(5)> y = M.square(x) 25 iex(6)> "square(#{x}) = #{y}" "square(5) = 25" 38

Slide 39

Slide 39 text

無名関数( ラムダ式) {- Haskell -} λ> (\n -> n * n * n) 2 8 it :: Num a => a λ> map (\n -> n * n * n) [0..10] [0,1,8,27,64,125,216,343,512,729,1000] it :: (Num b, Enum b) => [b] /* Scala */ scala> ((n: Int) => n * n * n)(2) val res0: Int = 8 scala> (0 to 10).map(n => n * n * n) val res1: IndexedSeq[Int] = Vector(0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000) 39

Slide 40

Slide 40 text

;;; Clojure user=> ((fn [n] (* n n n )) 2) 8 user=> (#(* % % %) 2) ; 無名関数の略記法 8 user=> (map (fn [n] (* n n n )) (range 0 (inc 10))) (0 1 8 27 64 125 216 343 512 729 1000) user=> (map #(* % % %) (range 0 (inc 10))) (0 1 8 27 64 125 216 343 512 729 1000) ## Elixir iex(1)> (fn n -> n * n * n end).(2) 8 iex(2)> (&(&1 * &1 * &1)).(2) # 無名関数の略記法 8 iex(3)> Enum.map(0..10, fn n -> n * n * n end) [0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000] iex(4)> Enum.map(0..10, &(&1 * &1 * &1)) [0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000] 40

Slide 41

Slide 41 text

再帰と無限リスト(1): 階乗 {- Haskell -} module Factorial where factorial :: Integral a => a -> a factorial 0 = 1 factorial n = n * factorial (n - 1) factorialSeq :: Integral a => [a] factorialSeq = scanl (*) 1 [1..] -- import Factorial (factorialSeq) λ> take 10 factorialSeq [1,1,2,6,24,120,720,5040,40320,362880] it :: Integral a => [a] λ> factorialSeq !! 100 9332621544394415268169923885626670049071596826438162146859296 3895217599993229915608941463976156518286253697920827223758251 185210916864000000000000000000000000 it :: Integral a => a 41

Slide 42

Slide 42 text

/* Scala */ object Factorial: def factorial(n: Int): BigInt = n match case 0 => 1 case _ => n * factorial(n - 1) def factorialSeq: LazyList[BigInt] = LazyList.from(1).scanLeft(BigInt(1))(_ * _) // import Factorial.factorialSeq scala> factorialSeq.take(10).toList val res0: List[BigInt] = List(1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880) scala> factorialSeq(100) val res1: BigInt = 933262154439441526816992388562667004907159 6826438162146859296389521759999322991560894146397615651828625 3697920827223758251185210916864000000000000000000000000 42

Slide 43

Slide 43 text

;;; Clojure (ns factorial) (defn factorial [n] (if (zero? n) 1 (*' n (factorial (dec' n))))) (defn factorial-seq [] (reductions *' 1 (iterate inc' 1))) ;; (require '[factorial :refer [factorial-seq]]) user=> (take 10 (factorial-seq)) (1 1 2 6 24 120 720 5040 40320 362880) user=> (nth (factorial-seq) 100) 9332621544394415268169923885626670049071596826438162146859296 3895217599993229915608941463976156518286253697920827223758251 185210916864000000000000000000000000N 43

Slide 44

Slide 44 text

## Elixir defmodule Factorial do def factorial(0), do: 1 def factorial(n), do: n * factorial(n - 1) def factorial_seq do Stream.concat( [1], Stream.scan(Stream.from_index(1), &*/2) ) end end # import Factorial, only: [factorial_seq: 0] iex(2)> Enum.take(factorial_seq, 10) [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] iex(3)> Enum.at(factorial_seq, 100) 9332621544394415268169923885626670049071596826438162146859296 3895217599993229915608941463976156518286253697920827223758251 185210916864000000000000000000000000 44

Slide 45

Slide 45 text

再帰と無限リスト(2): フィボナッチ数 {- Haskell -} module Fibonacci where fib :: Integral a => a -> a fib 0 = 0 fib 1 = 1 fib n = fib (n - 1) + fib (n - 2) fibSeq :: Integral a => [a] fibSeq = map fst $ iterate (\(a, b) -> (b, a + b)) (0, 1) -- import Fibonacci (fibSeq) λ> take 10 fibSeq [0,1,1,2,3,5,8,13,21,34] it :: Integral a => [a] λ> take 3 $ drop 100 fibSeq [354224848179261915075,573147844013817084101, 927372692193078999176] it :: Integral a => [a] 45

Slide 46

Slide 46 text

/* Scala */ object Fibonacci: def fib(n: Int): BigInt = n match case 0 => 0 case 1 => 1 case _ => fib(n - 1) + fib(n - 2) def fibSeq: LazyList[BigInt] = LazyList.iterate((BigInt(0), BigInt(1))) { case (a, b) => (b, a + b) }.map(_(0)) // import Fibonacci.fibSeq scala> fibSeq.take(10).toList val res2: List[BigInt] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34) scala> fibSeq.drop(100).take(3).toList val res3: List[BigInt] = List(354224848179261915075, 573147844013817084101, 927372692193078999176) 46

Slide 47

Slide 47 text

;;; Clojure (ns fibonacci) (defn fib [n] (case n 0 0 1 1 (+' (fib (-' n 1)) (fib (-' n 2))))) (defn fib-seq [] (->> [0 1] (iterate (fn [[a b]] [b (+' a b)])) (map first))) ;; (require '[fibonacci :refer [fib-seq]]) user=> (take 10 (fib-seq)) (0 1 1 2 3 5 8 13 21 34) user=> (->> (fib-seq) (drop 100) (take 3)) (354224848179261915075N 573147844013817084101N 927372692193078999176N) 47

Slide 48

Slide 48 text

## Elixir defmodule Fibonacci do def fib(0), do: 0 def fib(1), do: 1 def fib(n), do: fib(n - 1) + fib(n - 2) def fib_seq do {0, 1} |> Stream.iterate(fn {a, b} -> {b, a + b} end) |> Stream.map(&elem(&1, 0)) end end # import Fibonacci, only: [fib_seq: 0] iex(2)> Enum.take(fib_seq, 10) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] iex(3)> fib_seq |> Stream.drop(100) |> Enum.take(3) [354224848179261915075, 573147844013817084101, 927372692193078999176] 48

Slide 49

Slide 49 text

[PR] のグッズ👕 関数型まつり 公式オンラインストア 49

Slide 50

Slide 50 text

3-2. 🍷 関数 50

Slide 51

Slide 51 text

パターン (patterns) 再帰 (recursion) 遅延評価 (lazy evaluation) メモ化 (memoization) 並⾏プログラミング (concurrent programming) アクターモデル (actor model) STM (software transactional memory) CSP (communicating sequential processes) 継続 (continuation) ⾼階関数 (higher-order function) 関数合成 (function composition) カリー化 (currying) 部分適⽤ (partial application) 評価 (evaluation) 制御 (control) 関数 (function) パイプ演算⼦ (pipe operator) メソッドチェーン (method chaining) 代数的データ型 (algebraic data type; ADT) パターンマッチ (pattern matching) クロージャー/ 関数閉包 (closure) オブジェクト (object) 抽象データ型 (abstract data type) データ (data) 直和型 (sum type) 直積型 (product type) カプセル化 (encapsulation) ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相 (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) 分配束縛 (destructuring) 純粋関数型データ構造 (purely functional data structure) 永続データ構造 (persistent data structure) オーバーロード/ 多重定義 (overloading) pipes & filters goroutines & channels データ指向プログラミング (data-oriented programming) ファンクター (functor) モナド (monad) リスト (list) 遅延リスト/ ストリーム (lazy list/stream) ベクター (vector) 全域関数 (total function) 部分関数 (partial function) オブジェクト指向プログラミング (object-oriented programming) アプリカティブ (applicative) トレイト (trait) 必要呼び (call by need) ジェネリクス (generics) 末尾再帰 (tail recursion) 再帰型 (recursive type) GoF デザインパターン (GoF Design Patterns) 51

Slide 52

Slide 52 text

⾼階関数 (higher-order function) 関数合成 (function composition) カリー化 (currying) 部分適⽤ (partial application) 関数 (function) パイプ演算⼦ (pipe operator) メソッドチェーン (method chaining) クロージャー/ 関数閉包 (closure) オブジェクト (object) pipes & filters 全域関数 (total function) 部分関数 (partial function) オブジェクト指向プログラミング (object-oriented programming) GoF デザインパターン (GoF Design Patterns) 52

Slide 53

Slide 53 text

式を評価すると値が得られる 部分式を関数として抽出( 分解) し関数を組み合わせ ( 合成) て式を構成する → 関数が簡潔に楽に組み合わせられると旨い😋 53

Slide 54

Slide 54 text

部分適用 {- Haskell -} λ> :t map -- 関数はカリー(1 引数関数の連鎖) 化されている map :: (a -> b) -> [a] -> [b] λ> :t (+) -- 演算子もカリー化されている (+) :: Num a => a -> a -> a λ> :t (+ 1) -- \x -> x + 1 と等価( セクション記法による部分適用) (+ 1) :: Num a => a -> a λ> :t map (+ 1) -- \xs -> map (+ 1) xs と等価( 部分適用) map (+ 1) :: Num b => [b] -> [b] λ> map (+ 1) [0..9] [1,2,3,4,5,6,7,8,9,10] it :: (Num b, Enum b) => [b] λ> f x y z = x * y * z f :: Num a => a -> a -> a -> a λ> :t f 2 3 -- 2 番目の引数まで部分適用 f 2 3 :: Num a => a -> a λ> f 2 3 4 24 it :: Num a => a 54

Slide 55

Slide 55 text

/* Scala */ scala> :t (1 to 10).map // メソッドが関数になる(eta-expansion) (Int => Any) => IndexedSeq[Any] scala> :t (_: Int) + (_: Int) // (x, y) => x + y と等価 (Int, Int) => Int scala> :t (_: Int) + 1 // x => x + 1 と等価( 部分適用) Int => Int scala> (0 to 9).map(_ + 1) val res0: IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> def f(x: Int)(y: Int)(z: Int): Int = x * y * z def f(x: Int)(y: Int)(z: Int): Int scala> f(2)(3) // 2 番目の引数まで部分適用 val res1: Int => Int = Lambda/0x00001ff001554c40@478c84aa scala> f(2)(3)(4) val res2: Int = 24 55

Slide 56

Slide 56 text

;;; Clojure user=> #(+ %1 %2 %3) ; (fn [x y z] (+ x y z)) と等価 #object[user$eval245$fn__246 0x7103ab0 "user$eval245$fn__246@ 7103ab0"] user=> #(+ % 1) ; (fn [x] (+ x 1)) と等価( 部分適用) #object[user$eval235$fn__236 0x4c03a37 "user$eval235$fn__236@ 4c03a37"] user=> (map #(+ % 1) (range 0 (inc 9))) (1 2 3 4 5 6 7 8 9 10) user=> (defn f [x y z] (* x y z)) #'user/f user=> (partial f 2 3) ; 2 番目の引数まで部分適用 = #(f 2 3 %) #object[clojure.core$partial$fn__5929 0x4ba89729 "clojure.cor e$partial$fn__5929@4ba89729"] user=> ((partial f 2 3) 4) 24 user=> (f 2 3 4) 24 56

Slide 57

Slide 57 text

## Elixir iex(1)> &(&1 + &2) # fn (x, y) -> x + y end と等価 &:erlang.+/2 iex(2)> &(&1 + 1) # fn x -> x + 1 end と等価( 部分適用) #Function<42.81571850/1 in :erl_eval.expr/6> iex(3)> Enum.map(0..9, &(&1 + 1)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] iex(4)> f = fn x -> fn y -> fn z -> x * y * z end end end #Function<42.81571850/1 in :erl_eval.expr/6> iex(5)> f.(2).(3) # 2 番目の引数まで部分適用 #Function<42.81571850/1 in :erl_eval.expr/6> iex(6)> f.(2).(3).(4) 24 57

Slide 58

Slide 58 text

関数合成 ref. {- Haskell: import Fibonacci (fibSeq) -} λ> sumOfEvenFibs upper = sum (takeWhile (<= upper) (filter even (drop 2 fibSeq))) sumOfEvenFibs :: Integral a => a -> a λ> :{ λ| sumOfEvenFibs' upper = sum λ| $ takeWhile (<= upper) -- f $ x = f x λ| $ filter even -- ( 関数適用演算子) λ| $ drop 2 fibSeq λ| :} sumOfEvenFibs' :: Integral a => a -> a λ> sumOfEvenFibs' 4000000 4613732 it :: Integral a => a #2 Even Fibonacci Numbers - Project Euler 58

Slide 59

Slide 59 text

{- Haskell -} λ> :{ λ| sumOfEvenNums :: Integral a => a -> [a] -> a λ| sumOfEvenNums upper = sum λ| . takeWhile (<= upper) -- f . g = \x -> f (g x) λ| . filter even -- ( 関数合成演算子) λ| :} sumOfEvenNums :: Integral a => a -> [a] -> a λ> sumOfEvenNums 4000000 $ drop 2 fibSeq 4613732 it :: Integral a => a 59

Slide 60

Slide 60 text

/* Scala: import Fibonacci.fibSeq */ scala> def sumOfEvenFibs(upper: BigInt) = | fibSeq | .drop(2) | .filter(_ % 2 == 0) | .takeWhile(_ <= upper) | .sum | def sumOfEvenFibs(upper: BigInt): BigInt scala> sumOfEvenFibs(4000000) val res0: BigInt = 4613732 60

Slide 61

Slide 61 text

/* Scala */ scala> extension (nums: Seq[BigInt]) // 拡張メソッドの定義 | def sumOfEvenNums(upper: BigInt): BigInt = | nums | .filter(_ % 2 == 0) | .takeWhile(_ <= upper) | .sum | def sumOfEvenNums(ns: Seq[BigInt])(upper: BigInt): BigInt scala> fibSeq.drop(2).sumOfEvenNums(4000000) val res1: BigInt = 4613732 61

Slide 62

Slide 62 text

/* Scala */ scala> def sumOfEvenNums2(upper: BigInt)(nums: Seq[BigInt]): BigInt = | nums | .filter(_ % 2 == 0) | .takeWhile(_ <= upper) | .sum | def sumOfEvenNums2(upper: BigInt)(nums: Seq[BigInt]): BigInt scala> import scala.util.chaining._ // pipe メソッドを導入 scala> fibSeq.drop(2).pipe(sumOfEvenNums2(4000000)) val res2: BigInt = 4613732 62

Slide 63

Slide 63 text

;;; Clojure: (require '[fibonacci :refer [fib-seq]]) user=> (defn sum-of-even-fibs [upper] (apply + (take-while #(<= % upper) (filter even? (drop 2 (fib-seq)))))) #'user/sum-of-even-fibs user=> (defn sum-of-even-fibs' [upper] (->> (fib-seq) ; (->> x f g) = (g (f x)) (drop 2) ; (thread-last マクロ) (filter even?) (take-while #(<= % upper)) (apply +))) #'user/sum-of-even-fibs' user=> (sum-of-even-fibs' 4000000) 4613732 63

Slide 64

Slide 64 text

;;; Clojure user=> (defn sum-of-even-fibs'' [upper] (transduce ;; 関数合成関数comp でトランスデューサー(transducer) を合成 (comp (drop 2) (filter even?) (take-while #(<= % upper))) + (fib-seq))) #'user/sum-of-even-fibs'' user=> user=> (sum-of-even-fibs'' 4000000) 4613732 user=> (defn sum-of-even-nums [upper nums] (transduce (comp (filter even?) (take-while #(<= % upper))) + nums)) #'user/sum-of-even-nums user=> (->> (fib-seq) (drop 2) (sum-of-even-nums 4000000)) 4613732 64

Slide 65

Slide 65 text

## Elixir: import Fibonacci, only: [fib_seq: 0] iex(2)> require Integer Integer iex(3)> sum_of_even_fibs = fn upper -> ...(3)> Enum.sum(Stream.take_while(Stream.filter( Stream.drop(fib_seq, 2), &Integer.is_even/1), &(&1 <= upper))) ...(3)> end #Function<42.81571850/1 in :erl_eval.expr/6> iex(4)> sum_of_even_fibs2 = fn upper -> ...(4)> fib_seq # x |> f |> g = g(f(x)) ...(4)> |> Stream.drop(2) # ( パイプ演算子) ...(4)> |> Stream.filter(&Integer.is_even/1) ...(4)> |> Stream.take_while(&(&1 <= upper)) ...(4)> |> Enum.sum() ...(4)> end #Function<42.81571850/1 in :erl_eval.expr/6> iex(5)> sum_of_even_fibs2.(4000000) 4613732 65

Slide 66

Slide 66 text

## Elixir iex(6)> sum_of_even_nums = fn nums, upper -> ...(6)> nums ...(6)> |> Stream.filter(&Integer.is_even/1) ...(6)> |> Stream.take_while(&(&1 <= upper)) ...(6)> |> Enum.sum() ...(6)> end #Function<41.81571850/2 in :erl_eval.expr/6> iex(7)> fib_seq |> Stream.drop(2) |> sum_of_even_nums.(4000000) 4613732 66

Slide 67

Slide 67 text

3-3. 🍷 データ 67

Slide 68

Slide 68 text

パターン (patterns) 再帰 (recursion) 遅延評価 (lazy evaluation) メモ化 (memoization) 並⾏プログラミング (concurrent programming) アクターモデル (actor model) STM (software transactional memory) CSP (communicating sequential processes) 継続 (continuation) ⾼階関数 (higher-order function) 関数合成 (function composition) カリー化 (currying) 部分適⽤ (partial application) 評価 (evaluation) 制御 (control) 関数 (function) パイプ演算⼦ (pipe operator) メソッドチェーン (method chaining) 代数的データ型 (algebraic data type; ADT) パターンマッチ (pattern matching) クロージャー/ 関数閉包 (closure) オブジェクト (object) 抽象データ型 (abstract data type) データ (data) 直和型 (sum type) 直積型 (product type) カプセル化 (encapsulation) ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相 (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) 分配束縛 (destructuring) 純粋関数型データ構造 (purely functional data structure) 永続データ構造 (persistent data structure) オーバーロード/ 多重定義 (overloading) pipes & filters goroutines & channels データ指向プログラミング (data-oriented programming) ファンクター (functor) モナド (monad) リスト (list) 遅延リスト/ ストリーム (lazy list/stream) ベクター (vector) 全域関数 (total function) 部分関数 (partial function) オブジェクト指向プログラミング (object-oriented programming) アプリカティブ (applicative) トレイト (trait) 必要呼び (call by need) ジェネリクス (generics) 末尾再帰 (tail recursion) 再帰型 (recursive type) GoF デザインパターン (GoF Design Patterns) 68

Slide 69

Slide 69 text

代数的データ型 (algebraic data type; ADT) パターンマッチ (pattern matching) 抽象データ型 (abstract data type) データ (data) 直和型 (sum type) 直積型 (product type) カプセル化 (encapsulation) 分配束縛 (destructuring) 純粋関数型データ構造 (purely functional data structure) 永続データ構造 (persistent data structure) データ指向プログラミング (data-oriented programming) ファンクター (functor) モナド (monad) リスト (list) 遅延リスト/ ストリーム (lazy list/stream) ベクター (vector) オブジェクト指向プログラミング (object-oriented programming) アプリカティブ (applicative) 再帰型 (recursive type) 69

Slide 70

Slide 70 text

不変なデータ構造を関数を介して変換する データ型を定義したり構築したり分解したり → 不変データ構造が標準で豊富で、データ型の定義/ 利用が楽だと旨い😋 70

Slide 71

Slide 71 text

標準の不変/ 永続コレクション {- Haskell -} λ> :t [1, 2, 3] -- ( 連結) リスト [1, 2, 3] :: Num a => [a] λ> 0 : [1, 2, 3] -- 先頭に要素追加(cons) [0,1,2,3] it :: Num a => [a] λ> import qualified Data.Vector as V λ> :t V.fromList [1, 2, 3] -- ベクター( 可変長配列) V.fromList [1, 2, 3] :: Num a => V.Vector a λ> V.snoc (V.fromList [1, 2, 3]) 4 -- 末尾に要素追加(snoc) [1,2,3,4] it :: Num a => V.Vector a 71

Slide 72

Slide 72 text

{- Haskell -} λ> import qualified Data.Set as S λ> :t S.fromList [1, 2, 3] -- セット S.fromList [1, 2, 3] :: (Ord a, Num a) => S.Set a λ> S.insert 4 $ S.fromList [1, 2, 3] fromList [1,2,3,4] it :: (Ord a, Num a) => S.Set a λ> import qualified Data.Map as M λ> :t M.fromList [("a", 1), ("b", 2), ("c", 3)] -- マップ M.fromList [("a", 1), ("b", 2), ("c", 3)] :: Num a => M.Map String a λ> M.insert "d" 4 $ M.fromList [("a", 1), ("b", 2), ("c", 3)] fromList [("a",1),("b",2),("c",3),("d",4)] it :: Num a => M.Map String a 72

Slide 73

Slide 73 text

{- Haskell -} λ> [x * y | x <- [1..2], y <- [1..9]] -- リスト内包表記 [1,2,3,4,5,6,7,8,9,2,4,6,8,10,12,14,16,18] it :: (Enum a, Num a) => [a] λ> :{ λ| do -- do 記法(a.k.a. モナド内包表記) λ| x <- [1..2] λ| y <- [1..9] λ| return $ x * y λ| :} [1,2,3,4,5,6,7,8,9,2,4,6,8,10,12,14,16,18] it :: (Num b, Enum b) => [b] λ> :{ λ| do -- Maybe, Either など任意のモナドで利用できる λ| x <- Just 2 λ| y <- Just 3 λ| return $ x * y λ| :} Just 6 it :: Num b => Maybe b 73

Slide 74

Slide 74 text

/* Scala */ scala> List(1, 2, 3) // ( 連結) リスト val res0: List[Int] = List(1, 2, 3) scala> 0 :: List(1, 2, 3) // 先頭に要素追加(cons) val res1: List[Int] = List(0, 1, 2, 3) scala> Vector(1, 2, 3) // ベクター( 可変長配列) val res2: Vector[Int] = Vector(1, 2, 3) scala> Vector(1, 2, 3) :+ 4 // 末尾に要素追加 val res3: Vector[Int] = Vector(1, 2, 3, 4) 74

Slide 75

Slide 75 text

/* Scala */ scala> Set(1, 2, 3) // セット val res4: Set[Int] = Set(1, 2, 3) scala> Set(1, 2, 3) + 4 // 要素追加 val res5: Set[Int] = Set(1, 2, 3, 4) scala> Map("a" -> 1, "b" -> 2, "c" -> 3) // マップ val res6: Map[String, Int] = Map(a -> 1, b -> 2, c -> 3) scala> Map("a" -> 1, "b" -> 2, "c" -> 3) + ("d" -> 4) val res7: Map[String, Int] = Map(a -> 1, b -> 2, c -> 3, d -> 4) 75

Slide 76

Slide 76 text

/* Scala */ scala> for // for 式(a.k.a. for 内包表記) | x <- 1 to 2 | y <- 1 to 9 | yield x * y val res8: IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, 6, 8, 10, 12, 14, 16, 18) scala> for // Option, Either などflatMap/map を備えた型で利用できる | x <- Some(2) | y <- Some(3) | yield x * y val res9: Option[Int] = Some(6) 76

Slide 77

Slide 77 text

;;; Clojure user=> (type '(1 2 3)) ; ( 連結) リスト clojure.lang.PersistentList user=> (conj '(1 2 3) 0) ; 先頭に要素追加 (0 1 2 3) user=> (cons 0 '(1 2 3)) ; シーケンスとして要素追加 (0 1 2 3) user=> (type [1 2 3]) ; ベクター( 可変長配列) clojure.lang.PersistentVector user=> (conj [1 2 3] 4) ; 末尾に要素追加 [1 2 3 4] user=> (cons 0 [1 2 3]) ; シーケンスとして要素追加 (0 1 2 3) 77

Slide 78

Slide 78 text

;;; Clojure user=> (type #{1 2 3}) ; セット clojure.lang.PersistentHashSet user=> (conj #{1 2 3} 4) ; 要素追加 #{1 4 3 2} user=> (cons 0 #{1 2 3}) ; シーケンスとして要素追加 (0 1 3 2) user=> (type {:a 1 :b 2 :c 3}) ; マップ clojure.lang.PersistentArrayMap user=> (assoc {:a 1 :b 2 :c 3} :d 4) ; エントリー追加 {:a 1, :b 2, :c 3, :d 4} user=> (cons [:z 0] {:a 1 :b 2 :c 3}) ; シーケンスとして要素追加 ([:z 0] [:a 1] [:b 2] [:c 3]) 78

Slide 79

Slide 79 text

;;; Clojure user=> (for [x (range 1 (inc 2)) ; for マクロ y (range 1 (inc 9))] ; (a.k.a. シーケンス内包表記) (* x y)) (1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18) ;; 様々なデータがシーケンス( 論理的なリスト) として扱える(seqable) user=> (take 2 "abc") ; 文字列 (\a \b) ; 文字シーケンス user=> (take 2 (java.util.List/of 1 2 3)) ; Java のリスト (1 2) ; Clojure のシーケンス user=> (take 2 {:a 1 :b 2 :c 3}) ; マップ ([:a 1] [:b 2]) ; エントリー( ベクター) シーケンス user=> (require '[clojure.java.io :as io]) nil user=> (with-open [r (io/reader "fibonacci.clj")] ; ファイル (->> r line-seq (take 3) doall)) ("(ns fibonacci)" "" "(defn fib [n]") ; テキスト行シーケンス 79

Slide 80

Slide 80 text

## Elixir iex(1)> i [1, 2, 3] # ( 連結) リスト Term [1, 2, 3] Data type List ... iex(2)> [0 | [1, 2, 3]] # 先頭に要素追加(cons) [0, 1, 2, 3] iex(3)> i [a: 1, b: 2, c: 3] # キーワードリスト Term [a: 1, b: 2, c: 3] Data type List ... iex(4)> [{:z, 0} | [a: 1, b: 2, c: 3]] # 先頭に要素追加 [z: 0, a: 1, b: 2, c: 3] 80

Slide 81

Slide 81 text

## Elixir iex(5)> i MapSet.new([1, 2, 3]) # セット Term MapSet.new([1, 2, 3]) Data type MapSet ... iex(6)> MapSet.put(MapSet.new([1, 2, 3]), 4) MapSet.new([1, 2, 3, 4]) iex(7)> i %{a: 1, b: 2, c: 3} # マップ Term %{c: 3, b: 2, a: 1} Data type Map ... iex(8)> Map.put(%{a: 1, b: 2, c: 3}, :d, 4) %{c: 3, b: 2, a: 1, d: 4} 81

Slide 82

Slide 82 text

## Elixir iex(9)> for x <- 1..2, # Enumerable に対する内包表記 ...(9)> y <- 1..9 do ...(9)> x * y ...(9)> end [1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 4, 6, 8, 10, 12, 14, 16, 18] # Enumerable であればEnum モジュールの関数で扱える iex(10)> Enum.take([a: 1, b: 2, c: 3], 2) [a: 1, b: 2] iex(11)> Enum.take(MapSet.new([1, 2, 3]), 2) [1, 2] iex(12)> Enum.take(%{a: 1, b: 2, c: 3}, 2) [c: 3, b: 2] iex(13)> Enum.take([1, 2, 3], 2) [1, 2] 82

Slide 83

Slide 83 text

データ型の定義と値の構築・分解/ 分岐 {- Haskell -} λ> :{ λ| data Tree a -- 代数的データ型の定義 λ| = Leaf !a λ| | Branch { left :: !(Tree a) λ| , right :: !(Tree a) λ| } λ| deriving (Show, Eq) λ| :} type Tree :: * -> * data Tree a = ... left :: Tree a -> Tree a right :: Tree a -> Tree a 83

Slide 84

Slide 84 text

{- Haskell -} λ> t = Branch (Branch (Leaf 1) (Branch (Leaf 2) (Leaf 3))) (Leaf 4) t :: Num a => Tree a [Prelude] λ> left t Branch {left = Leaf 1, right = Branch {left = Leaf 2, right = Leaf 3}} it :: Num a => Tree a λ> right t Leaf 4 it :: Num a => Tree a λ> :{ λ| size :: Tree a -> Int λ| size (Leaf _) = 1 -- 関数の引数でのパターンマッチ λ| size (Branch l r) = 1 + size l + size r λ| :} size :: Tree a -> Int λ> size t 7 it :: Int 84

Slide 85

Slide 85 text

/* Scala */ scala> enum Tree[+A]: // 代数的データ型の定義 | case Leaf(value: A) | case Branch(left: Tree[A], right: Tree[A]) | // defined class Tree scala> import Tree.* scala> val t: Branch[Int] = Branch(Branch(Leaf(1), Branch( Leaf(2), Leaf(3))), Leaf(4)) val t: Tree.Branch[Int] = Branch(Branch(Leaf(1),Branch( Leaf(2),Leaf(3))),Leaf(4)) scala> t.left val res0: Tree[Int] = Branch(Leaf(1),Branch(Leaf(2),Leaf(3))) scala> t.right val res1: Tree[Int] = Leaf(4) 85

Slide 86

Slide 86 text

/* Scala */ scala> extension [A](tree: Tree[A]) // 拡張メソッドの定義 | def size: Int = tree match | case Leaf(_) => 1 | case Branch(l, r) => 1 + l.size + r.size | def size[A](tree: Tree[A]): Int scala> t.size val res2: Int = 7 86

Slide 87

Slide 87 text

;;; Clojure user=> (defrecord Leaf [value]) ; レコードの定義 user.Leaf user=> (defrecord Branch [left right]) user.Branch user=> (def t (->Branch (->Branch (->Leaf 1) (->Branch (->Leaf 2) (->Leaf 3))) (->Leaf 4))) #'user/t user=> (:left t) #user.Branch{:left #user.Leaf{:value 1}, :right #user.Branch{ :left #user.Leaf{:value 2}, :right #user.Leaf{:value 3}}} user=> (:right t) #user.Leaf{:value 4} 87

Slide 88

Slide 88 text

;;; Clojure user=> (defmulti size class) ; マルチメソッドの定義 #'user/size user=> (defmethod size Leaf [_] 1) #object[clojure.lang.MultiFn 0x6fc3e1a4 "clojure.lang.MultiFn @6fc3e1a4"] ; ↓ 関数の引数での分配束縛 user=> (defmethod size Branch [{:keys [left right]}] (+ 1 (size left) (size right))) #object[clojure.lang.MultiFn 0x6fc3e1a4 "clojure.lang.MultiFn @6fc3e1a4"] user=> (size t) 7 88

Slide 89

Slide 89 text

## Elixir iex(1)> defmodule Leaf do # 構造体の定義 ...(1)> defstruct [:value] ...(1)> end {:module, Leaf, ...} iex(2)> defmodule Branch do ...(2)> defstruct [:left, :right] ...(2)> end {:module, Branch, ...} iex(3)> t = %Branch{left: %Branch{left: %Leaf{value: 1}, right: %Branch{left: %Leaf{value: 2}, right: %Leaf{value: 3}}}, right: %Leaf{value: 4}} %Branch{...} iex(4)> t.left %Branch{left: %Leaf{value: 1}, right: %Branch{left: %Leaf{ value: 2}, right: %Leaf{value: 3}}} iex(5)> t.right %Leaf{value: 4} 89

Slide 90

Slide 90 text

## Elixir iex(6)> defmodule Tree do ...(6)> def size(%Leaf{}), do: 1 # 関数の引数でのパターンマッチ ...(6)> def size(%Branch{left: left, right: right}) do ...(6)> 1 + size(left) + size(right) ...(6)> end ...(6)> end {:module, Tree, ...} iex(7)> Tree.size(t) 7 90

Slide 91

Slide 91 text

3-4. 🍷 評価 91

Slide 92

Slide 92 text

パターン (patterns) 再帰 (recursion) 遅延評価 (lazy evaluation) メモ化 (memoization) 並⾏プログラミング (concurrent programming) アクターモデル (actor model) STM (software transactional memory) CSP (communicating sequential processes) 継続 (continuation) ⾼階関数 (higher-order function) 関数合成 (function composition) カリー化 (currying) 部分適⽤ (partial application) 評価 (evaluation) 制御 (control) 関数 (function) パイプ演算⼦ (pipe operator) メソッドチェーン (method chaining) 代数的データ型 (algebraic data type; ADT) パターンマッチ (pattern matching) クロージャー/ 関数閉包 (closure) オブジェクト (object) 抽象データ型 (abstract data type) データ (data) 直和型 (sum type) 直積型 (product type) カプセル化 (encapsulation) ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相 (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) 分配束縛 (destructuring) 純粋関数型データ構造 (purely functional data structure) 永続データ構造 (persistent data structure) オーバーロード/ 多重定義 (overloading) pipes & filters goroutines & channels データ指向プログラミング (data-oriented programming) ファンクター (functor) モナド (monad) リスト (list) 遅延リスト/ ストリーム (lazy list/stream) ベクター (vector) 全域関数 (total function) 部分関数 (partial function) オブジェクト指向プログラミング (object-oriented programming) アプリカティブ (applicative) トレイト (trait) 必要呼び (call by need) ジェネリクス (generics) 末尾再帰 (tail recursion) 再帰型 (recursive type) GoF デザインパターン (GoF Design Patterns) 92

Slide 93

Slide 93 text

遅延評価 (lazy evaluation) メモ化 (memoization) 評価 (evaluation) 必要呼び (call by need) 93

Slide 94

Slide 94 text

純粋関数、不変データを前提とすると、評価の順序 やタイミングの自由度が高まる 必要になるまで評価を先送りしたり( 遅延評価) 、評 価済みの値を再利用したり( メモ化) → 目的に合わせて評価を制御できると旨い😋 94

Slide 95

Slide 95 text

評価の制御と遅延コレクション {- Haskell: 言語のデフォルトの評価戦略は遅延( 非正格) 評価 -} -- 関数の定義: call by need λ> f x y = if x > 0 then x else y f :: (Ord a, Num a) => a -> a -> a -- 引数y にアクセスされなければエラーが生じない λ> f 1 (2 `div` 0) 1 it :: Integral a => a λ> f 0 (2 `div` 0) *** Exception: divide by zero -- bang pattern を利用した関数の定義: call by value λ> g !x !y = if x > 0 then x else y g :: (Ord a, Num a) => a -> a -> a -- 引数x, y は直ちに評価される λ> g 1 (2 `div` 0) *** Exception: divide by zero 95

Slide 96

Slide 96 text

{- Haskell -} -- 無限に続く整数のリストから先頭3 要素を取り出す λ> take 3 [0..] [0,1,2] it :: (Num a, Enum a) => [a] -- 3 番目の要素にアクセスしなければエラーが生じない λ> [1, 2, 3 `div` 0] !! 2 *** Exception: divide by zero λ> take 2 [1, 2, 3 `div` 0] [1,2] 96

Slide 97

Slide 97 text

{- Haskell -} -- データ型の定義 λ> data Pair = Pair { l :: Int, r :: Int } ... -- r にアクセスしなければエラーが生じない λ> l $ Pair 1 (2 `div` 0) 1 it :: Int λ> r $ Pair 1 (2 `div` 0) *** Exception: divide by zero -- 値コンストラクタに正格性フラグを利用したデータ型の定義 λ> data Pair' = Pair' { l :: !Int, r :: !Int } ... -- r にアクセスしなくてもエラーが生じる λ> l $ Pair' 1 (2 `div` 0) *** Exception: divide by zero 97

Slide 98

Slide 98 text

/* Scala: 言語の評価戦略は積極( 正格) 評価 */ // 関数の定義: call by value scala> def f(x: Int, y: Int): Int = | if (x > 0) x else y | def f(x: Int, y: Int): Int // 引数x, y は直ちに評価される scala> f(1, 2 / 0) java.lang.ArithmeticException: / by zero // 名前渡しパラメータを利用した関数の定義: call by name scala> def g(x: => Int, y: => Int): Int = | if (x > 0) x else y // この場合、x は最大2 回評価される | // ( 回避するには評価結果をローカル束縛( メモ化) して再利用) def g(x: => Int, y: => Int): Int // 引数y にアクセスされなければエラーが生じない scala> g(1, 2 / 0) val res1: Int = 1 scala> g(0, 2 / 0) java.lang.ArithmeticException: / by zero 98

Slide 99

Slide 99 text

/* Scala */ // 無限に続く整数の遅延リストから先頭3 要素を取り出す scala> LazyList.from(0).take(3).toList val res3: List[Int] = List(0, 1, 2) // 3 番目の要素にアクセスしなければエラーが生じない scala> (1 #:: 2 #:: (3 / 0) #:: LazyList.empty)(2) java.lang.ArithmeticException: / by zero scala> (1 #:: 2 #:: (3 / 0) #:: LazyList.empty).take(2) .toList val res5: List[Int] = List(1, 2) 99

Slide 100

Slide 100 text

;;; Clojure: 言語の評価戦略は積極( 正格) 評価 ;; 関数の定義: call by value user=> (defn f [x y] (if (pos? x) x y)) #'user/f ;; 引数x, y は直ちに評価される user=> (f 1 (/ 2 0)) Execution error (ArithmeticException) at user/eval373 ... Divide by zero ;; マクロの定義: call by name user=> (defmacro g [x y] `(if (pos? ~x) ~x ~y)) ; この場合、x は最大2 回評価される #'user/g ; ( 回避するには評価結果をローカル束縛( メモ化) して再利用) ;; 引数y にアクセスされなければエラーが生じない user=> (g 1 (/ 2 0)) 1 user=> (g 0 (/ 2 0)) Execution error (ArithmeticException) at user/eval382 ... Divide by zero 100

Slide 101

Slide 101 text

;;; Clojure ;; 無限に続く整数の遅延シーケンスから先頭3 要素を取り出す user=> (take 3 (range)) (0 1 2) ;; 3 番目の要素にアクセスしなければエラーが生じない user=> (nth (lazy-seq (cons 1 (lazy-seq (cons 2 (lazy-seq (cons (/ 3 0) nil)))))) 2) Execution error (ArithmeticException) at user/eval428$fn$f... Divide by zero user=> (take 2 (lazy-seq (cons 1 (lazy-seq (cons 2 (lazy-seq (cons (/ 3 0) nil))))))) (1 2) 101

Slide 102

Slide 102 text

;;; Clojure ;; 最も基本的な高階関数map, filter さえ遅延シーケンスを返す user=> (type (map inc (range 0 (inc 9)))) clojure.lang.LazySeq user=> (type (filter odd? (range 0 (inc 9)))) clojure.lang.LazySeq user=> (require '[clojure.java.io :as io]) nil ;; line-seq も遅延シーケンスを返すため実体化前にリソース解放するとエラー user=> (with-open [r (io/reader "fibonacci.clj")] (->> r line-seq (take 3))) Error printing return value (IOException) at java.io.Buffered Reader/ensureOpen (BufferedReader.java:123). Stream closed user=> (with-open [r (io/reader "fibonacci.clj")] (->> r line-seq (take 3) doall)) ; doall で直ちに実体化 ("(ns fibonacci)" "" "(defn fib [n]") 102

Slide 103

Slide 103 text

## Elixir: 言語の評価戦略は積極( 正格) 評価 # 関数の定義: call by value iex(1)> defmodule Some do ...(1)> def f(x, y) do ...(1)> if x > 0, do: x, else: y ...(1)> end ...(1)> end {:module, Some, ...} # 引数x, y は直ちに評価される iex(2)> Some.f(1, 2 / 0) ** (ArithmeticError) bad argument in arithmetic expression... 103

Slide 104

Slide 104 text

## Elixir # マクロの定義: call by name iex(3)> defmodule Other do ...(3)> defmacro g(x, y) do ...(3)> quote do ...(3)> if unquote(x) > 0, # この場合、x は最大2 回評価される ...(3)> do: unquote(x), else: unquote(y) ...(3)> end ...(3)> end # ( 回避するには評価結果をローカル束縛( メモ化) して再利用) ...(3)> end {:module, Other, ...} iex(4)> require Other Other # 引数y にアクセスされなければエラーが生じない iex(5)> Other.g(1, 2 / 0) 1 iex(6)> Other.g(0, 2 / 0) ** (ArithmeticError) bad argument in arithmetic expression... 104

Slide 105

Slide 105 text

## Elixir # 無限に続く整数のストリームから先頭3 要素を取り出す iex(7)> Enum.take(Stream.from_index, 3) [0, 1, 2] # 3 番目の要素にアクセスしなければエラーが生じない iex(8)> 1..3 |> Stream.map(fn n -> if n < 3, do: n, else: n / 0 end) |> Enum.at(2) ** (ArithmeticError) bad argument in arithmetic expression... iex(9)> 1..3 |> Stream.map(fn n -> if n < 3, do: n, else: n / 0 end) |> Enum.take(2) [1, 2] 105

Slide 106

Slide 106 text

BONUS: ( 評価の制御といえば) マクロ i.e. AST レベルでのコンパイル時メタプログラミング 式 (op arg0 arg1 ...) の個々の引数 arg0, arg1, ... がオペレータ op に適用される前に関数 action-fn の呼び出しを差し込めるマクロ ;;; Clojure (defmacro apply-after [action-fn op & args] ; マクロの定義 (let [args (->> args (map (juxt identity pr-str)) (map-indexed (fn [i [expr s]] `(doto ~expr (~action-fn ~i ~s)))))] `(~op ~@args))) 106

Slide 107

Slide 107 text

;;; Clojure user=> (defn inspect [v i s] (println (str "arg" i ":") s "=>" v)) #'user/inspect user=> (apply-after inspect + (- 1 2 3) (* 4 5) (/ 6 7)) arg0: (- 1 2 3) => -4 arg1: (* 4 5) => 20 arg2: (/ 6 7) => 6/7 118/7 ; (+ (- 1 2 3) (* 4 5) (/ 6 7)) と等価 user=> (apply-after inspect if (odd? 2) (println :odd) (println :even)) arg0: (odd? 2) => false :even ; (println :even) による標準出力 arg2: (println :even) => nil nil ; (if (odd? 2) (println :odd) (println :even)) と等価 107

Slide 108

Slide 108 text

;;; Clojure user=> (macroexpand-1 ; マクロ展開(1 段階) '(apply-after inspect if (odd? 2) (println :odd) (println :even))) (if (clojure.core/doto (odd? 2) (inspect 0 "(odd? 2)")) (clojure.core/doto (println :odd) (inspect 1 "(println :odd)")) (clojure.core/doto (println :even) (inspect 2 "(println :even)"))) nil 108

Slide 109

Slide 109 text

3-5. 🍷 ポリモーフィズム 109

Slide 110

Slide 110 text

パターン (patterns) 再帰 (recursion) 遅延評価 (lazy evaluation) メモ化 (memoization) 並⾏プログラミング (concurrent programming) アクターモデル (actor model) STM (software transactional memory) CSP (communicating sequential processes) 継続 (continuation) ⾼階関数 (higher-order function) 関数合成 (function composition) カリー化 (currying) 部分適⽤ (partial application) 評価 (evaluation) 制御 (control) 関数 (function) パイプ演算⼦ (pipe operator) メソッドチェーン (method chaining) 代数的データ型 (algebraic data type; ADT) パターンマッチ (pattern matching) クロージャー/ 関数閉包 (closure) オブジェクト (object) 抽象データ型 (abstract data type) データ (data) 直和型 (sum type) 直積型 (product type) カプセル化 (encapsulation) ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相 (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) 分配束縛 (destructuring) 純粋関数型データ構造 (purely functional data structure) 永続データ構造 (persistent data structure) オーバーロード/ 多重定義 (overloading) pipes & filters goroutines & channels データ指向プログラミング (data-oriented programming) ファンクター (functor) モナド (monad) リスト (list) 遅延リスト/ ストリーム (lazy list/stream) ベクター (vector) 全域関数 (total function) 部分関数 (partial function) オブジェクト指向プログラミング (object-oriented programming) アプリカティブ (applicative) トレイト (trait) 必要呼び (call by need) ジェネリクス (generics) 末尾再帰 (tail recursion) 再帰型 (recursive type) GoF デザインパターン (GoF Design Patterns) 110

Slide 111

Slide 111 text

ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相 (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) オーバーロード/ 多重定義 (overloading) オブジェクト指向プログラミング (object-oriented programming) トレイト (trait) ジェネリクス (generics) 111

Slide 112

Slide 112 text

ポリモーフィズム( 多相/ 多態性) というと、オブジェ クト指向言語での継承による仕組み(= サブタイプ 多相の一例) が想起されやすいかもしれない 関数型言語では異なる機構が提供されていることも 多い → 簡潔に柔軟に関数の振る舞いをポリモーフィック にできると旨い😋 112

Slide 113

Slide 113 text

サブタイプ多相に関する仕組み /* Scala */ scala> trait Solid: // トレイトの定義 | def surfaceArea: Double | def volume: Double | // defined trait Solid scala> case class Cuboid( // トレイトSolid を実装したクラスの定義 | a: Double, | b: Double, | c: Double, | ) extends Solid: | def surfaceArea: Double = | 2.0 * (a * b + b * c + c * a) | def volume: Double = | a * b * c 113

Slide 114

Slide 114 text

/* Scala */ scala> case class Sphere(r: Double) extends Solid: | def surfaceArea: Double = | 4.0 * Math.PI * Math.pow(r, 2) | def volume: Double = | 4.0 / 3.0 * Math.PI * Math.pow(r, 3) scala> def solidProps(x: Solid): Map[String, Double] = | Map( | "surface area" -> x.surfaceArea, | "volume" -> x.volume, | ) | def solidProps(x: Solid): Map[String, Double] scala> Seq(Cuboid(2, 3, 4), Sphere(2)).map(solidProps) val res0: Seq[Map[String, Double]] = List(Map(surface area -> 52.0, volume -> 24.0), Map(surface area -> 50.26548245743669, volume -> 33.510321638291124)) 114

Slide 115

Slide 115 text

アドホック多相に関する仕組み {- Haskell -} λ> :{ λ| class Solid a where -- 型クラスの定義 λ| surfaceArea :: a -> Double λ| volume :: a -> Double λ| :} type Solid :: Constraint λ> :{ λ| data Cuboid = Cuboid λ| { a :: !Double λ| , b :: !Double λ| , c :: !Double } λ| λ| data Sphere = Sphere { r :: !Double } λ| :} type Cuboid :: * ... 115

Slide 116

Slide 116 text

{- Haskell -} λ> :{ λ| instance Solid Cuboid where -- 型クラスのインスタンスの定義 λ| surfaceArea (Cuboid a b c) = λ| 2 * (a * b + b * c + c * a) λ| volume (Cuboid a b c) = λ| a * b * c λ| λ| instance Solid Sphere where λ| surfaceArea (Sphere r) = λ| 4 * pi * r ^ 2 λ| volume (Sphere r) = λ| 4 / 3 * pi * r ^ 3 λ| :} 116

Slide 117

Slide 117 text

{- Haskell -} λ> import qualified Data.Map as M λ> :{ λ| solidProps :: Solid a => a -> M.Map String Double λ| solidProps x = M.fromList λ| [("surface area", surfaceArea x), ("volume", volume x)] λ| :} solidProps :: Solid a => a -> M.Map String Double λ> solidProps $ Cuboid 2 3 4 fromList [("surface area",52.0),("volume",24.0)] it :: M.Map String Double λ> solidProps $ Sphere 2 fromList [("surface area",50.26548245743669),("volume", 33.510321638291124)] it :: M.Map String Double 117

Slide 118

Slide 118 text

/* Scala */ scala> trait Solid2[A]: // 型クラスの定義 | extension (a: A) | def surfaceArea: Double | def volume: Double | // defined trait Solid2 scala> case class Cuboid2( | a: Double, | b: Double, | c: Double, | ) // defined case class Cuboid2 scala> case class Sphere2(r: Double) // defined case class Sphere2 118

Slide 119

Slide 119 text

/* Scala */ scala> given Solid2[Cuboid2] with // 型クラスのインスタンスの定義 | extension (x: Cuboid2) | def surfaceArea: Double = | 2.0 * (x.a * x.b + x.b * x.c + x.c * x.a) | def volume: Double = | x.a * x.b * x.c | given Solid2[Sphere2] with | extension (x: Sphere2) | def surfaceArea: Double = | 4.0 * Math.PI * Math.pow(x.r, 2) | def volume: Double = | 4.0 / 3.0 * Math.PI * Math.pow(x.r, 3) // defined object given_Solid2_Cuboid2 // defined object given_Solid2_Sphere2 119

Slide 120

Slide 120 text

/* Scala */ scala> def solidProps[A: Solid2](x: A): Map[String, Double] = | Map( | "surface area" -> x.surfaceArea, | "volume" -> x.volume, | ) | def solidProps[A](x: A)(using evidence$1: Solid2[A]): Map[String, Double] scala> solidProps(Cuboid2(2, 3, 4)) val res0: Map[String, Double] = Map(surface area -> 52.0, volume -> 24.0) scala> solidProps(Sphere2(2)) val res1: Map[String, Double] = Map(surface area -> 50.26548245743669, volume -> 33.510321638291124) 120

Slide 121

Slide 121 text

;;; Clojure user=> (defprotocol Solid ; プロトコルの定義 (surface-area [this]) (volume [this])) Solid user=> (defrecord Cuboid [a b c]) user.Cuboid user=> (defrecord Sphere [r]) user.Sphere 121

Slide 122

Slide 122 text

;;; Clojure user=> (extend-protocol Solid ; プロトコルの実装 Cuboid (surface-area [{:keys [a b c]}] (* 2 (+ (* a b) (* b c) (* c a)))) (volume [{:keys [a b c]}] (* a b c)) Sphere (surface-area [{:keys [r]}] (* 4 Math/PI (Math/pow r 2))) (volume [{:keys [r]}] (* 4/3 Math/PI (Math/pow r 3)))) nil 122

Slide 123

Slide 123 text

;;; Clojure user=> (defn solid-props [x] {:surface-area (surface-area x) :volume (volume x)}) #'user/solid-props user=> (solid-props (->Cuboid 2 3 4)) {:surface-area 52, :volume 24} user=> (solid-props (->Sphere 2)) {:surface-area 50.26548245743669, :volume 33.51032163829112} 123

Slide 124

Slide 124 text

## Elixir iex(1)> defprotocol Solid do # プロトコルの定義 ...(1)> def surface_area(x) ...(1)> def volume(x) ...(1)> end {:module, Solid, ...} iex(2)> defmodule Cuboid do ...(2)> defstruct [:a, :b, :c] ...(2)> end {:module, Cuboid, ...} iex(3)> defmodule Sphere do ...(3)> defstruct [:r] ...(3)> end {:module, Sphere, ...} 124

Slide 125

Slide 125 text

## Elixir iex(4)> defimpl Solid, for: Cuboid do # プロトコルの実装 ...(4)> def surface_area(%Cuboid{a: a, b: b, c: c}) do ...(4)> 2 * (a * b + b * c + c * a) ...(4)> end ...(4)> def volume(%Cuboid{a: a, b: b, c: c}) do ...(4)> a * b * c ...(4)> end ...(4)> end {:module, Solid.Cuboid, ...} iex(5)> defimpl Solid, for: Sphere do ...(5)> def surface_area(%Sphere{r: r}) do ...(5)> 4 * :math.pi() * r ** 2 ...(5)> end ...(5)> def volume(%Sphere{r: r}) do ...(5)> 4 / 3 * :math.pi() * r ** 3 ...(5)> end ...(5)> end {:module, Solid.Sphere, ...} 125

Slide 126

Slide 126 text

## Elixir iex(6)> solid_props = fn x -> ...(6)> %{ ...(6)> surface_area: Solid.surface_area(x), ...(6)> volume: Solid.volume(x) ...(6)> } ...(6)> end #Function<42.81571850/1 in :erl_eval.expr/6> iex(7)> solid_props.(%Cuboid{a: 2, b: 3, c: 4}) %{surface_area: 52, volume: 24} iex(8)> solid_props.(%Sphere{r: 2}) %{ surface_area: 50.26548245743669, volume: 33.510321638291124 } 126

Slide 127

Slide 127 text

パラメータ多相に関する仕組み {- Haskell -} λ> :t id -- 関数id の型(type) は? id :: a -> a -- 型a λ> id 42 -- id を数値に適用したとき 42 it :: Num a => a -- 型クラスNum の制約付きの型a λ> :t Just -- 1 引数の値コンストラクタJust Just :: a -> Maybe a λ> :t Just 42 -- Just を数値に適用したとき Just 42 :: Num a => Maybe a λ> :t Nothing -- 0 引数の値コンストラクタNothing Nothing :: Maybe a -- cf. 型Maybe a の定義( 抜粋) data Maybe a = Nothing | Just a 127

Slide 128

Slide 128 text

{- Haskell -} λ> :k Maybe -- 型コンストラクタMaybe のカインド(kind) は? Maybe :: * -> * -- 型レベルでの関数に相当 λ> :k Num a => Maybe a -- Maybe を型a に適用したとき Num a => Maybe a :: * -- 型レベルでの値に相当 λ> :k Functor -- 型クラスFunctor Functor :: (* -> *) -> Constraint -- 型レベルでの高階関数に相当 λ> :k Functor Maybe -- Functor をMaybe に適用したとき Functor Maybe :: Constraint -- cf. 型クラスFunctor の定義( 抜粋) class Functor f where fmap :: (a -> b) -> f a -> f b 128

Slide 129

Slide 129 text

型(type) カインド(kind) 値(value): a 型(type): * 関数(function)/ 値コンスト ラクタ(value constructor): a -> b 型コンストラクタ (type constructor): * -> * 高階関数(higher-order function): (a -> b) -> f a -> f b 高カインド型(higher- kinded type): (* -> *) -> * 129

Slide 130

Slide 130 text

/* Scala */ scala> [A] => (x: A) => identity(x) val res0: [A] => (x: A) => A = Lambda/0x00002000015018d8@3... scala> identity(42) val res1: Int = 42 scala> [A] => (x: A) => Some(x) val res2: [A] => (x: A) => Some[A] = Lambda/0x000020000150... scala> val x: Option[Int] = Some(42) // Some はOption の派生型 val x: Option[Int] = Some(42) scala> val y: Option[Int] = None // None はOption の派生型 val y: Option[Int] = None scala> val z: Option[Any] = x // 型パラメータA は共変(covariant) val z: Option[Any] = Some(42) // cf. 関数identity の定義( 抜粋) def identity[A](x: A): A = x // cf. 型Option[+A] の定義( 抜粋) sealed abstract class Option[+A] extends IterableOnce[A] with Product with Serializable final case class Some[+A](value: A) extends Option[A] case object None extends Option[Nothing] 130

Slide 131

Slide 131 text

/* Scala */ // 型クラスFunctor の定義 scala> trait Functor[F[_]]: | extension [A](x: F[A]) | def fmap[B](f: A => B): F[B] | // defined trait Functor // Option に対するインスタンスの定義 scala> given Functor[Option] with | extension [A](x: Option[A]) | def fmap[B](f: A => B): Option[B] = | x.map(f) | // defined object given_Functor_Option scala> (x, y) val res3: (Option[Int], Option[Int]) = (Some(42),None) scala> x.fmap(_ + 1) val res4: Option[Int] = Some(43) scala> y.fmap(_ + 1) val res5: Option[Int] = None 131

Slide 132

Slide 132 text

4. 関数型プログラマの メンタルモデル 132

Slide 133

Slide 133 text

重視する もの (values) 式指向 (expression-oriented) 不変性 (immutability) 宣⾔型プログラミング (declarative programming) ( 型) 安全性 ((type) safety) 合成可能性 (composability) 永続性 (persistence) 純粋性 (purity) 参照透過性 (referential transparency) 命令型プログラミング (imperative programming) ⽂指向 (statement-oriented) 副作⽤ (side effect) 破壊的更新 (mutation) 可変性 (mutability) ⼊出⼒ (I/O) 133

Slide 134

Slide 134 text

関数型言語使い/ 関数型プログラミング実践者の発想 ⛓️ 適切な制約が解放をもたらす 引数に対して戻り値を返す以外のことをする関数 は信頼できない😱 → 純粋関数を基本に いつでもどこでも破壊的に更新できるデータ構造 は怖い😱 → 不変/ 永続データを基本に とりうる値が分からないのは不安😱 → 不正値を 表現不能に、より( 型) 安全に 🧱 単純で安定したブロックを基礎に全体を構成し たい → 式指向に、宣言的に、合成可能に 134

Slide 135

Slide 135 text

おわりに Haskell, Scala, Clojure, Elixir を例に、関数型言語で 楽しめる基本的な旨みについて探ってみた 関数型プログラミングの本質、そこから導かれる不 可欠な要素について考えるきっかけになれば幸い 関数型プログラミング( 言語) と を 引き続きぜひご賞味ください🍷 「関数型まつり」 135

Slide 136

Slide 136 text

Further Reading 言語横断 : Scala, Erlang, Clojure, Haskell 続編 : Elm, Elixir, Idris ( 通称: SICP, 魔術師本): サンプルコードはScheme ( 通称: TAPL): サンプルコードはOCaml ( 通称: PFDS): サンプルコ ードはStandard ML, Haskell 『7 つの言語 7 つの世界』 Seven More Languages in Seven Weeks 『計算機プログラムの構造と解釈 第2 版』 『型システム入門 プログラミング言語と型の理論』 『純粋関数型データ構造』 136

Slide 137

Slide 137 text

Haskell ( 通称: すごいH 本) 『プログラミングHaskell 第2 版』 『すごいHaskell たのしく学ぼう!』 『 [増補改訂]関数プログラミング実践入門』 『Haskell 入門 関数型プログラミング言語の基礎と 実践』 137

Slide 138

Slide 138 text

Scala ( 通称: FP in Scala) ( 通称: コップ本) 最新版 『なっとく!関数型プログラミング』 『実践Scala 入門』 『Scala 関数型デザイン&プログラミング』 『Scala スケーラブルプログラミング 第4 版』 Programming in Scala, Fifth Edition Functional Programming Patterns in Scala and Clojure 138

Slide 139

Slide 139 text

Clojure ( 通称: 孔雀本) 最新版 『プログラミングClojure 第2 版』 Programming Clojure, Third Edition Getting Clojure Clojure Applied 『関数型デザイン 原則、パターン、実践』 139

Slide 140

Slide 140 text

Elixir & Erlang ( 通称: 飛行機本) ( 通称: すごいE 本) 『プログラミングElixir (第2 版) 』 『Elixir 実践入門』 『プログラミングErlang 』 『すごいErlang ゆかいに学ぼう!』 140