---
title: 速習FP in Scala with Flix: Flix言語で親しむ純粋関数型のコード設計
tags:  #関数型プログラミング #関数型言語 #flix  
author: [Kent OHASHI](https://docswell.com/user/lagenorhynque)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/L73W6X8175.jpg?width=480
description: 書籍Functional Programming in Scala (略称&quot;FP in Scala&quot;)で豊富な演習問題とともに語られる純粋関数型プログラミングの典型的な設計パターンや抽象化の過程について、JVM関数型言語Flix ( https://flix.dev/ )のコード例を通して親しもう。  関数型まつり2026セッション概要: https://fortee.jp/2026fp-matsuri/proposal/02925098-b3c9-4814-b6a1-e0a710399be0
published: July 11, 26
canonical: https://docswell.com/s/lagenorhynque/5JWEQN-quick-learning-fp-in-scala-with-flix
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/L73W6X8175.jpg)

速習 FP in Scala with Flix
言語で親しむ純粋関数型のコード設計
Flix
#fp_matsuri #fp_matsuri_a
1


# Page. 2

![Page Image](https://bcdn.docswell.com/page/87DKV2MKJG.jpg)

🐬カマイルカ
lagénorhynque
関数型プログラミング(言語)の愛好者/実践者
特にClojure, Haskell, Flixがエレガントで好き
関数型まつり2026の運営スタッフ(座長のひとり)
主に(CfPを含む)メインコンテンツの準備を担当
株式会社スマートラウンドのシニアエンジニア
スタートアップと投資家のやり取りを効率化する
データ管理プラットフォームを開発
ソフトウェア設計/開発とともに会計/財務/法務
について探求するのも好き
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/VJPKV1R3E8.jpg)

きっかけ
書籍
とは
言語の基本
純粋 関数型のコード設計
関数型プログラミング入門
関数型設計とコンビネータライブラリ
関数型設計に共通する構造
エフェクトと
1.
2.
FP in Scala
3. Flix
4. (
)
1.
2.
3.
4.
I/O
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/2EVVZYRNEQ.jpg)

1.
きっかけ
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/57GL49M5EL.jpg)

関数型まつり2025での🐬の発表
関数型言語テイスティング: Haskell, Scala, Clojure, Elixirを比べて味わう関数型プログラミングの旨さ
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/4EQYGWRLJP.jpg)

書籍 Functional Programming in Scala
(FP in Scala)
今年度は関数型プログラミングらしいコード設計/
抽象化のパターンにより踏み込んで解説したい
関数型言語について学び始めた頃に取り組んだ本の
ひとつに FP in Scala があった
演習問題が膨大で途中まで進めて積んでいたが😇
関数型まつり2026の「FP入門ハンズオン」の題材
として FP in Scala の演習問題を利用することに
ハンズオン資料: fp-matsuri/2026.fp-hands-on
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/KJ4W5K8571.jpg)

との出会い
Flix
Clojurian
が出会ったsimple志向なJVM関数型言語Flix ——ファーストインプレッション
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/LE1YWX227G.jpg)

エフェクトシステムを備えた関数型言語Flix
エフェクトシステムと代数的エフェクトを探求する
過程で、Flixという言語に出会った
simple と easy を区別して simplicity を重視すると
いう方向性もFlixの設計指針に含まれている
Rich Hickeyの有名なプレゼンSimple Made Easy
に由来する
🐬はClojurianなので大いに感激した😆
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/GEWG6NR2J2.jpg)

参考] Shibuya.lisp lispmeetup #118での🐬の発表
[
&quot;Simple Made Easy&quot; Made Easier: Clojure
に学ぶsimplicity
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/47ZLY3R4J3.jpg)

「&quot;Simple Made Easy&quot; Made Easier 」の要約
simple は客観的な構造の単純さ、 easy は主観的な
(広義の)近さの問題(なのだが誤解されがち)
Clojureエコシステム/コミュニティには様々な
simplicity 追求の例がある
抽象度を上げて、客体/モノと主体/ヒトの区別、
さらにはars/τέχνη (技芸)とvita/βίος (人生)の対比
と捉えることもできる
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/YJ6WDXRGJV.jpg)

2.
書籍 FP in Scala とは
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/GJ5M3X8XJ4.jpg)

Functional Programming in Scala, Second Edition
の書籍詳細ページより
Manning
12


# Page. 13

![Page Image](https://bcdn.docswell.com/page/LE3W6X84E5.jpg)

『Scala関数型デザイン＆プログラミング』
第1版)の訳書
※ Functional Programming in Scala (
インプレスブックスの書籍詳細ページより
13


# Page. 14

![Page Image](https://bcdn.docswell.com/page/8EDKV2M57G.jpg)

📕
オブジェクト指向/関数型言語Scalaで(純粋関数型
言語Haskellのような) FP実践を目指す本
2014年初版当時のScalaコミュニティでは良くも
悪くも&quot;better Java&quot;扱いが珍しくなかった
ScalaでFP設計を深く学べる(おそらく)最初の本
豊富な演習問題に取り組みながら理解を深め、抽象
化の必要性/必然性を体感できる教育的な構成
ただし、独習では挫折しやすい本でもある🥹
FP in Scala, a.k.a. The Red Book
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/V7PKV1RDJ8.jpg)

書籍公式リポジトリfpinscala
演習問題/ヒント/解答例/テストコード
Wiki: 追加の解説/チャプターノート
Haskellの概説ページも
[参考] HaskellコードのFlix移植例 by 🐬
cf. Scala以外の関数型言語による実装
fp-matsuri/fp-in-scala-exercises
FP in Scala
by
lagenorhynque/fp-in-scala
Clojure/Flix
by
ベースの関数型プログラミング演習
問題 関数型まつり
実装 🐬
15


# Page. 16

![Page Image](https://bcdn.docswell.com/page/2JVVZYRGJQ.jpg)

言語の基本
3. Flix
16


# Page. 17

![Page Image](https://bcdn.docswell.com/page/5EGL496DJL.jpg)

とは
エフェクトシステムの組み込みサポートが特徴的な
静的型付き関数型言語
副作用を伴う関数には IO エフェクトが付く
Scalaと関係が深いが非オブジェクト指向言語
(サブタイピング(部分型)/継承とも無縁)
JVM言語(Scala, Kotlin, Clojureなどと同じ)
主にHaskell, Scala, OCaml, Rustの影響が見られる
処理系の実装言語もScala
🐬&lt; 大雑把には「Scalaの皮を被ったHaskell」🐑
Flix
17


# Page. 18

![Page Image](https://bcdn.docswell.com/page/4JQYGW4X7P.jpg)

ℹ️ 他の関数型言語経験者向けの補足
Hindley-Milner型システムが採用されている
HaskellやML系言語と同様
⚠️ トップレベルの定義のみ敢えて型注釈必須
関数は常にカリー化される
HaskellやML系言語と同様
構文的に多引数関数に見えても実体は1引数関数
の連鎖
Haskellによく似た型クラス(type class)がある
trait というキーワードはRust/Scala由来
18


# Page. 19

![Page Image](https://bcdn.docswell.com/page/K74W5KQ2E1.jpg)

コードの書き方(0): 事前準備
mkdir first-flix-project
cd first-flix-project
#
flix init
#
echo &#039;pub mod FirstFlixProject {}&#039; &gt; src/FirstFlixProject.flix
プロジェクトを生成
プロジェクトルートのモジュールファイルを追加
flix
コマンドでプロジェクトを生成する
19


# Page. 20

![Page Image](https://bcdn.docswell.com/page/LJ1YWXPKEG.jpg)

コードの書き方(1): モジュールとそのトップレベル関数
// src/FirstFlixProject/SimpleMath.flix
pub mod FirstFlixProject.SimpleMath {
//
pub def square(n: Int32): Int32 =
n * n
パブリック関数
プライベート関数
⚠️ 未使用のままだとコンパイルエラー
//
//
def double(n: Int32): Int32 =
n * 2
}
20


# Page. 21

![Page Image](https://bcdn.docswell.com/page/GJWG6N2P72.jpg)

コードの書き方(2): トップレベル関数による定数
// src/FirstFlixProject/SomeModule.flix
pub mod FirstFlixProject.SomeModule {
//
0
pub def a(): Int32 = 1
パブリックな 引数関数
プライベートな 引数関数
⚠️ 未使用のままだとコンパイルエラー
//
0
//
def b(): Int32 = 2
}
トップレベル変数は存在しない
21


# Page. 22

![Page Image](https://bcdn.docswell.com/page/4EZLY3D673.jpg)

コードの書き方(3): モジュールの利用
flix&gt; use FirstFlixProject.{SimpleMath =&gt; M}; \
&gt;
M.square(3)
9
flix&gt; use FirstFlixProject.{SimpleMath =&gt; M}; \
&gt;
List.map(M.square, List.range(0, 11))
0 :: 1 :: 4 :: 9 :: 16 :: 25 :: 36 :: 49 :: 64 :: 81 :: 100
:: Nil
flix&gt; use FirstFlixProject.{SimpleMath =&gt; M}; \
&gt;
List.range(0, 11) |&gt; List.map(M.square)
0 :: 1 :: 4 :: 9 :: 16 :: 25 :: 36 :: 49 :: 64 :: 81 :: 100
:: Nil
は式の区切り文字
は での行継続
はパイプ(ライン)演算子
;
\ REPL
|&gt;
22


# Page. 23

![Page Image](https://bcdn.docswell.com/page/Y76WDX1L7V.jpg)

コードの書き方(4): ローカル束縛
flix&gt; \\
&gt; use FirstFlixProject.{SimpleMath =&gt; M};
&gt; let x = 5;
&gt; let y = M.square(x);
&gt; &quot;square(${x}) = ${y}&quot;
&gt; \\
square(5) = 25
\\
はREPLでの複数行入力の開始/終了
23


# Page. 24

![Page Image](https://bcdn.docswell.com/page/G75M3X4M74.jpg)

コードの書き方(5): 無名関数(ラムダ式)
flix&gt; (n -&gt; n * n * n)(2)
8
flix&gt; List.map(n -&gt; n * n * n, List.range(0, 11))
0 :: 1 :: 8 :: 27 :: 64 :: 125 :: 216 :: 343 :: 512 :: 729 ::
1000 :: Nil
24


# Page. 25

![Page Image](https://bcdn.docswell.com/page/9J29ZXVRER.jpg)

純粋)関数型のコード設計
4. (
1.
2.
3.
4.
関数型プログラミング入門
関数型設計とコンビネータライブラリ
関数型設計に共通する構造
エフェクトとI/O
25


# Page. 26

![Page Image](https://bcdn.docswell.com/page/DEY4R835JM.jpg)

第1部 関数型プログラミング入門
関数型プログラミングとは
で始める関数型プログラミング
関数型データ構造
例外によらないエラーハンドリング
正格性と遅延性
純粋関数型の状態
1.
2. Flix
3.
4.
5.
6.
26


# Page. 27

![Page Image](https://bcdn.docswell.com/page/VJNYDQV478.jpg)

第1章 関数型プログラミングとは
27


# Page. 28

![Page Image](https://bcdn.docswell.com/page/YE9PGD64J3.jpg)

キーワード
関数型プログラミング(functional programming)
純粋関数(pure function)
副作用(side effect)
参照透過性(referential transparency)
純粋性(purity)
置換モデル(substitution model)
28


# Page. 29

![Page Image](https://bcdn.docswell.com/page/GE8DV3ZVED.jpg)

カフェでコーヒーを注文する過程を表す素朴なコード
⚠️
の かつ非 なコード
//
Scala OOP
FP
class Cafe:
def buyCoffee(cc: CreditCard): Coffee =
val cup = Coffee()
cc.charge(cup.price)
cup
class CreditCard:
def charge(price: Double): Unit =
println(&quot;charging &quot; + price) //
class Coffee:
val price: Double = 2.0
請求処理(簡単のため標準出力)
利用例
//
scala&gt; Cafe().buyCoffee(CreditCard())
charging 2.0 //
val res0: Coffee = Coffee@353cb7e5 //
標準出力
戻り値
クレジットカードへの請求処理という副作用が埋め
込まれている
29


# Page. 30

![Page Image](https://bcdn.docswell.com/page/LELM5VDR7R.jpg)

で典型的な改善策: インターフェースを導入してDI
OOP
⚠️
の かつ非 なコード
//
Scala OOP
FP
class Cafe:
def buyCoffee(cc: CreditCard, p: Payments): Coffee =
val cup = Coffee()
p.charge(cc, cup.price)
cup
//
trait Payments:
def charge(cc: CreditCard, price: Double): Unit
//
class SimulatedPayments extends Payments:
def charge(cc: CreditCard, price: Double): Unit =
println(&quot;charging &quot; + price + &quot; to &quot; + cc)
インターフェース定義
インターフェースに対する実装
副作用を含む実装を差し替えやすくなった
しかし依然として:
テストしやすいとまではいえない
buyCoffee の再利用性は低い
30


# Page. 31

![Page Image](https://bcdn.docswell.com/page/4JMY3GPPJW.jpg)

での対応A: (非OOPLなので)振る舞いを関数で渡す
Flix
mod Coffee {
// price
Coffee
pub enum Coffee({price = Float64})
//
(ToString
)
instance ToString[Coffee] {
pub def toString(coffee: Coffee): String =
let Coffee({price}) = coffee;
&quot;Coffee({price = ${price}})&quot;
}
//
(
)
pub def make(): Coffee = Coffee({price = 2.0})
}
フィールドを持つレコードによる
文字列表現
型クラスの実装
型
コンストラクタ関数 ただの関数
mod CreditCard {
//
IO
(
)
pub def charge(price: Float64): Unit \ IO =
println(&quot;charging ${price}&quot;)
}
金額を受け取って何も返さず、 エフェクト 副作用 を伴う関数
31


# Page. 32

![Page Image](https://bcdn.docswell.com/page/PJR9QZ1Y79.jpg)

mod Cafe {
use FpInFlix.Exercises.Introduction.FirstExample.Coffee
//
IO
pub def buyCoffee(charge: Float64 -&gt; Unit \ IO):
Coffee \ IO =
let coffee = Coffee.make();
let Coffee.Coffee({price}) = coffee;
charge(price);
coffee
}
請求関数を受け取ってコーヒーを返し、 エフェクトを伴う関数
利用例
//
flix&gt; use FpInFlix.Exercises.Introduction.FirstExample.{Cafe,
CreditCard}; \ //
use
&gt;
Cafe.buyCoffee(CreditCard.charge)
charging 2.0
//
Coffee({price = 2.0}) //
ℹ️ 以降はスペース節約のため を省略する
標準出力
戻り値
高階関数によるDI
高階関数は様々な設計パターンの基礎になる
32


# Page. 33

![Page Image](https://bcdn.docswell.com/page/PEXQ59Y4JX.jpg)

での対応B: 振る舞いをエフェクトで表す
Flix
mod CreditCard {
//
(
)
CreditCard (Eq, ToString
pub enum CreditCard(String) with Eq, ToString
}
//
pub eff Payment {
//
def charge(cc: CreditCard, amount: Float64): Unit
}
文字列 識別子 を持つ
型
は自動導出)
決済エフェクト
クレジットカードと金額を受け取って何かする関数
mod Cafe {
use FpInFlix.Exercises.Introduction.SecondExample.{
Coffee, CreditCard, Payment}
//
pub def buyCoffee(cc: CreditCard): Coffee \ Payment =
let coffee = Coffee.make();
let Coffee.Coffee({price}) = coffee;
Payment.charge(cc, price); //
coffee
}
請求関数を受け取ってコーヒーを返し、決済エフェクトを伴う関数
決済エフェクトの関数を利用
33


# Page. 34

![Page Image](https://bcdn.docswell.com/page/3EK96P3PED.jpg)

pub def example(): Unit \ IO =
let cup = run {
Cafe.buyCoffee(CreditCard.CreditCard(&quot;A&quot;))
} with handler Payment { //
def charge(cc, amount, resume) = {
println(&quot;charging ${amount} to ${cc}&quot;);
resume() //
}
//
(
)
};
println(&quot;a cup of ${cup}&quot;)
決済エフェクトに対するハンドラー
エフェクト発生地点から続く処理を再開
継続を呼び出す
利用例
//
flix&gt; example()
charging 2.0 to CreditCard(A)
a cup of Coffee({price = 2.0})
() //
戻り値
//
//
標準出力(Payment.charge)
標準出力(exampleの最後)
代数的エフェクトとエフェクトハンドラーによるDI
エフェクトシステムの活用例
34


# Page. 35

![Page Image](https://bcdn.docswell.com/page/L73W6XG475.jpg)

での対応C: 副作用を完全に隔離する
Flix
mod Charge {
use FpInFlix.Exercises.Introduction.ThirdExample.
CreditCard
// creditCard, amount
Charge
pub enum Charge({
creditCard = CreditCard,
amount = Float64
})
//
(ToString
)
instance ToString[Charge] {
pub def toString(charge: Charge): String =
let Charge({creditCard, amount}) = charge;
&quot;Charge({creditCard = ${creditCard}, amount =
${amount}})&quot;
}
フィールドを持つレコードによる
文字列表現
型
型クラスの実装
「請求」をただのデータとして表す
解釈は利用側に委ねる
35


# Page. 36

![Page Image](https://bcdn.docswell.com/page/87DKV215JG.jpg)

クレジットカードが同一の場合に限って つの請求をまとめる純粋関数
//
2
pub def combine(a: Charge, b: Charge): Result[String,
Charge] =
let Charge({creditCard=cardA, amount=amountA}) = a;
let Charge({creditCard=cardB, amount=amountB}) = b;
if (cardA == cardB) Ok(Charge({creditCard = cardA,
amount = amountA + amountB}))
else Err(&quot;Can&#039;t combine charges to different cards:
cardA=${cardA}, cardB=${cardB}&quot;)
請求リストをクレジットカードごとにまとめる純粋関数
//
pub def coalesce(charges: List[Charge]): Result[String,
List[Charge]] =
charges
|&gt; List.groupBy(charge -&gt; //
let Charge({creditCard | _}) = charge;
creditCard
)
|&gt; Traversable.traverse(group -&gt; // combine
Foldable.foldLeftM(combine, Nel.head(group),
Nel.tail(group)))
カードでグループ化
で集約
}
36


# Page. 37

![Page Image](https://bcdn.docswell.com/page/VJPKV1QDE8.jpg)

mod Cafe {
use FpInFlix.Exercises.Introduction.ThirdExample.
{Charge, Coffee, CreditCard}
//
pub def buyCoffee(cc: CreditCard): (Coffee, Charge) =
let coffee = Coffee.make();
let Coffee.Coffee({price}) = coffee;
let charge = Charge.Charge({creditCard = cc,
amount = price});
(coffee, charge)
カードを受け取ってコーヒーと請求を返す純粋関数
カードと個数を受け取ってコーヒーリストと請求を返す純粋関数
//
pub def buyCoffees(cc: CreditCard, n: Int32):
Result[String, (List[Coffee], Charge)] =
let (coffees, charges) = buyCoffee(cc)
|&gt; List.repeat(n) |&gt; List.unzip; // n
charges //
|&gt; Foldable.foldLeftM(Charge.combine,
Charge.Charge({creditCard = cc,
amount = 0.0}))
|&gt; Functor.map(charge -&gt; (coffees, charge))
個繰り返し
コーヒーリストと請求のタプルに集約
}
37


# Page. 38

![Page Image](https://bcdn.docswell.com/page/2EVVZY5GEQ.jpg)

利用例
//
flix&gt; Cafe.buyCoffee(CreditCard.CreditCard(&quot;A&quot;))
(Coffee({price = 2.0}), Charge({creditCard = CreditCard(A),
amount = 2.0})) //
戻り値
flix&gt; Cafe.buyCoffees(CreditCard.CreditCard(&quot;A&quot;), 3)
Ok((Coffee({price = 2.0}) :: Coffee({price = 2.0}) ::
Coffee({price = 2.0}) :: Nil, Charge({creditCard =
CreditCard(A), amount = 6.0}))) //
戻り値
データを変換する純粋関数
多様なデータを表す代数的データ型
List, Nel (non-empty list), Result, etc.
関数のパターンを抽象化した型クラス
Traversable, Foldable, Functor, etc.
38


# Page. 39

![Page Image](https://bcdn.docswell.com/page/57GL49PDEL.jpg)

第2章 Flixで始める関数型
プログラミング
39


# Page. 40

![Page Image](https://bcdn.docswell.com/page/4EQYGW2XJP.jpg)

キーワード
再帰関数(recursive function)
末尾再帰関数(tail-recursive function)
末尾呼び出し最適化/除去(tail-call
optimization/elimination, TCO/TCE)
(higher-order function)
(polymorphic function)
(monomorphic function)
(parametric polymorphism)
(type-driven development)
高階関数
多相関数
↔ 単相関数
パラメータ多相
型駆動開発
40


# Page. 41

![Page Image](https://bcdn.docswell.com/page/KJ4W5KL271.jpg)

ループ(繰り返し処理)したい ⇒ 再帰関数
関数型言語のチュートリアルでお馴染みの 階乗関数
末尾再帰であることを保証するアノテーション
末尾再帰関数 ループ相当に最適化される)
基底部
再帰部
// (
)
pub def factorial(n: Int32): Int32 =
@Tailrec //
def go(x, acc) = //
(
if (x &lt;= 0) acc
//
else go(x - 1, x * acc); //
go(n, 1)
利用例
//
flix&gt; List.map(factorial, List.range(0, 6))
1 :: 1 :: 2 :: 6 :: 24 :: 120 :: Nil
41


# Page. 42

![Page Image](https://bcdn.docswell.com/page/LE1YWXLK7G.jpg)

関数の振る舞いを切り替えたい ⇒ 高階関数
pub def formatAbs(x: Int32): String =
&quot;The absolute value of ${x} is ${abs(x)}.&quot;
pub def formatFactorial(n: Int32): String =
&quot;The factorial of ${n} is ${factorial(n)}.&quot;
//
pub def formatResult(name: String, n: Int32,
f: Int32 -&gt; Int32): String =
&quot;The ${name} of ${n} is ${f(n)}.&quot;
整数を受け取って整数を返す関数をパラメータ化して高階関数に
利用例
//
flix&gt; formatResult(&quot;absolute value&quot;, -3, abs)
The absolute value of -3 is 3.
flix&gt; formatResult(&quot;factorial&quot;, 5, factorial)
The factorial of 5 is 120.
42


# Page. 43

![Page Image](https://bcdn.docswell.com/page/GEWG6NVPJ2.jpg)

任意の型で再利用したい ⇒ (パラメータ)多相関数
文字列のベクターをキーで線形探索してインデックスを返す関数
//
pub def findFirst(key: String, ss: Vector[String]): Int32 =
@Tailrec
def loop(n) =
if (n &gt;= Vector.size(ss)) -1
else if (Vector.get(n, ss) == key) n
else loop(n + 1);
loop(0)
任意の要素型のベクターを述語関数で線形探索してインデックスを返す関数
//
pub def findFirst(p: a -&gt; Bool, xs: Vector[a]): Int32 =
@Tailrec
def loop(n) =
if (n &gt;= Vector.size(xs)) -1
else if (xs |&gt; Vector.get(n) |&gt; p) n
else loop(n + 1);
loop(0)
43


# Page. 44

![Page Image](https://bcdn.docswell.com/page/47ZLY346J3.jpg)

第3章 関数型データ構造
44


# Page. 45

![Page Image](https://bcdn.docswell.com/page/YJ6WDX3LJV.jpg)

キーワード
代数的データ型(algebraic data type, ADT)
直積型(product type)
直和型(sum type)
再帰データ型(recursive data type)
パターンマッチング(pattern matching)
網羅性(exhaustiveness)
純粋関数型データ型(purely functional data type),
永続データ型(persistent data type)
構造共有(structural sharing)
45


# Page. 46

![Page Image](https://bcdn.docswell.com/page/GJ5M3XLMJ4.jpg)

不変コレクションがほしい ⇒ 代数的データ型
リスト 単方向連結リスト
//
(
)
pub enum List[a] with Eq, ToString { //
case Nil
//
(
case Cons(a, List[a]) //
(
}
//
(
)
pub enum Tree[a] with Eq, ToString {
case Leaf(a)
//
case Branch(Tree[a], Tree[a]) //
}
ツリー 二分木
Flix
等価判定、文字列表現付き
空の場合 基底部)
空でない場合 再帰部)
葉の場合(基底部)
枝の場合(再帰部)
標準ライブラリ:
List, Nel, Vector, Chain, Nec
Set, Map
RedBlackTree
46


# Page. 47

![Page Image](https://bcdn.docswell.com/page/9E29ZXLR7R.jpg)

データを構造に沿って扱いたい ⇒ パターンマッチング
リストの長さ
//
pub def length(l: List[a]): Int32 =
match l {
case Nil =&gt; 0
case Cons(_, xs) =&gt; 1 + length(xs)
}
//
pub def size(t: Tree[a]): Int32 =
match t {
case Leaf(_) =&gt; 1
case Branch(left, right) =&gt; 1 + size(left) +
size(right)
}
ツリーの要素数
利用例
//
flix&gt; length(Cons(&quot;a&quot;, Cons(&quot;b&quot;, Cons(&quot;c&quot;, Nil))))
3
flix&gt; size(Branch(Branch(Leaf(&quot;a&quot;), Leaf(&quot;b&quot;)), Leaf(&quot;c&quot;)))
5
47


# Page. 48

![Page Image](https://bcdn.docswell.com/page/D7Y4R8Q5EM.jpg)

データの操作パターンを抽象化したい ⇒ 高階関数
右から畳み込む関数 再帰構造に沿った集約関数
⚠️ 愚直な実装では末尾再帰 スタックセーフにならない 工夫が必要
//
(
)
//
/
(
)
pub def foldRight(f: (a, b) -&gt; b \ ef, acc: b, l: List[a]):
b \ ef =
match l {
case Nil =&gt; acc
case Cons(x, xs) =&gt; f(x, foldRight(f, acc, xs))
}
利用例
//
flix&gt; foldRight((-), 0, Cons(1, Cons(2, Cons(3, Nil))))
2
//
(1 - (2 - (3 - 0)))
// = (1 - (2 - 3))
// = (1 - (-1))
// = 2
48


# Page. 49

![Page Image](https://bcdn.docswell.com/page/VENYDQK4J8.jpg)

左から畳み込む関数 末尾再帰の集約関数
末尾再帰
//
(
)
@Tailrec //
pub def foldLeft(f: (b, a) -&gt; b \ ef, acc: b, l: List[a]):
b \ ef =
match l {
case Nil =&gt; acc
case Cons(x, xs) =&gt; foldLeft(f, f(acc, x), xs)
}
利用例
//
flix&gt; foldLeft((-), 0, Cons(1, Cons(2, Cons(3, Nil))))
-6
//
(((0 - 1) - 2) - 3)
// = (((-1) - 2) - 3)
// = ((-3) - 3)
// = -6
ℹ️ 結合法則を満たさない(+ 単位元を持たない)演算
では foldRight と foldLeft の結果が異なる
49


# Page. 50

![Page Image](https://bcdn.docswell.com/page/Y79PGDV4E3.jpg)

リストを返す関数を各要素に適用して得られるリストを平坦化して返す関数
//
pub def flatMap(f: a -&gt; List[b] \ ef, l: List[a]):
List[b] \ ef =
//
append: (List[a], List[a]) -&gt; List[a]
foldRight((x, acc) -&gt; append(f(x), acc), Nil, l)
ℹ️
は定義済みとする
利用例
//
flix&gt; \\
&gt; flatMap(
&gt;
x -&gt; Cons(x, Cons(x + 1, Nil)),
&gt;
Cons(1, Cons(2, Cons(3, Nil)))
&gt; )
&gt; \\
Cons(1, Cons(2, Cons(2, Cons(3, Cons(3, Cons(4, Nil))))))
50


# Page. 51

![Page Image](https://bcdn.docswell.com/page/G78DV3PV7D.jpg)

関数を各要素に適用して得られるリストを返す関数
//
pub def map(f: a -&gt; b \ ef, l: List[a]): List[b] \ ef =
flatMap(x -&gt; Cons(f(x), Nil), l)
利用例
//
flix&gt; \\
&gt; map(
&gt;
x -&gt; x * x * x,
&gt;
Cons(1, Cons(2, Cons(3, Nil)))
&gt; )
&gt; \\
Cons(1, Cons(8, Cons(27, Nil)))
51


# Page. 52

![Page Image](https://bcdn.docswell.com/page/L7LM5V1RJR.jpg)

述語を満たす要素のみのリストを返す関数
//
pub def filter(p: a -&gt; Bool \ ef, l: List[a]): List[a] \ ef =
flatMap(x -&gt; if (p(x)) Cons(x, Nil) else Nil, l)
利用例
//
flix&gt; \\
&gt; filter(
&gt;
//
&gt;
x -&gt; x `Int32.remainder` 2 != 0,
&gt;
Cons(1, Cons(2, Cons(3, Nil)))
&gt; )
&gt; \\
Cons(1, Cons(3, Nil))
ℹ️ バッククォートで括ると中置記法に
他にも多様な高階関数を定義できる
他のデータ構造についても同様に整備できる
52


# Page. 53

![Page Image](https://bcdn.docswell.com/page/4EMY3GWPEW.jpg)

第4章 例外によらないエラー
ハンドリング
53


# Page. 54

![Page Image](https://bcdn.docswell.com/page/PER9QZYYJ9.jpg)

値の有無を区別したい ⇒ Option/Maybe
pub enum Option[a] with Eq, ToString {
case None
//
case Some(a) //
}
値がない場合
値がある場合
例外は参照透過性を損なう副作用なので避ける
代わりに代数的データ型で表す
⇒ コレクションと同様に豊かな操作の対象に
Flix標準ライブラリ: Option
54


# Page. 55

![Page Image](https://bcdn.docswell.com/page/P7XQ59G4EX.jpg)

処理の成否を区別したい ⇒ Either/Result
pub enum Either[e, a] with Eq, ToString {
case Left(e)
//
:
case Right(a) //
:
}
失敗の場合 エラー値を持つ
成功の場合 成功値を持つ
Flix
標準ライブラリ: Result
55


# Page. 56

![Page Image](https://bcdn.docswell.com/page/37K96P4P7D.jpg)

データの操作パターンを抽象化したい ⇒ 高階関数
pub def flatMap(f: a -&gt; Option[b] \ ef, o: Option[a]):
Option[b] \ ef =
match o {
case None =&gt; None
case Some(x) =&gt; f(x)
}
pub def flatMap(f: a -&gt; Either[e, b] \ ef, et: Either[e, a]):
Either[e, b] \ ef =
match et {
case Left(x) =&gt; Left(x)
case Right(y) =&gt; f(y)
}
56


# Page. 57

![Page Image](https://bcdn.docswell.com/page/LJ3W6XL4J5.jpg)

pub def map(f: a -&gt; b \ ef, o: Option[a]):
Option[b] \ ef = ???
pub def filter(p: a -&gt; Bool \ ef, o: Option[a]):
Option[a] \ ef = ???
利用例
//
flix&gt; map(x -&gt; x / 2, Some(42))
Some(21)
flix&gt; map(x -&gt; x / 2, None)
None
flix&gt; filter(x -&gt; x `Int32.remainder` 2 != 0, Some(42))
None
flix&gt; filter(x -&gt; x `Int32.remainder` 2 != 0, None)
None
57


# Page. 58

![Page Image](https://bcdn.docswell.com/page/8JDKV2N5EG.jpg)

pub def map(f: a -&gt; b \ ef, et: Either[e, a]):
Either[e, b] \ ef = ???
//
//
⚠️ Eitherのfilterは無条件には定義できない
(述語を満たさない場合に採用するエラー値が定まらないため)
利用例
//
flix&gt; map(x -&gt; x / 2, (Right(42): Either[String, Int32]))
Right(21)
flix&gt; map(x -&gt; x / 2, Left(&quot;falsity&quot;))
Left(falsity)
58


# Page. 59

![Page Image](https://bcdn.docswell.com/page/VEPKV15D78.jpg)

引数関数を 個の
値に適用して
を返す関数
// 2
2
Option
Option
pub def map2(f: (a, b) -&gt; c \ ef, o1: Option[a],
o2: Option[b]): Option[c] \ ef =
flatMap(x -&gt; map(y -&gt; f(x, y), o2), o1)
利用例
//
flix&gt; map2((+), Some(1), Some(2))
Some(3)
flix&gt; map2((+), None, Some(2))
None
flix&gt; map2((+), Some(1), None)
None
flix&gt; map2(((+): (Int32, Int32) -&gt; Int32), None, None)
None
Either
についても同様に定義できる
59


# Page. 60

![Page Image](https://bcdn.docswell.com/page/27VVZYPG7Q.jpg)

を返す関数をリストの各要素に適用してリストの
を返す関数
// Option
Option
pub def traverse(f: a -&gt; Option[b] \ ef, l: List[a]):
Option[List[b]] \ ef =
List.foldRight(
(x, acc) -&gt; map2(List.cons, f(x), acc),
Some(Nil),
l
)
// Option
Option
pub def sequence(l: List[Option[a]]): Option[List[a]] =
traverse(identity, l)
のリストをリストの
にする関数
利用例 ℹ️ 標準ライブラリの
を利用
//
:
List
flix&gt; traverse(x -&gt; if (x `Int32.remainder` 2 != 0) Some(x)
else None, List#{1, 2, 3})
None
flix&gt; traverse(x -&gt; if (x `Int32.remainder` 2 != 0) Some(x)
else None, List#{1, 3, 5})
Some(1 :: 3 :: 5 :: Nil)
Either
についても同様に定義できる
60


# Page. 61

![Page Image](https://bcdn.docswell.com/page/5JGL49QD7L.jpg)

エラー値を蓄積したい ⇒ Validated/Validation
pub enum Validated[e, a] with Eq, ToString {
case Valid(a)
//
:
case Invalid(Nel[e]) //
:
}
有効な場合 有効値を持つ
無効な場合 エラー値の非空リストを持つ
標準ライブラリ: Validation
ℹ️ 性能特性から Nel (non-empty list)ではなく Nec
(non-empty chain)が採用されている
Flix
61


# Page. 62

![Page Image](https://bcdn.docswell.com/page/47QYGWKXEP.jpg)

第5章 正格性と遅延性
62


# Page. 63

![Page Image](https://bcdn.docswell.com/page/KE4W5KP2J1.jpg)

キーワード
非正格性(nonstrictness)、非正格評価(nonstrict
evaluation)
↔ 正格性(strictness)、正格評価(strict
evaluation)/先行評価(eager evaluation)
遅延性(laziness)、遅延評価(lazy evaluation)
計算の記述(description)と評価(evaluation)の分離
余再帰関数(corecursive function)
63


# Page. 64

![Page Image](https://bcdn.docswell.com/page/L71YWX9KJG.jpg)

必要になるまで実体化されないデータ構造がほしい
⇒ 代数的データ型 + 評価を制御する工夫
言語によっては
と呼ばれる
は遅延評価される式を表す の組み込み型
pub enum LazyList[a] { //
Stream
case Empty
//
Lazy
Flix
case Cons(Lazy[a], Lazy[LazyList[a]])
}
ℹ️
pub def take(n: Int32, ll: LazyList[a]): LazyList[a] =
match ll {
case Cons(head, tail) if n &gt; 1 =&gt;
//
lazy
//
force
Cons(head, lazy take(n - 1, force tail))
case Cons(head, _) if n == 1 =&gt;
Cons(head, lazy Empty)
case _ =&gt; Empty
}
ℹ️
Flix
は直後の式を遅延評価されるようにし、
は直後の遅延評価式を実体化する
標準ライブラリ: DelayList, DelayMap
64


# Page. 65

![Page Image](https://bcdn.docswell.com/page/G7WG6NQPE2.jpg)

からの整数列
// n
pub def intsFrom(n: Int32): LazyList[Int32] =
Cons(lazy n, lazy intsFrom(n + 1))
//
pub def fibs(): LazyList[BigInt] =
def go(current, next) =
Cons(lazy current, lazy go(next, current + next));
go(0ii, 1ii)
フィボナッチ数列
利用例
//
flix&gt; LazyList.intsFrom(0) |&gt; LazyList.map(x -&gt; x * x)
|&gt; LazyList.take(5) |&gt; LazyList.toList
0 :: 1 :: 4 :: 9 :: 16 :: Nil
flix&gt; LazyList.fibs() |&gt; LazyList.take(10) |&gt; LazyList.toList
0 :: 1 :: 1 :: 2 :: 3 :: 5 :: 8 :: 13 :: 21 :: 34 :: Nil
65


# Page. 66

![Page Image](https://bcdn.docswell.com/page/4JZLY3W6E3.jpg)

第6章 純粋関数型の状態
66


# Page. 67

![Page Image](https://bcdn.docswell.com/page/YE6WDXGLEV.jpg)

擬似乱数を生成したい ⇒ 状態を受け渡す純粋関数
シード値を持つ
を受け取って整数と新たな を返す関数
乱数生成器 型
//
Rng (random number generator,
)
pub enum Rng(Int64) with Eq, ToString
// Rng
Rng
pub def nextInt(rng: Rng): (Int32, Rng) =
let Rng(seed) = rng;
//
let newSeed = Int64.bitwiseAnd(
seed * 25214903917i64 + 11i64, 281474976710655i64);
let nextRng = Rng(newSeed);
let n = Int64.rightShift(newSeed, 16)
|&gt; Int64.truncateToInt32;
(n, nextRng)
// Rng
Rng
pub type alias Rand[a] = Rng -&gt; (a, Rng)
線形合同法による乱数生成
を受け取って任意のランダム生成値と新たな を返す関数の型エイリアス
Flix
標準ライブラリ: Math.Random (エフェクト)
67


# Page. 68

![Page Image](https://bcdn.docswell.com/page/GE5M3XVME4.jpg)

自然に定義できるユーティリティ関数の例
//
pub def unit(a: a): Rand[a] = ???
pub def map(f: a -&gt; b, ra: Rand[a]): Rand[b] = ???
pub def map2(f: (a, b) -&gt; c, ra: Rand[a], rb: Rand[b]):
Rand[c] = ???
pub def flatMap(f: a -&gt; Rand[b], ra: Rand[a]): Rand[b] = ???
//
pub def int(): Rand[Int32] = rng -&gt; nextInt(rng)
pub def boolean(): Rand[Bool] =
rng -&gt;
let (i, rng2) = nextInt(rng);
(i `Int32.remainder` 2 == 0, rng2)
ランダム関数の例
利用例
//
flix&gt; map2((i, b) -&gt; &quot;int: ${i}, boolean: ${b}&quot;, int(),
boolean())(Rng(42i64))
(int: 16159453, boolean: false, Rng(197491923327988))
flix&gt; map2((i, b) -&gt; &quot;int: ${i}, boolean: ${b}&quot;, int(),
boolean())(Rng(197491923327988i64))
(int: -340305902, boolean: true, Rng(149370390209998))
68


# Page. 69

![Page Image](https://bcdn.docswell.com/page/9729ZX3RJR.jpg)

第2部 関数型設計とコンビネータ
ライブラリ
7.
8.
9.
純粋関数型の並列処理
プロパティベーステスト
パーサーコンビネータ
69


# Page. 70

![Page Image](https://bcdn.docswell.com/page/DJY4R8157M.jpg)

第7章 純粋関数型の並列処理
70


# Page. 71

![Page Image](https://bcdn.docswell.com/page/V7NYDQ24E8.jpg)

並列/並行処理したい ⇒ 処理の記述と実行の分離
マルチスレッド実行管理オブジェクトを受け取り非同期処理の結果を返す関数
//
enum Par[a](ExecutorService -&gt; Future[a] \ IO)
pub def unit(a: a): Par[a] =
Par(_es -&gt; /* ExecutorService
a
pub def fork(pa: Lazy[Par[a]]): Par[a] =
Par(es -&gt; /* ExecutorService
pub def runPar(es: ExecutorService, pa: Par[a]):
Future[a] \ IO =
let Par(f) = pa;
f(es)
を利用せず直ちに を返す */)
を利用して非同期的にタスク実行 */)
標準機能:
par-yield 構文による並列処理
(Go言語に似た)軽量スレッドと Channel による
CSPベースの並行処理
Flix
71


# Page. 72

![Page Image](https://bcdn.docswell.com/page/YJ9PGD1473.jpg)

自然に定義できるユーティリティ関数の例
//
pub def flatMap(f: a -&gt; Par[b], pa: Par[a]): Par[b] = ???
pub def map2(f: (a, b) -&gt; c, pa: Par[a], pb: Par[b]):
Par[c] = ???
pub def map(f: a -&gt; b, pa: Par[a]): Par[b] = ???
//
pub def runWithThreadPool(pa: Par[a]): a \ IO =
let es = Executors.newFixedThreadPool(4);
let v = runPar(es, pa).get();
es.shutdown();
v
並列処理を実行する関数の例
利用例
//
flix&gt; map2((a, b) -&gt; a * b, unit(2), unit(3))
|&gt; runWithThreadPool
6
flix&gt; map2((a, b) -&gt; a ++ b, unit(List#{1, 2}),
unit(List#{3, 4, 5})) |&gt; runWithThreadPool
1 :: 2 :: 3 :: 4 :: 5 :: Nil
72


# Page. 73

![Page Image](https://bcdn.docswell.com/page/GJ8DV34VJD.jpg)

第8章 プロパティベーステスト
73


# Page. 74

![Page Image](https://bcdn.docswell.com/page/LJLM5V6RER.jpg)

プロパティベーステスト(PBT)ライブラリがほしい
⇒ 状態を受け渡す純粋関数 + 処理の記述と実行の分離
のジェネレータ
を受け取って と新たな を返す関数
// PBT
: Rng
a
Rng
enum Gen[a](Rng -&gt; (a, Rng))
pub def map(f: a -&gt; b, ga: Gen[a]): Gen[b] = ???
pub def map2(f: (a, b) -&gt; c, ga: Gen[a], gb: Gen[b]):
Gen[c] = ???
pub def flatMap(f: a -&gt; Gen[b], ga: Gen[a]): Gen[b] = ???
//
(sized)
mod SGen {
pub enum SGen[a](Int32 -&gt; Gen[a])
pub def map(f: a -&gt; b, sga: SGen[a]): SGen[b] = ???
pub def flatMap(f: a -&gt; SGen[b], sga: SGen[a]):
SGen[b] = ???
}
サイズ指定付き
のジェネレータ
具体的なジェネレータの例
//
pub def boolean(): Gen[Bool] = ???
pub def int(): Gen[Int32] = ???
pub def list(ga: Gen[a]): SGen[List[a]] = ???
74


# Page. 75

![Page Image](https://bcdn.docswell.com/page/47MY3G5P7W.jpg)

のプロパティ テスト対象の性質
// PBT
(
)
enum Prop((MaxSize, TestCases, Rng) -&gt; Result.Result)
pub def forAll(ga: Gen[a], f: a -&gt; Bool):
Prop with ToString[a] = ???
pub def forAllSized(sga: Gen.SGen[a], f: a -&gt; Bool):
Prop with ToString[a] = ???
pub def &amp;&amp;(p1: Prop, p2: Prop): Prop = ???
pub def ||(p1: Prop, p2: Prop): Prop = ???
pub def runProp(p: Prop): Unit \ {IO, Time.Clock} = ???
//
利用例
回反転したリストは元のリストに一致する
// 2
flix&gt; forAllSized(list(int()), xs -&gt;
xs |&gt; List.reverse |&gt; List.reverse == xs) \
//
&gt;
&amp;&amp; forAllSized(list(int()), xs -&gt;
List.head(xs) == xs |&gt; List.reverse |&gt; List.last) \
&gt;
|&gt; runProp
+ OK, passed 100 tests.
()
リストの先頭要素は反転したリストの最終要素に一致する
75


# Page. 76

![Page Image](https://bcdn.docswell.com/page/P7R9QZ3YE9.jpg)

第9章 パーサーコンビネータ
76


# Page. 77

![Page Image](https://bcdn.docswell.com/page/PJXQ59447X.jpg)

パーサー(構文解析器)がほしい ⇒ 純粋関数の合成
パーサーを表すトレイト
型クラス
//
(a.k.a.
)
pub trait Parsers[parser: Type -&gt; Type] {
pub def string(s: String): parser[String]
pub def char(c: Char): parser[Char]
pub def regex(r: Regex): parser[String]
pub def runParser(input: String, p: parser[a]):
Result[ParseError, a]
pub def map(f: a -&gt; b, p: parser[a]): parser[b]
pub def map2(f: (a, b) -&gt; c, p1: parser[a],
p2: Lazy[parser[b]]): parser[c]
pub def flatMap(f: a -&gt; parser[b], p: parser[a]):
parser[b]
...
}
トレイトの実装例
//
pub enum Parser[a](Location -&gt; ParseResult[a])
instance Parsers[Parser] {
pub def string(s: String): Parser[String] = ???
...
}
77


# Page. 78

![Page Image](https://bcdn.docswell.com/page/3JK96PZPJD.jpg)

パーサーの例 非負整数の直後にその数だけ が繰り返す文字列
//
:
a
pub def nConsecutiveAs(): parser[Int32]
with Parsers[parser] =
Parsers.flatMap(n -&gt;
Parsers.map(_ -&gt;
n, // n
+
a
Parsers.listOfN(n, Parsers.char(&#039;a&#039;))
), //
nonNegativeInt()
)
回の繰り返し 文字 のパーサー
非負整数のパーサー
利用例
//
flix&gt; (nConsecutiveAs(): Parser[Int32])
|&gt; Parsers.runParser(&quot;12aaaaaaaaaaaab&quot;)
Ok(12)
flix&gt; (nConsecutiveAs(): Parser[Int32])
|&gt; Parsers.runParser(&quot;12aaabaaaaaaaab&quot;)
Err(1.6 &#039;a&#039; // 1 6
a
行 文字目で文字 のパースエラー
12aaabaaaaaaaa
^)
78


# Page. 79

![Page Image](https://bcdn.docswell.com/page/LE3W6XD4E5.jpg)

第3部 関数型設計に共通する構造
10.
11.
12.
モノイド
モナド
アプリカティブ/トラバーサブルファンクター
79


# Page. 80

![Page Image](https://bcdn.docswell.com/page/8EDKV2P57G.jpg)

第10章 モノイド
80


# Page. 81

![Page Image](https://bcdn.docswell.com/page/V7PKV1NDJ8.jpg)

キーワード
代数的構造(algebraic structure)
モノイド(monoid)
結合法則/結合律(associativity, associative law)
単位元(identity element)、単位律(identity law)
→ モノイド則(monoid laws)
型クラス(type class)
アドホック多相(ad-hoc polymorphism)
Foldable 型クラス
高カインド型(higher-kinded type, HKT)
81


# Page. 82

![Page Image](https://bcdn.docswell.com/page/2JVVZYKGJQ.jpg)

型クラスによるアドホック多相
(型によって異なる振る舞いを与える多相)
型クラスの定義 共通のインターフェース
//
:
pub trait HasSize[a] {
pub def size(x: a): Int32
}
型クラスのインスタンス 型ごとの実装
//
:
instance HasSize[String] {
pub def size(x: String): Int32 = String.length(x)
}
instance HasSize[List[a]] {
pub def size(x: List[a]): Int32 = List.size(x)
}
第 章で定義した
二分木)
instance HasSize[Tree[a]] { //
3
Tree (
pub def size(x: Tree[a]): Int32 = Tree.size(x)
}
82


# Page. 83

![Page Image](https://bcdn.docswell.com/page/5EGL499DJL.jpg)

利用例
//
flix&gt; HasSize.size(&quot;foo bar&quot;)
7
flix&gt; HasSize.size(List#{&#039;a&#039;, &#039;b&#039;, &#039;c&#039;})
3
flix&gt; HasSize.size(Branch(Leaf(1), Branch(Leaf(2), Leaf(3))))
5
パラメータ多相
型にかかわらず同じ振る舞いを可能とする多相)
Eq や ToString はFlix標準の型クラスの例
cf.
(
83


# Page. 84

![Page Image](https://bcdn.docswell.com/page/4JQYGWWX7P.jpg)

型クラス Monoid (モノイド):
結合法則を満たす + 単位元が存在する
定義
//
pub trait Monoid[a] {
//
2
a × a
pub def combine(a1: a, a2: a): a //
mappend
//
e
pub def empty(): a
//
mempty
}
//
(
)
pub def combineAll(xs: List[a]): a with Monoid[a] =
List.foldLeft(Monoid.combine, Monoid.empty(), xs)
pub def foldMap(f: a -&gt; b, xs: List[a]): b with Monoid[b] =
List.foldLeft((b, a) -&gt; Monoid.combine(b, f(a)),
Monoid.empty(), xs)
結合法則を満たす 項演算
単位元
派生的 定義から導出可能 な関数
Flix
別名
別名
標準ライブラリ: Monoid
84


# Page. 85

![Page Image](https://bcdn.docswell.com/page/K74W5KK2E1.jpg)

Monoid
の具体例
文字列 リストの連結と空文字列 リスト
//
/
/
flix&gt; Monoid.combineAll(List#{&quot;alpha&quot;, &quot;beta&quot;, &quot;gamma&quot;})
alphabetagamma
flix&gt; Monoid.combineAll(List#{List#{2}, List#{3}, List#{5}})
2 :: 3 :: 5 :: Nil
数の和と ※ここでは、
をラップした型
数の積と ※ここでは、
をラップした型
//
0
Int32
enum SumInt(Int32)
flix&gt; Monoid.foldMap(SumInt, List#{2, 3, 4})
SumInt(9)
//
1
Int32
enum ProductInt(Int32)
flix&gt; Monoid.foldMap(ProductInt, List#{2, 3, 4})
ProductInt(24)
論理和と
ここでは、
をラップした型
論理積と
ここでは、
をラップした型
//
false ※
Bool
enum Or(Bool)
flix&gt; Monoid.foldMap(Or, List#{true, false, true})
Or(true)
//
true ※
Bool
enum And(Bool)
flix&gt; Monoid.foldMap(And, List#{true, false, true})
And(false)
85


# Page. 86

![Page Image](https://bcdn.docswell.com/page/LJ1YWXXKEG.jpg)

型クラス Foldable: fold (畳み込み)できる
は高カインド型
pub trait Foldable[f: Type -&gt; Type] { // f
//
1
pub def foldRight(f: (a, b) -&gt; b, acc: b, fa: f[a]): b
pub def foldLeft(f: (b, a) -&gt; b, acc: b, fa: f[a]): b
pub def foldMap(f: a -&gt; b, fa: f[a]): b with Monoid[b]
//
(
)
pub def combineAll(fa: f[a]): a with Monoid[a]
pub def toList(fa: f[a]): List[a]
}
定義 ※いずれか つの関数から他は導出可能
派生的 定義から導出可能 な関数
利用例
//
flix&gt; foldMap(constant(SumInt(1)), List#{&#039;a&#039;, &#039;b&#039;, &#039;c&#039;})
SumInt(3)
flix&gt; foldLeft((_, x) -&gt; &quot;value: ${x}&quot;, &quot;nothing&quot;, Some(42))
value: 42
Flix
標準ライブラリ: Foldable
86


# Page. 87

![Page Image](https://bcdn.docswell.com/page/GJWG6NNP72.jpg)

第11章 モナド
87


# Page. 88

![Page Image](https://bcdn.docswell.com/page/4EZLY33673.jpg)

キーワード
ファンクター/関手(functor)
恒等(identity)
合成(composition)
→ ファンクター則(functor laws)
モナド(monad): ファンクターの特殊化
結合法則/結合律(associativity, associative law)
左単位律(left identity law)
右単位律(right identity law)
→ モナド則(monad laws)
88


# Page. 89

![Page Image](https://bcdn.docswell.com/page/Y76WDXXL7V.jpg)

型クラス Functor: map (文脈を保って写像)できる
定義
//
pub trait Functor[f: Type -&gt; Type] {
pub def map(f: a -&gt; b, fa: f[a]): f[b]
}
//
別名fmap
インスタンスの例 これまでに を実装したすべての型を含む
//
:
map
instance Functor[List] {
pub def map(f: a -&gt; b, fa: List[a]): List[b] =
List.map(f, fa)
}
利用例
//
flix&gt; Functor.map(x -&gt; x * x * x, List#{1, 2, 3})
1 :: 8 :: 27 :: Nil
Flix
標準ライブラリ: Functor
89


# Page. 90

![Page Image](https://bcdn.docswell.com/page/G75M3XXM74.jpg)

型クラス Monad: flatMap (文脈を引き継いで連鎖)
できる
pub trait Monad[f: Type -&gt; Type] with Functor[f] {
//
flatMap compose
map join
pub def unit(a: a): f[a] //
return
pub def flatMap(f: a -&gt; f[b], fa: f[a]): f[b] //
&gt;&gt;=
//
(
)
pub def compose(f: a -&gt; f[b], g: b -&gt; f[c]): a -&gt; f[c]
pub def join(ffa: f[f[a]]): f[a]
pub def map2(f: (a, b) -&gt; c, fa: f[a], fb: f[b]): f[c]
pub def sequence(fas: List[f[a]]): f[List[a]]
pub def traverse(f: a -&gt; f[b], xs: List[a]): f[List[b]]
pub def replicateM(n: Int32, fa: f[a]): f[List[a]]
}
定義 ※
は
もしくは と
別名
派生的 定義から導出可能 な関数
Flix
からも導出可能
別名
標準ライブラリ: Monad
90


# Page. 91

![Page Image](https://bcdn.docswell.com/page/9J29ZXXRER.jpg)

Monad
の具体例
インスタンスの例 これまでに
を実装したすべての型を含む
状態を関数で引き回す型 第 章の乱数生成器の一般化)
//
:
flatMap
pub mod State { //
( 6
pub enum State[s, a](s -&gt; (a, s))
pub def map(f: a -&gt; b, sa: State[s, a]):
State[s, b] = ???
pub def unit(a: a): State[s, a] = ???
pub def flatMap(f: a -&gt; State[s, b], sa: State[s, a]):
State[s, b] = ???
pub def get(): State[s, s] = ???
pub def set(s: s): State[s, Unit] = ???
pub def modify(f: s -&gt; s): State[s, Unit] = ???
pub def runState(s0: s, sa: State[s, a]): (a, s) = ???
}
の実装
instance Functor[State[s]] { /* map
*/ }
instance Monad[State[s]] {
pub def unit(a: a): State[s, a] = State.unit(a)
pub redef flatMap(f: a -&gt; State[s, b], fa: State[s, a]):
State[s, b] = State.flatMap(f, fa)
}
91


# Page. 92

![Page Image](https://bcdn.docswell.com/page/DEY4R885JM.jpg)

モナドを活用した関数の例
// State
pub def zipWithIndex(xs: List[a]): List[(Int32, a)] =
List.foldLeft((acc, x) -&gt;
Monad.flatMap(ys -&gt;
Monad.flatMap(n -&gt;
Functor.map(_ -&gt;
(n, x) :: ys,
State.set(n + 1) //
), State.get()
//
), acc
), Monad.unit(Nil), xs
) // =&gt; State[Int32, List[(Int32, a)]]
|&gt; State.runState(0) // =&gt; (List[(Int32, a)], Int32)
|&gt; fst
|&gt; List.reverse
状態を更新
状態を取得
利用例
//
flix&gt; zipWithIndex(List#{&#039;a&#039;, &#039;b&#039;, &#039;c&#039;})
(0, a) :: (1, b) :: (2, c) :: Nil
92


# Page. 93

![Page Image](https://bcdn.docswell.com/page/VJNYDQQ478.jpg)

標準の Monad に対する糖衣構文 forM
Flix
flix&gt; \\
&gt; Monad.flatMap(x -&gt;
&gt;
Functor.map(y -&gt;
&gt;
x * y,
&gt;
Some(3)
&gt;
),
&gt;
Some(2)
&gt; )
&gt; \\
Some(6)
flix&gt; \\
&gt; forM(
&gt;
x &lt;- Some(2);
&gt;
y &lt;- Some(3)
&gt; ) yield x * y
&gt; \\ // =&gt; Option[Int32]
Some(6)
の 式、Haskellのdo記法
cf. Scala for
93


# Page. 94

![Page Image](https://bcdn.docswell.com/page/YE9PGDD4J3.jpg)

が
標準の
のインスタンスなら、以下のように書ける
// State Flix
Monad
pub def zipWithIndex(xs: List[a]): List[(Int32, a)] =
List.foldLeft((acc, x) -&gt;
forM(
ys &lt;- acc;
n &lt;- State.get();
_ &lt;- State.set(n + 1)
) yield (n, x) :: ys,
Applicative.point(Nil), xs
)
|&gt; State.runState(0)
|&gt; fst
|&gt; List.reverse
94


# Page. 95

![Page Image](https://bcdn.docswell.com/page/GE8DV33VED.jpg)

第12章 アプリカティブ/
トラバーサブルファンクター
95


# Page. 96

![Page Image](https://bcdn.docswell.com/page/LELM5VVR7R.jpg)

キーワード
アプリカティブファンクター(applicative functor):
ファンクターの特殊化、モナドの一般化
左単位律(left identity law)
右単位律(right identity law)
結合法則/結合律(associativity, associative law)
自然(naturality)
→ アプリカティブ則(applicative laws)
半群(semigroup): モノイドの一般化
トラバーサブルファンクター(traversable functor):
ファンクターとFoldableの特殊化
モナドトランスフォーマー/モナド変換子(monad
transformer)
96


# Page. 97

![Page Image](https://bcdn.docswell.com/page/4JMY3GGPJW.jpg)

型クラス Applicative: apply (多引数関数を適用)
できる
pub trait Applicative[f: Type -&gt; Type] with Functor[f] {
//
apply map2
pub def unit(a: a): f[a] //
point, pure
pub def apply(fab: f[a -&gt; b], fa: f[a]): f[b] //
ap
//
(
)
pub def map2(f: (a, b) -&gt; c, fa: f[a], fb: f[b]): f[c]
pub def sequence(fas: List[f[a]]): f[List[a]]
pub def traverse(f: a -&gt; f[b], xs: List[a]): f[List[b]]
pub def replicateM(n: Int32, fa: f[a]): f[List[a]]
pub def product(fa: f[a], fb: f[b]): f[(a, b)]
}
定義 ※
は
からも導出可能
別名
派生的 定義から導出可能 な関数
別名
まで特殊化せず様々な関数が定義できる
Flix標準ライブラリ: Applicative
Monad
97


# Page. 98

![Page Image](https://bcdn.docswell.com/page/PJR9QZZY79.jpg)

Applicative
の特殊化として再構成した Monad
pub trait Monad[f: Type -&gt; Type] with Applicative[f] {
//
flatMap compose
map join
pub def flatMap(f: a -&gt; f[b], fa: f[a]): f[b]
//
(
)
pub def compose(f: a -&gt; f[b], g: b -&gt; f[c]): a -&gt; f[c]
pub def join(ffa: f[f[a]]): f[a]
}
定義 ※
は
もしくは と
派生的 定義から導出可能 な関数
からも導出可能
特殊化の流れ:
Functor → Applicative → Monad
98


# Page. 99

![Page Image](https://bcdn.docswell.com/page/PEXQ5994JX.jpg)

Applicative
インスタンスの例 これまでに
の具体例
を実装したすべての型を含む
の実装
無限に続く の遅延リスト
//
:
map2
pub enum ZipList[a](DelayList[a]) with Eq, ToString
instance Functor[ZipList] { /* map
*/ }
instance Applicative[ZipList] {
pub def unit(a: a): ZipList[a] = //
a
DelayList.repeat(a) |&gt; fromDelayList
pub redef map2(f: (a, b) -&gt; c, fa: ZipList[a],
fb: ZipList[b]): ZipList[c] =
DelayList.zip(toDelayList(fa), toDelayList(fb))
|&gt; DelayList.map(t -&gt; let (a, b) = t; f(a, b))
|&gt; fromDelayList
}
利用例
//
flix&gt; Applicative.map2((a, b) -&gt; (a, b),
&gt;
fromDelayList(DelayList.startFrom(0)),
&gt;
Applicative.unit(&#039;α&#039;)
&gt;
) |&gt; toDelayList |&gt; DelayList.take(5)
DelayList((0, α), (1, α), (2, α), (3, α), (4, α))
99


# Page. 100

![Page Image](https://bcdn.docswell.com/page/3EK96PPPED.jpg)

標準の Applicative に対する糖衣構文 forA
Flix
flix&gt; \\
&gt; Applicative.map3(
&gt;
(x, y, z) -&gt; List#{x, y, z},
&gt;
Validation.Failure(Nec.singleton(&quot;x not found&quot;)),
&gt;
Validation.Success(3),
&gt;
Validation.Failure(Nec.singleton(&quot;z not found&quot;))
&gt; )
&gt; \\ // =&gt; Validation[String, List[Int32]]
Failure(Nec#{x not found, z not found})
flix&gt; \\
&gt; forA(
&gt;
x &lt;- Validation.Failure(Nec.singleton(&quot;x not found&quot;));
&gt;
y &lt;- Validation.Success(3);
&gt;
z &lt;- Validation.Failure(Nec.singleton(&quot;z not found&quot;))
&gt; ) yield List#{x, y, z}
&gt; \\
Failure(Nec#{x not found, z not found})
の
拡張ApplicativeDo
cf. Haskell GHC
100


# Page. 101

![Page Image](https://bcdn.docswell.com/page/L73W6XX475.jpg)

型クラス Semigroup: 結合法則を満たす
pub trait Semigroup[a] {
pub def combine(a1: a, a2: a): a
}
// Semigroup
Monoid
pub trait Monoid[a] with Semigroup[a] {
pub def empty(): a
}
の特殊化として再構成した
だが
ではない例 非空リスト
// Semigroup
Monoid
:
pub enum NonEmptyList[a](a, List[a]) with Eq, ToString
instance Semigroup[NonEmptyList[a]] {
pub def combine(a1: NonEmptyList[a],
a2: NonEmptyList[a]): NonEmptyList[a] =
let NonEmptyList(head1, tail1) = a1;
let NonEmptyList(head2, tail2) = a2;
NonEmptyList(head1, tail1 ++ (head2 :: tail2))
}
Flix
標準ライブラリ: SemiGroup
101


# Page. 102

![Page Image](https://bcdn.docswell.com/page/87DKV225JG.jpg)

型クラス Traversable: traverse (走査)できる
ℹ️ 書籍の
コードでは事情により
と命名されている
//
Scala
Traverse
pub trait Traversable[f: Type -&gt; Type]
with Functor[f], Foldable[f] {
//
traverse map sequence
pub def traverse(f: a -&gt; g[b], fa: f[a]): g[f[b]]
with Applicative[g]
//
(
)
pub def sequence(fga: f[g[a]]): g[f[a]]
with Applicative[g]
pub def mapAccum(f: (a, s) -&gt; (b, s), s: s, fa: f[a]):
(f[b], s)
}
定義 ※
は と
からも導出可能
派生的 定義から導出可能 な関数
Flix
標準ライブラリ: Traversable
102


# Page. 103

![Page Image](https://bcdn.docswell.com/page/VJPKV11DE8.jpg)

の実装例 文脈を保って写像しつつ
モナドで状態を読み書き
// mapAccum
:
State
pub def mapAccum(f: (a, s) -&gt; (b, s), s: s, fa: f[a]):
(f[b], s) =
traverse(a -&gt;
Monad.flatMap(s1 -&gt;
let (b, s2) = f(a, s1);
Functor.map(_ -&gt;
b,
State.set(s2)
), State.get()
), fa
) |&gt; State.runState(s)
で実装できる関数の例
// mapAccum
pub def zipWithIndex(fa: f[a]): f[(a, Int32)]
with Traversable[f] =
fa
|&gt; Traversable.mapAccum((a, s) -&gt; ((a, s), s + 1), 0)
|&gt; fst
103


# Page. 104

![Page Image](https://bcdn.docswell.com/page/2EVVZYYGEQ.jpg)

の具体例
Traversable
インスタンスの例 これまでに
を実装したすべての型を含む
//
:
traverse
instance Traversable[List] { ... }
instance Traversable[Option] { ... }
instance Traversable[Map[k]] with Order[k] { ... }
利用例
リストの要素がインデックス付きに
//
//
flix&gt; zipWithIndex(List#{&#039;a&#039;, &#039;b&#039;, &#039;c&#039;})
(a, 0) :: (b, 1) :: (c, 2) :: Nil
の
の値がインデックス付きに
// Option Some
flix&gt; zipWithIndex(Some(42))
Some((42, 0))
マップのエントリーの値がインデックス付きに
//
flix&gt; zipWithIndex(Map#{&#039;α&#039; =&gt; &#039;a&#039;, &#039;β&#039; =&gt; &#039;b&#039;, &#039;γ&#039; =&gt; &#039;g&#039;})
Map#{α =&gt; (a, 0), β =&gt; (b, 1), γ =&gt; (g, 2)}
104


# Page. 105

![Page Image](https://bcdn.docswell.com/page/57GL49WDEL.jpg)

13.
14.
15.
第4部 エフェクトとI/O
外部エフェクトとI/O
局所エフェクトと可変状態
ストリーム処理とインクリメンタルI/O
105


# Page. 106

![Page Image](https://bcdn.docswell.com/page/4EQYGW3XJP.jpg)

第13章 外部エフェクトとI/O
106


# Page. 107

![Page Image](https://bcdn.docswell.com/page/KJ4W5K1271.jpg)

キーワード
エフェクト/作用(effect)
副作用(side effect)
埋め込み/内部DSL (embedded/internal DSL)
IO モナド
Free モナド
cf. 代数的エフェクト(algebraic effect)
トランポリン(trampolining)
tagless final
107


# Page. 108

![Page Image](https://bcdn.docswell.com/page/LE1YWXGK7G.jpg)

副作用が発生しうるコードの表現と解釈を分離したい
⇒ Free モナドによるDSL
ℹ️ 書籍の
コードとは異なり
標準ライブラリの定義を採用した
//
Scala
Haskell
mod Free {
pub enum Free[f: Type -&gt; Type, a: Type] {
case Pure(a)
//
:
case Free(f[Free[f, a]]) //
:
}
// f Functor
Free[f] Monad
instance Functor[Free[f]] with Functor[f] { ... }
instance Applicative[Free[f]] with Functor[f] { ... }
instance Monad[Free[f]] with Functor[f] { ... }
// f[a] Free[f, a]
pub def liftF(fa: f[a]): Free[f, a] with Functor[f] =
Free(Functor.map(a -&gt; Pure(a), fa))
}
が
なら
を
は
基底部 計算の結果
再帰部 計算の継続
になる
に持ち上げるユーティリティ
ℹ️ 関数型のDSL実装手法は Free によるもの以外に
もいろいろある
108


# Page. 109

![Page Image](https://bcdn.docswell.com/page/GEWG6NKPJ2.jpg)

Free
モナドによるDSLの例: コンソール入出力
pub enum Console[next] {
case ReadLine(Option[String] -&gt; next) //
case PrintLine(String, next)
//
}
// Console Functor
Free[Console]
Monad
instance Functor[Console] {
pub def map(f: a -&gt; b \ ef, fa: Console[a]):
Console[b] \ ef =
match fa {
case ReadLine(g) =&gt; ReadLine(s -&gt;
//
unchecked_cast(f(g(s)) as _ \ {}))
case PrintLine(s, next) =&gt; PrintLine(s, f(next))
}
}
を
とすれば
文字列の入力
文字列の出力
は自動的に
に
⚠️ エフェクトを含む型と噛み合わずキャストが必要
109


# Page. 110

![Page Image](https://bcdn.docswell.com/page/47ZLY3Z6J3.jpg)

コンソール入出力DSLの具体例
の構成要素
// DSL
pub def readLn(): Free[Console, Option[String]] =
Free.liftF(ReadLine(identity))
pub def printLn(line: String): Free[Console, Unit] =
Free.liftF(PrintLine(line, ()))
でのコード表現の例
// DSL
pub def exampleExpr(): Free[Console, Unit] =
forM( // Free[Console] Monad
forM
_ &lt;- printLn(&quot;Enter a temperature in °F: &quot;);
d &lt;- readLn() |&gt; Functor.map(Option.flatMap(
Float64.fromString));
_ &lt;- printLn(&quot;In °C: ${Option.map(fToC, d)}&quot;)
) yield ()
//
def fToC(f: Float64): Float64 =
(f - 32.0) * 5.0 / 9.0
は
なので糖衣構文
も使える
華氏から摂氏への換算関数
110


# Page. 111

![Page Image](https://bcdn.docswell.com/page/YJ6WDXZLJV.jpg)

に対するインタープリタA (副作用あり)
DSL
pub def interpretWithSysConsole(expr: Free[Console, a]):
a \ Sys.Console =
match expr {
case Free.Pure(a) =&gt; a
case Free.Free(ReadLine(next)) =&gt;
interpretWithSysConsole(next(Sys.Console.readln()))
case Free.Free(PrintLine(line, next)) =&gt;
Sys.Console.println(line);
interpretWithSysConsole(next)
}
利用例
//
flix&gt; interpretWithSysConsole(exampleExpr())
Enter a temperature in °F:
77 //
In °C: Some(25.0)
()
ユーザー入力
111


# Page. 112

![Page Image](https://bcdn.docswell.com/page/GJ5M3XWMJ4.jpg)

に対するインタープリタB (副作用なし)
DSL
pub def interpretWithBuffer(in: List[String],
expr: Free[Console, a]): (a, List[String]) =
def loop(x, inBuf, outBuf) =
match x {
case Free.Pure(a) =&gt; (a, List.reverse(outBuf))
case Free.Free(ReadLine(next)) =&gt;
match inBuf {
case Nil =&gt; loop(next(None), Nil, outBuf)
case s :: ss =&gt;
loop(next(Some(s)), ss, outBuf)
}
case Free.Free(PrintLine(line, next)) =&gt;
loop(next, inBuf, line :: outBuf)
};
loop(expr, in, Nil)
利用例
//
flix&gt; interpretWithBuffer(List#{&quot;77&quot;}, exampleExpr())
((), Enter a temperature in °F: :: In °C: Some(25.0) :: Nil)
112


# Page. 113

![Page Image](https://bcdn.docswell.com/page/LE3W6X49E5.jpg)

らしい別解: 代数的エフェクト
Flix
pub eff Console {
def readLn(): Option[String]
def printLn(line: String): Unit
}
//
//
文字列の入力
文字列の出力
エフェクトを活用した関数の例
// Console
pub def exampleFn(): Unit \ Console =
Console.printLn(&quot;Enter a temperature in °F: &quot;);
let d = Console.readLn() |&gt; Option.flatMap(
Float64.fromString);
Console.printLn(&quot;In °C: ${Option.map(fToC, d)}&quot;)
Flix
標準ライブラリ: Sys.Console
113


# Page. 114

![Page Image](https://bcdn.docswell.com/page/8EDKV2Q87G.jpg)

エフェクトハンドラーA (副作用あり)
pub def runWithSysConsole(f: Unit -&gt; a \ ef):
a \ ef - Console + Sys.Console =
run {
f()
} with handler Console {
def readLn(resume) =
resume(Sys.Console.readln())
def printLn(line, resume) = {
Sys.Console.println(line);
resume()
}
}
利用例
//
flix&gt; run { exampleFn() } with runWithSysConsole
Enter a temperature in °F:
77 //
In °C: Some(25.0)
()
ユーザー入力
114


# Page. 115

![Page Image](https://bcdn.docswell.com/page/V7PKV1LWJ8.jpg)

エフェクトハンドラーB (副作用なし)
pub def runWithBuffer(in: List[String], f: Unit -&gt; a \ ef):
(a, List[String]) \ ef - Console =
region rc {
let inBuf = Ref.fresh(rc, in);
let outBuf = Ref.fresh(rc, Nil);
let a = run {
f()
} with handler Console {
def readLn(resume) = ???
def printLn(line, resume) = ???
};
(a, outBuf |&gt; Ref.get |&gt; List.reverse)
}
利用例
//
flix&gt; run { exampleFn() } with runWithBuffer(List#{&quot;77&quot;})
((), Enter a temperature in °F: :: In °C: Some(25.0) :: Nil)
115


# Page. 116

![Page Image](https://bcdn.docswell.com/page/2JVVZYQ9JQ.jpg)

第14章 局所エフェクトと可変状態
116


# Page. 117

![Page Image](https://bcdn.docswell.com/page/5EGL49W4JL.jpg)

局所的に可変性がほしい ⇒ ST モナド
mod St {
enum St[s, a](s -&gt; (a, s))
instance Functor[St[s]] { ... }
instance Applicative[St[s]] { ... }
instance Monad[St[s]] { .... }
//
st: forall s. Unit -&gt; St[s, a]
pub def runSt(st: Unit -&gt; St[s, a]): a = ???
}
⚠️
のようには書けない
モナドで局所化された可変参照
では可変参照を扱うと必ず
mod StRef { // ST
//
Flix
r: Region
struct StRef[_s: Type, a: Type, r: Region] {
mut cell: a
}
//
pub def make(rc: Region[r], a: a): St[s, StRef[s, a, r]]
\ r = ???
pub def read(r: StRef[s, a, r]): St[s, a] \ r = ???
pub def write(a: Lazy[a], r: StRef[s, a, r]): St[s, Unit]
\ r = ???
}
⚠️
が現れる
⚠️ エフェクトを含む型と噛み合わずキャストを多用することになる
117


# Page. 118

![Page Image](https://bcdn.docswell.com/page/4JQYGW3R7P.jpg)

モナドで局所化された可変配列
では 可変 配列を扱うと必ず
mod StArray { // ST
//
Flix
(
)
r: Region
struct StArray[_s: Type, a: Type, r: Region] {
mut value: Array[a, r]
}
//
pub def fromList(rc: Region[r], xs: List[a]):
St[s, StArray[s, a, r]] \ r = ???
pub def read(i: Int32, arr: StArray[s, a, r]):
St[s, a] \ r = ???
pub def write(i: Int32, a: a, arr: StArray[s, a, r]):
St[s, Unit] \ r = ???
...
}
⚠️
が現れる
⚠️ エフェクトを含む型と噛み合わずキャストを多用することになる
少なくともFlixには適さないアプローチ
エフェクトシステムでのより良い方法がある
118


# Page. 119

![Page Image](https://bcdn.docswell.com/page/K74W5K1QE1.jpg)

らしい別解: リージョン(region)機構
Flix
リージョンで局所化された可変参照
mod MyRef { //
struct MyRef[a: Type, r: Region] {
mut cell: a
}
pub def make(rc: Region[r], cell: a): MyRef[a, r] \ r =
new MyRef @ rc {cell = cell}
pub def read(r: MyRef[a, r]): a \ r =
r-&gt;cell
pub def write(a: a, r: MyRef[a, r]): Unit \ r =
r-&gt;cell = a
}
Flix
標準ライブラリ: Ref
119


# Page. 120

![Page Image](https://bcdn.docswell.com/page/LJ1YWXGLEG.jpg)

リージョンで局所化された可変配列
mod MyArray { //
struct MyArray[a: Type, r: Region] {
mut value: Array[a, r]
}
pub def fromList(rc: Region[r], xs: List[a]):
MyArray[a, r] \ r =
new MyArray @ rc {value = List.toArray(rc, xs)}
pub def read(i: Int32, arr: MyArray[a, r]): a \ r =
Array.get(i, arr-&gt;value)
pub def write(i: Int32, a: a, arr: MyArray[a, r]):
Unit \ r =
Array.put(a, i, arr-&gt;value)
...
}
Flix
標準ライブラリ: Array
120


# Page. 121

![Page Image](https://bcdn.docswell.com/page/GJWG6NKQ72.jpg)

局所的な可変性を活用した純粋関数の例
//
pub def quickSort(xs: List[Int32]): List[Int32] =
if (List.isEmpty(xs)) xs
else region rc { //
let arr = MyArray.fromList(rc, xs); //
qs(rc, 0, MyArray.size(arr) - 1, arr);
MyArray.freeze(arr)
//
}
可変性を閉じ込めるリージョンブロック
リストを配列化
配列をリスト化
def qs(rc: Region[r], l: Int32, r: Int32,
arr: MyArray[Int32, r]): Unit \ r = //
if (l &lt; r)
let pi = partition(rc, l, r, l + (r - l) / 2, arr);
qs(rc, l, pi - 1, arr); qs(rc, pi + 1, r, arr)
else ()
def partition(rc: Region[r], l: Int32, r: Int32,
pivot: Int32, arr: MyArray[Int32, r]): Int32 \ r =
let vp = MyArray.read(pivot, arr);
MyArray.swap(pivot, r, arr);
let j = MyRef.make(rc, l);
Range.forEach(i -&gt;
//
……
リージョンエフェクト付き
以下に実装が続く
121


# Page. 122

![Page Image](https://bcdn.docswell.com/page/4EZLY3Z373.jpg)

第15章 ストリーム処理と
インクリメンタルI/O
122


# Page. 123

![Page Image](https://bcdn.docswell.com/page/Y76WDXZZ7V.jpg)

外部リソースに安全に宣言的にアクセスしたい
⇒ ストリーム変換
ℹ️ 書籍では
の 類似の ストリーム処理ライブラリを実装している
ストリームの文字列を華氏温度と解釈して摂氏の数値に換算する
//
Scala (FS2
)
//
def conversion(iter: Iterator[String, ef, r]):
Iterator[Float64, ef, r] =
iter
|&gt; Iterator.map(String.trim)
|&gt; Iterator.filter(s -&gt; not String.isEmpty(s))
|&gt; Iterator.filter(s -&gt;
not String.startsWith(prefix = &quot;#&quot;, s)
)
|&gt; Iterator.filterMap(Float64.fromString)
|&gt; Iterator.map(toCelsius)
def toCelsius(fahrenheit: Float64): Float64 =
(5.0 / 9.0) * (fahrenheit - 32.0)
Flix
標準ライブラリ: Iterator
123


# Page. 124

![Page Image](https://bcdn.docswell.com/page/G75M3XW974.jpg)

ファイルI/Oユーティリティ
ℹ️ 現時点のFlix標準ライブラリには見当たらないためJava APIで自作
// インクリメンタルに行を読み出すイテレータ
//
//
def lineIterator(rc: Region[r], reader: BufferedReader):
Iterator[String, r + IO, r] \ r =
Iterator.iterate(rc, _ -&gt;
let line = reader.readLine();
if (Object.isNull(line)) None
else Some(line),
&quot;&quot;
)
バッファリング機能付きの文字列ストリームリーダー ライター
//
/
def bufferedReader(file: String): BufferedReader \ IO =
Files.newBufferedReader(Paths.get(file))
def bufferedWriter(file: String): BufferedWriter \ IO =
Files.newBufferedWriter(Paths.get(file))
124


# Page. 125

![Page Image](https://bcdn.docswell.com/page/9J29ZXQ5ER.jpg)

ローン
パターンのリソース管理ユーティリティ
//
(loan)
def withResource(
acquire: Unit -&gt; Closeable \ ef1,
handle: Closeable -&gt; a \ ef2
): Result[IOException, a] \ ef1 + ef2 + IO =
region rc {
let closable = Ref.fresh(rc, Option.empty());
let result = try {
Ref.put(Some(acquire()), closable);
closable |&gt; Ref.get |&gt; Option.foldLeft((_, c) -&gt;
c |&gt; handle |&gt; Ok,
Err(new IOException(&quot;unexpected&quot;))
)} catch { case e: IOException =&gt; Err(e) };
closable |&gt; Ref.get |&gt; Option.forEach(c -&gt; c.close());
result }
def withReader(file: String, handle: BufferedReader -&gt; a \ ef
): Result[IOException, a] \ ef + IO = ???
def withWriter(file: String, handle: BufferedWriter -&gt; a \ ef
): Result[IOException, a] \ ef + IO = ???
125


# Page. 126

![Page Image](https://bcdn.docswell.com/page/DEY4R8W6JM.jpg)

ストリーム変換を活用した関数の例
//
pub def convert(inputFile: String, outputFile: String):
Result[String, Unit] \ IO =
withReader(inputFile, reader -&gt;
withWriter(outputFile, writer -&gt;
region rc {
lineIterator(rc, reader)
|&gt; conversion
|&gt; Iterator.forEach(a -&gt; {
writer.write(&quot;${a}&quot;);
writer.newLine()
})
}))
|&gt; Result.flatten
|&gt; inspectEx
126


# Page. 127

![Page Image](https://bcdn.docswell.com/page/VJNYDQP178.jpg)

利用例
//
flix&gt; convert(&quot;input.txt&quot;, &quot;output.txt&quot;)
&gt; \\
Ok(())
入出力ファイル例
$ bat input.txt output.txt #
─────┬──────────────────────────────────────────
│ File: input.txt
─────┼──────────────────────────────────────────
1 │ -12.3
2 │ # 0
3 │ abc
4 │ 77
─────┴──────────────────────────────────────────
─────┬──────────────────────────────────────────
│ File: output.txt
─────┼──────────────────────────────────────────
1 │ -24.61111111111111
2 │ 25.0
─────┴──────────────────────────────────────────
127


# Page. 128

![Page Image](https://bcdn.docswell.com/page/YE9PGD3YJ3.jpg)

おわりに: 関数型プログラミングの抽象化過程
書籍 FP in Scala から見えてくる、また、Flix言語でも
推奨されている段階的な習熟/活用のステップ:
1. 再帰、代数的データ型とパターンマッチに親しむ
2. よくある操作パターンを高階関数として定義/利用
できるようになる
3. 様々な型で繰り返し現れる(高階)関数を型クラスと
して抽象化できるようになる
4. エフェクト/作用を上手く扱えるようになる
🐬&lt; FP in Scala もFlixも(純粋)関数型プログラミングの
深淵への入口として非常にオススメ😈
128


# Page. 129

![Page Image](https://bcdn.docswell.com/page/GE8DV3MKED.jpg)

Further Reading
書籍 FP in Scala
Functional Programming in Scala, Second Edition
cf. 1
Scala
第 版訳書『 関数型デザイン＆プログラ
ミング』
fpinscala: 書籍公式リポジトリ
fp-matsuri/fp-in-scala-exercises: FP in Scala ベース
の関数型プログラミング演習問題 by 関数型まつり
lagenorhynque/fp-in-scala: Clojure/Flix実装 by 🐬
129


# Page. 130

![Page Image](https://bcdn.docswell.com/page/LELM5V3P7R.jpg)

Flix
公式サイト
が出会った
志向な 関数型言語
ファーストインプレッション 紹介
関数型プログラミング(言語)
関数型言語テイスティング: Haskell, Scala, Clojure,
Elixirを比べて味わう関数型プログラミングの旨さ:
関数型プログラミング(言語)概説
&quot;Simple Made Easy&quot; Made Easier: Clojureに学ぶ
simplicity: Simple Made Easy解説
The Flix Programming Language: Flix
Clojurian
simple
JVM
Flix ——
: Flix
130


