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

63.9K Views

June 14, 25

スライド概要

4つの関数型言語を例に、関数型プログラミング(言語)の本質を探ろう。

関数型まつり2025セッション概要: https://fortee.jp/2025fp-matsuri/proposal/f7646b8b-29b0-4ac4-8ec3-46cabaa8ef1a

profile-image

「楽しく楽にcoolにsmartに」を理想とするprogrammer/philosopher/liberalist/realist。 好きな言語はClojure, Haskell, Elixir, English, français, русский。 読書、プログラミング、語学、法学、数学が大好き! イルカと海も大好き🐬

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

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

2.

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

3.

参考] 🐬の関数型言語学習/利用経験 GitHubでのコード量 × リポジトリ数 [ 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% 3

4.

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

5.

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

6.

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

7.

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

8.

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

9.

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

10.

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

11.

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

12.

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

13.

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

14.

1. きっかけ 14

15.

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

16.

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

17.

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

18.

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

19.

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

20.

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

21.

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

22.

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

23.

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

24.

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

25.

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

26.

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

27.

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

28.

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

29.

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

30.

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

31.

モジュール定義とトップレベル変数 {- 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

32.

;;; 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

33.

トップレベル関数 {- 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

34.

;;; 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

35.
[beta]
モジュールの利用
コマンドによる

{- 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

36.
[beta]
コマンドによる

;;; 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

37.
[beta]
ローカル束縛(変数)
{- 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

38.

;;; 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

39.
[beta]
無名関数(ラムダ式)
{- 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

40.

;;; 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

41.
[beta]
再帰と無限リスト(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

42.

/* 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

43.

;;; 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

44.

## 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

45.

再帰と無限リスト(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

46.
[beta]
/* 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

47.

;;; 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

48.
[beta]
## 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

49.

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

50.

3-2. 🍷 関数 50

51.

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

52.

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

53.

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

54.
[beta]
部分適用
{- 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

55.

/* 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

56.

;;; 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

57.

## 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

58.
[beta]
関数合成
{- 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

関数適用演算子)

ref. #2 Even Fibonacci Numbers - Project Euler
58

59.
[beta]
{- 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

60.

/* 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

61.

/* 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

62.

/* 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

63.

;;; 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

64.

;;; 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

65.

## 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

66.

## 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

67.

3-3. 🍷 データ 67

68.

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

69.

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

70.

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

71.
[beta]
標準の不変/永続コレクション
{- Haskell -}
λ> :t [1, 2, 3] -- (
)
[1, 2, 3] :: Num a => [a]
λ> 0 : [1, 2, 3] -[0,1,2,3]
it :: Num a => [a]

連結 リスト
先頭に要素追加(cons)

λ> 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 -[1,2,3,4]
it :: Num a => V.Vector a

ベクター 可変長配列)
末尾に要素追加(snoc)

71

72.
[beta]
{- 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

73.
[beta]
{- 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

74.

/* Scala */ scala> List(1, 2, 3) // ( ) val res0: List[Int] = List(1, 2, 3) scala> 0 :: List(1, 2, 3) // val res1: List[Int] = List(0, 1, 2, 3) 連結 リスト 先頭に要素追加(cons) ベクター 可変長配列 末尾に要素追加 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

75.

/* 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

76.

/* 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

77.

;;; 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

78.
[beta]
;;; 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

79.
[beta]
;;; 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

80.

## Elixir iex(1)> i [1, 2, 3] # ( ) Term [1, 2, 3] Data type List ... iex(2)> [0 | [1, 2, 3]] # [0, 1, 2, 3] 連結 リスト 先頭に要素追加(cons) キーワードリスト 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

81.

## 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

82.
[beta]
## 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

83.
[beta]
データ型の定義と値の構築・分解/分岐
{- 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

84.

{- 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

85.

/* 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

86.

/* 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

87.
[beta]
;;; 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

88.
[beta]
;;; 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

89.

## 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

90.

## 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

91.

3-4. 🍷 評価 91

92.

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

93.

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

94.

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

95.
[beta]
評価の制御と遅延コレクション
言語のデフォルトの評価戦略は遅延(非正格)評価 -}

{- 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

96.
[beta]
{- 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

97.
[beta]
{- 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

98.

言語の評価戦略は積極 正格 評価 関数の定義 /* 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

99.

/* 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

100.

言語の評価戦略は積極 正格 評価 ;;; 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

101.

;;; Clojure ;; user=> (take 3 (range)) (0 1 2) 無限に続く整数の遅延シーケンスから先頭3要素を取り出す 番目の要素にアクセスしなければエラーが生じない ;; 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

102.
[beta]
;;; 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

103.

言語の評価戦略は積極 正格 評価 ## 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

104.

## Elixir # : call by name iex(3)> defmodule Other do ...(3)> defmacro g(x, y) do ...(3)> quote do ...(3)> if unquote(x) > 0, # x ...(3)> do: unquote(x), else: unquote(y) ...(3)> end ...(3)> end # ( ( ...(3)> end {:module, Other, ...} マクロの定義 この場合、 は最大2回評価される 回避するには評価結果をローカル束縛 メモ化)して再利用) 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

105.

## 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

106.

評価の制御といえば)マクロ i.e. ASTレベルでのコンパイル時メタプログラミング BONUS: ( ;;; 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))) マクロの定義 式 (op arg0 arg1 ...) の個々の引数 arg0, arg1, ...がオペレータ op に適用される前に関数 action-fn の呼び出しを差し込めるマクロ 106

107.

;;; 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

108.
[beta]
;;; 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

109.

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

110.

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

111.

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

112.

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

113.

サブタイプ多相に関する仕組み /* 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

114.

/* 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

115.
[beta]
アドホック多相に関する仕組み
{- 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

116.

{- 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

117.
[beta]
{- 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

118.

/* 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

119.

/* 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

120.

/* 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

121.

;;; 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

122.

;;; 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

123.
[beta]
;;; 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

124.

## 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

125.

## 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

126.
[beta]
## 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

127.

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

128.

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

129.

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

130.

/* 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

131.

/* 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

132.

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

133.

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

134.

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

135.

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

136.

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

137.

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

138.

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

139.

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

140.

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