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

関数型言語テイスティング: Haskell, Scala, Clojure, Elixirを比...

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

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

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

Avatar for Kent OHASHI

Kent OHASHI

June 14, 2025
Tweet

More Decks by Kent OHASHI

Other Decks in Programming

Transcript

  1. のシニアエンジニア スタートアップの起業家と投資家のための業務効 率化/ 連携プラットフォームを開発している 主要技術スタック: Kotlin & TypeScript の運営にも協力 関数型プログラミング(

    言語) とLisp の熱烈な愛好者 ( 特に) と がエレガントで好き の運営スタッフ( 座長のひとり) lagénorhynque 🐬カマイルカ 株式会社スマートラウンド Server-Side Kotlin Meetup Clojure Haskell 関数型まつり2025 2
  2. [ 参考] 🐬の関数型言語学習/ 利用経験 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
  3. 初期(2013 年〜) の学びの主な機会 関数型プログラミング( 言語) に関する技術書/ 記事 コミュニティイベント 勉強会: ,

    , , , , etc. もくもく会: , 読書会: , カンファレンス: Haskell Day, ScalaMatsuri, Erlang & Elixir Fest Tamachi.clj Shibuya.lisp 渋谷java tokyo.ex rpscala Haskell もくもく会 Laketown.clj 関数型プログラミング勉強会 (Scala ) SICP 読書会 5
  4. (SICP, 魔術師本) Wikipedia 英語版の より Structure and Interpretation of Computer

    Programs Structure and Interpretation of Computer Programs 12
  5. 1. きっかけ 2. 「関数型プログラミング」 3. 🍷 テイスティング 1. 4 つの関数型言語の基本

    2. 関数 3. データ 4. 評価 5. ポリモーフィズム 4. 関数型プログラマのメンタルモデル 13
  6. エッセンス(essence) 語源: 🇬🇧 essence < essentia < esse + -ia

    (≒ 🇬🇧 beingness, あるということ) 語義: 「本質( 的に重要なもの) 」 抽出物( エキス) という派生的/ 比喩的な意味も 本来、単なる成分/ 要素のような意味ではない cf. 🇫🇷 哲学の文脈で関連する表現 l'existence précède l'essence ( 実存は本質に先立 つ) L'essentiel est invisible pour les yeux. ( 🦊「大切 なものは目に見えないんだよ。 」 ) 17
  7. ( 現在の) 🐬によるFP とFPL の定義 関数型プログラミング := 純粋関数を基本要素とし てその組み合わせによってプログラムを構成してい くプログラミングスタイル

    → 言語を問わず実践可能( 実践しやすさは異なる) 関数型言語 := 関数型プログラミングが言語/ 標準ラ イブラリレベルで十分に支援される( そして関数型 プログラミングスタイルがユビキタスな) 言語 → 例えばJavaScript/TypeScript やJava 、Kotlin 、 古典的なLisp 方言は含めない 20
  8. 重視する もの (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
  9. 重視する もの (values) 式指向 (expression-oriented) 不変性 (immutability) 宣⾔型プログラミング (declarative programming)

    ( 型) 安全性 ((type) safety) 合成可能性 (composability) 永続性 (persistence) 純粋性 (purity) 参照透過性 (referential transparency) 命令型プログラミング (imperative programming) ⽂指向 (statement-oriented) 副作⽤ (side effect) 破壊的更新 (mutation) 可変性 (mutability) ⼊出⼒ (I/O) 22
  10. ⾔語 (languages) Lisp Clojure Erlang Elixir Haskell OCaml Standard ML

    ML Idris Elm Coq (Rocq) Scala Agda F# Lean Gleam 23
  11. 理論 (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
  12. 処理系/ 実装 (implementations) 適⽤ (apply) 評価 (eval) 抽象構⽂⽊ (abstract syntax

    tree; AST) マクロ (macro) メタプログラミング (metaprogramming) パーサーコンビネーター (parser combinator) 構⽂解析 (parse) 同図像性 (homoiconicity) 超循環評価器 (meta-circular evaluator) セルフホスティング (self-hosting) 25
  13. パターン (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
  14. ⾔語 (languages) Lisp Clojure Erlang Elixir Haskell OCaml Standard ML

    ML Idris Elm Coq (Rocq) Scala Agda F# Lean Gleam 29
  15. 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
  16. モジュール定義とトップレベル変数 {- 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
  17. ;;; 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
  18. トップレベル関数 {- 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
  19. ;;; 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
  20. モジュールの利用 {- 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
  21. ;;; 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
  22. ローカル束縛( 変数) {- 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
  23. ;;; 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
  24. 無名関数( ラムダ式) {- 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
  25. ;;; 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
  26. 再帰と無限リスト(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
  27. /* 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
  28. ;;; 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
  29. ## 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
  30. 再帰と無限リスト(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
  31. /* 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
  32. ;;; 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
  33. ## 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
  34. パターン (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
  35. ⾼階関数 (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
  36. 部分適用 {- 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
  37. /* 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
  38. ;;; 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
  39. ## 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
  40. 関数合成 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
  41. {- 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
  42. /* 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
  43. /* 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
  44. /* 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
  45. ;;; 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
  46. ;;; 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
  47. ## 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
  48. ## 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
  49. パターン (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
  50. 代数的データ型 (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
  51. 標準の不変/ 永続コレクション {- 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
  52. {- 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
  53. {- 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
  54. /* 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
  55. /* 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
  56. /* 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
  57. ;;; 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
  58. ;;; 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
  59. ;;; 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
  60. ## 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
  61. ## 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
  62. ## 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
  63. データ型の定義と値の構築・分解/ 分岐 {- 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
  64. {- 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
  65. /* 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
  66. /* 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
  67. ;;; 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
  68. ;;; 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
  69. ## 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
  70. ## 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
  71. パターン (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
  72. 評価の制御と遅延コレクション {- 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
  73. {- 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
  74. {- 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
  75. /* 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
  76. /* 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
  77. ;;; 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
  78. ;;; 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
  79. ;;; 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
  80. ## 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
  81. ## 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
  82. ## 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
  83. 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
  84. ;;; 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
  85. ;;; 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
  86. パターン (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
  87. ポリモーフィズム/ 多相 (polymorphism) サブタイプ多相 (subtype polymorphism) パラメータ多相 (parametric polymorphism) アドホック多相

    (ad hoc polymorphism) 型クラス (type class) マルチメソッド (multimethod) プロトコル (protocol) 変性 (variance) 継承 (inheritance) オーバーロード/ 多重定義 (overloading) オブジェクト指向プログラミング (object-oriented programming) トレイト (trait) ジェネリクス (generics) 111
  88. サブタイプ多相に関する仕組み /* 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
  89. /* 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
  90. アドホック多相に関する仕組み {- 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
  91. {- 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
  92. {- 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
  93. /* 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
  94. /* 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
  95. /* 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
  96. ;;; 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
  97. ;;; 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
  98. ;;; 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
  99. ## 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
  100. ## 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
  101. ## 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
  102. パラメータ多相に関する仕組み {- 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
  103. {- 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
  104. 型(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
  105. /* 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
  106. /* 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
  107. 重視する もの (values) 式指向 (expression-oriented) 不変性 (immutability) 宣⾔型プログラミング (declarative programming)

    ( 型) 安全性 ((type) safety) 合成可能性 (composability) 永続性 (persistence) 純粋性 (purity) 参照透過性 (referential transparency) 命令型プログラミング (imperative programming) ⽂指向 (statement-oriented) 副作⽤ (side effect) 破壊的更新 (mutation) 可変性 (mutability) ⼊出⼒ (I/O) 133
  108. 関数型言語使い/ 関数型プログラミング実践者の発想 ⛓️ 適切な制約が解放をもたらす 引数に対して戻り値を返す以外のことをする関数 は信頼できない😱 → 純粋関数を基本に いつでもどこでも破壊的に更新できるデータ構造 は怖い😱

    → 不変/ 永続データを基本に とりうる値が分からないのは不安😱 → 不正値を 表現不能に、より( 型) 安全に 🧱 単純で安定したブロックを基礎に全体を構成し たい → 式指向に、宣言的に、合成可能に 134
  109. 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
  110. Haskell ( 通称: すごいH 本) 『プログラミングHaskell 第2 版』 『すごいHaskell たのしく学ぼう!』

    『 [増補改訂]関数プログラミング実践入門』 『Haskell 入門 関数型プログラミング言語の基礎と 実践』 137
  111. Scala ( 通称: FP in Scala) ( 通称: コップ本) 最新版

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

    Third Edition Getting Clojure Clojure Applied 『関数型デザイン 原則、パターン、実践』 139
  113. Elixir & Erlang ( 通称: 飛行機本) ( 通称: すごいE 本)

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