- 関数型プログラミング(言語)の愛好者/実践者
- 関数型まつり2026の運営スタッフ(座長のひとり)
- 主に(CfPを含む)メインコンテンツの準備を担当
- 株式会社スマートラウンドのシニアエンジニア
- スタートアップと投資家のやり取りを効率化するデータ管理プラットフォームを開発
- ソフトウェア設計/開発とともに会計/財務/法務について探求するのも好き
-
きっかけ
-
書籍 FP in Scala とは
-
Flix言語の基本
-
(純粋)関数型のコード設計
- 関数型プログラミング入門
- 関数型設計とコンビネータライブラリ
- 関数型設計に共通する構造
- エフェクトとI/O
関数型まつり2025での🐬の発表
-
今年度は関数型プログラミングらしいコード設計/抽象化のパターンにより踏み込んで解説したい
-
関数型言語について学び始めた頃に取り組んだ本のひとつに FP in Scala があった
- 演習問題が膨大で途中まで進めて積んでいたが😇
-
関数型まつり2026の「FP入門ハンズオン」の題材として FP in Scala の演習問題を利用することに
- ハンズオン資料: fp-matsuri/2026.fp-hands-on
Flixとの出会い
-
エフェクトシステムと代数的エフェクトを探求する過程で、Flixという言語に出会った
-
simple と easy を区別して simplicity を重視するという方向性もFlixの設計指針に含まれている
- Rich Hickeyの有名なプレゼンSimple Made Easyに由来する
- 🐬はClojurianなので大いに感激した😆
[参考] Shibuya.lisp lispmeetup #118での🐬の発表
-
simple は客観的な構造の単純さ、 easy は主観的な(広義の)近さの問題(なのだが誤解されがち)
-
Clojureエコシステム/コミュニティには様々な simplicity 追求の例がある
-
抽象度を上げて、客体/モノと主体/ヒトの区別、
さらにはars/τέχνη (技芸)とvita/βίος (人生)の対比と捉えることもできる
Functional Programming in Scala, Second Edition
Manningの書籍詳細ページより『Scala関数型デザイン&プログラミング』
※ Functional Programming in Scala (第1版)の訳書
-
オブジェクト指向/関数型言語Scalaで(純粋関数型言語Haskellのような) FP実践を目指す本
- 2014年初版当時のScalaコミュニティでは良くも悪くも"better Java"扱いが珍しくなかった
- ScalaでFP設計を深く学べる(おそらく)最初の本
-
豊富な演習問題に取り組みながら理解を深め、抽象化の必要性/必然性を体感できる教育的な構成
- ただし、独習では挫折しやすい本でもある🥹
-
書籍公式リポジトリfpinscala
- 演習問題/ヒント/解答例/テストコード
- Wiki: 追加の解説/チャプターノート
- Haskellの概説ページも
- [参考] HaskellコードのFlix移植例 by 🐬
- Haskellの概説ページも
-
cf. Scala以外の関数型言語による実装
- fp-matsuri/fp-in-scala-exercises
- FP in Scala ベースの関数型プログラミング演習問題 by 関数型まつり
- lagenorhynque/fp-in-scala
- Clojure/Flix実装 by 🐬
- fp-matsuri/fp-in-scala-exercises
Flixとは
-
エフェクトシステムの組み込みサポートが特徴的な静的型付き関数型言語
- 副作用を伴う関数には
IOエフェクトが付く - Scalaと関係が深いが非オブジェクト指向言語
(サブタイピング(部分型)/継承とも無縁)
- 副作用を伴う関数には
-
JVM言語(Scala, Kotlin, Clojureなどと同じ)
-
主にHaskell, Scala, OCaml, Rustの影響が見られる
- 処理系の実装言語もScala
-
🐬< 大雑把には「Scalaの皮を被ったHaskell」🐑
-
Hindley-Milner型システムが採用されている
- HaskellやML系言語と同様
⚠️ トップレベルの定義のみ敢えて型注釈必須
-
- HaskellやML系言語と同様
- 構文的に多引数関数に見えても実体は1引数関数の連鎖
-
Haskellによく似た型クラス(type class)がある
traitというキーワードはRust/Scala由来
mkdir first-flix-project
cd first-flix-project
# プロジェクトを生成
flix init
# プロジェクトルートのモジュールファイルを追加
echo 'pub mod FirstFlixProject {}' > src/FirstFlixProject.flixflixコマンドでプロジェクトを生成する
// src/FirstFlixProject/SimpleMath.flix
pub mod FirstFlixProject.SimpleMath {
// パブリック関数
pub def square(n: Int32): Int32 =
n * n
// プライベート関数
// ⚠️ 未使用のままだとコンパイルエラー
def double(n: Int32): Int32 =
n * 2
}// src/FirstFlixProject/SomeModule.flix
pub mod FirstFlixProject.SomeModule {
// パブリックな0引数関数
pub def a(): Int32 = 1
// プライベートな0引数関数
// ⚠️ 未使用のままだとコンパイルエラー
def b(): Int32 = 2
}- トップレベル変数は存在しない
flix> use FirstFlixProject.{SimpleMath => M}; \
> M.square(3)
9
flix> use FirstFlixProject.{SimpleMath => M}; \
> List.map(M.square, List.range(0, 11))
0 :: 1 :: 4 :: 9 :: 16 :: 25 :: 36 :: 49 :: 64 :: 81 :: 100
:: Nil
flix> use FirstFlixProject.{SimpleMath => M}; \
> List.range(0, 11) |> List.map(M.square)
0 :: 1 :: 4 :: 9 :: 16 :: 25 :: 36 :: 49 :: 64 :: 81 :: 100
:: Nil;は式の区切り文字\はREPLでの行継続|>はパイプ(ライン)演算子
flix> \\
> use FirstFlixProject.{SimpleMath => M};
> let x = 5;
> let y = M.square(x);
> "square(${x}) = ${y}"
> \\
square(5) = 25\\はREPLでの複数行入力の開始/終了
flix> (n -> n * n * n)(2)
8
flix> List.map(n -> n * n * n, List.range(0, 11))
0 :: 1 :: 8 :: 27 :: 64 :: 125 :: 216 :: 343 :: 512 :: 729 ::
1000 :: Nil- 関数型プログラミング入門
- 関数型設計とコンビネータライブラリ
- 関数型設計に共通する構造
- エフェクトとI/O
- 関数型プログラミングとは
- Flixで始める関数型プログラミング
- 関数型データ構造
- 例外によらないエラーハンドリング
- 正格性と遅延性
- 純粋関数型の状態
-
関数型プログラミング(functional programming)
-
純粋関数(pure function)
-
副作用(side effect)
-
参照透過性(referential transparency)
-
純粋性(purity)
-
置換モデル(substitution model)
// ⚠️ 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("charging " + price) // 請求処理(簡単のため標準出力)
class Coffee:
val price: Double = 2.0// 利用例
scala> Cafe().buyCoffee(CreditCard())
charging 2.0 // 標準出力
val res0: Coffee = Coffee@353cb7e5 // 戻り値- クレジットカードへの請求処理という副作用が埋め込まれている
// ⚠️ 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("charging " + price + " to " + cc)- 副作用を含む実装を差し替えやすくなった
- しかし依然として:
- テストしやすいとまではいえない
buyCoffeeの再利用性は低い
mod Coffee {
// priceフィールドを持つレコードによるCoffee型
pub enum Coffee({price = Float64})
// 文字列表現(ToString型クラスの実装)
instance ToString[Coffee] {
pub def toString(coffee: Coffee): String =
let Coffee({price}) = coffee;
"Coffee({price = ${price}})"
}
// コンストラクタ関数(ただの関数)
pub def make(): Coffee = Coffee({price = 2.0})
}mod CreditCard {
// 金額を受け取って何も返さず、IOエフェクト(副作用)を伴う関数
pub def charge(price: Float64): Unit \ IO =
println("charging ${price}")
}mod Cafe {
use FpInFlix.Exercises.Introduction.FirstExample.Coffee
// 請求関数を受け取ってコーヒーを返し、IOエフェクトを伴う関数
pub def buyCoffee(charge: Float64 -> Unit \ IO):
Coffee \ IO =
let coffee = Coffee.make();
let Coffee.Coffee({price}) = coffee;
charge(price);
coffee
}// 利用例
flix> use FpInFlix.Exercises.Introduction.FirstExample.{Cafe,
CreditCard}; \ // ℹ️ 以降はスペース節約のためuseを省略する
> Cafe.buyCoffee(CreditCard.charge)
charging 2.0 // 標準出力
Coffee({price = 2.0}) // 戻り値- 高階関数によるDI
- 高階関数は様々な設計パターンの基礎になる
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
}pub def example(): Unit \ IO =
let cup = run {
Cafe.buyCoffee(CreditCard.CreditCard("A"))
} with handler Payment { // 決済エフェクトに対するハンドラー
def charge(cc, amount, resume) = {
println("charging ${amount} to ${cc}");
resume() // エフェクト発生地点から続く処理を再開
} // (継続を呼び出す)
};
println("a cup of ${cup}")// 利用例
flix> example()
charging 2.0 to CreditCard(A) // 標準出力(Payment.charge)
a cup of Coffee({price = 2.0}) // 標準出力(exampleの最後)
() // 戻り値- 代数的エフェクトとエフェクトハンドラーによるDI
- エフェクトシステムの活用例
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;
"Charge({creditCard = ${creditCard}, amount =
${amount}})"
}- 「請求」をただのデータとして表す
- 解釈は利用側に委ねる
// クレジットカードが同一の場合に限って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("Can't combine charges to different cards:
cardA=${cardA}, cardB=${cardB}") // 請求リストをクレジットカードごとにまとめる純粋関数
pub def coalesce(charges: List[Charge]): Result[String,
List[Charge]] =
charges
|> List.groupBy(charge -> // カードでグループ化
let Charge({creditCard | _}) = charge;
creditCard
)
|> Traversable.traverse(group -> // combineで集約
Foldable.foldLeftM(combine, Nel.head(group),
Nel.tail(group)))
} 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)
|> List.repeat(n) |> List.unzip; // n個繰り返し
charges // コーヒーリストと請求のタプルに集約
|> Foldable.foldLeftM(Charge.combine,
Charge.Charge({creditCard = cc,
amount = 0.0}))
|> Functor.map(charge -> (coffees, charge))
}// 利用例
flix> Cafe.buyCoffee(CreditCard.CreditCard("A"))
(Coffee({price = 2.0}), Charge({creditCard = CreditCard(A),
amount = 2.0})) // 戻り値
flix> Cafe.buyCoffees(CreditCard.CreditCard("A"), 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.
-
再帰関数(recursive function)
- 末尾再帰関数(tail-recursive function)
- 末尾呼び出し最適化/除去(tail-call optimization/elimination)
- 末尾再帰関数(tail-recursive function)
-
高階関数(higher-order function)
-
多相関数(polymorphic function)
- ↔ 単相関数(monomorphic function)
- パラメータ多相(parametric polymorphism)
-
型駆動開発(type-driven development)
// (関数型言語のチュートリアルでお馴染みの)階乗関数
pub def factorial(n: Int32): Int32 =
@Tailrec // 末尾再帰であることを保証するアノテーション
def go(x, acc) = // 末尾再帰関数(ループ相当に最適化される)
if (x <= 0) acc // 基底部
else go(x - 1, x * acc); // 再帰部
go(n, 1)// 利用例
flix> List.map(factorial, List.range(0, 6))
1 :: 1 :: 2 :: 6 :: 24 :: 120 :: Nilpub def formatAbs(x: Int32): String =
"The absolute value of ${x} is ${abs(x)}."
pub def formatFactorial(n: Int32): String =
"The factorial of ${n} is ${factorial(n)}."
// 整数を受け取って整数を返す関数をパラメータ化して高階関数に
pub def formatResult(name: String, n: Int32,
f: Int32 -> Int32): String =
"The ${name} of ${n} is ${f(n)}."// 利用例
flix> formatResult("absolute value", -3, abs)
The absolute value of -3 is 3.
flix> formatResult("factorial", 5, factorial)
The factorial of 5 is 120.// 文字列のベクターをキーで線形探索してインデックスを返す関数
pub def findFirst(ss: Vector[String], key: String): Int32 =
@Tailrec
def loop(n) =
if (n >= Vector.size(ss)) -1
else if (Vector.get(n, ss) == key) n
else loop(n + 1);
loop(0)// 任意の要素型のベクターを述語関数で線形探索してインデックスを返す関数
pub def findFirst(xs: Vector[a], p: a -> Bool): Int32 =
@Tailrec
def loop(n) =
if (n >= Vector.size(xs)) -1
else if (xs |> Vector.get(n) |> p) n
else loop(n + 1);
loop(0)-
代数的データ型(algebraic data type, ADT)
- 直積型(product type)
- 直和型(sum type)
-
再帰データ型(recursive data type)
-
パターンマッチング(pattern matching)
- 網羅性(exhaustiveness)
-
純粋関数型データ型(purely functional data type), 永続データ型(persistent data type)
- 構造共有(structural sharing)
// リスト(単方向連結リスト)
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]) // 枝の場合(再帰部)
}// リストの長さ
pub def length(l: List[a]): Int32 =
match l {
case Nil => 0
case Cons(_, xs) => 1 + length(xs)
}
// ツリーの要素数
pub def size(t: Tree[a]): Int32 =
match t {
case Leaf(_) => 1
case Branch(left, right) => 1 + size(left) +
size(right)
}// 利用例
flix> length(Cons("a", Cons("b", Cons("c", Nil))))
3
flix> size(Branch(Branch(Leaf("a"), Leaf("b")), Leaf("c")))
5// 右から畳み込む関数(再帰構造に沿った集約関数)
// ⚠️ 愚直な実装では末尾再帰/スタックセーフにならない(工夫が必要)
pub def foldRight(f: (a, b) -> b \ ef, acc: b, l: List[a]):
b \ ef =
match l {
case Nil => acc
case Cons(x, xs) => f(x, foldRight(f, acc, xs))
}// 利用例
flix> foldRight((-), 0, Cons(1, Cons(2, Cons(3, Nil))))
2
// (1 - (2 - (3 - 0)))
// = (1 - (2 - 3))
// = (1 - (-1))
// = 2// 左から畳み込む関数(末尾再帰の集約関数)
@Tailrec // 末尾再帰
pub def foldLeft(f: (b, a) -> b \ ef, acc: b, l: List[a]):
b \ ef =
match l {
case Nil => acc
case Cons(x, xs) => foldLeft(f, f(acc, x), xs)
}// 利用例
flix> foldLeft((-), 0, Cons(1, Cons(2, Cons(3, Nil))))
-6
// (((0 - 1) - 2) - 3)
// = (((-1) - 2) - 3)
// = ((-3) - 3)
// = -6- ℹ️ 結合法則を満たさない(+ 単位元を持たない)演算では
foldRightとfoldLeftの結果が異なる
// リストを返す関数を各要素に適用して得られるリストを平坦化して返す関数
pub def flatMap(f: a -> List[b] \ ef, l: List[a]):
List[b] \ ef =
// ℹ️ append: (List[a], List[a]) -> List[a] は定義済みとする
foldRight((x, acc) -> append(f(x), acc), Nil, l)// 利用例
flix> \\
> flatMap(
> x -> Cons(x, Cons(x + 1, Nil)),
> Cons(1, Cons(2, Cons(3, Nil)))
> )
> \\
Cons(1, Cons(2, Cons(2, Cons(3, Cons(3, Cons(4, Nil))))))// 関数を各要素に適用して得られるリストを返す関数
pub def map(f: a -> b \ ef, l: List[a]): List[b] \ ef =
flatMap(x -> Cons(f(x), Nil), l)// 利用例
flix> \\
> map(
> x -> x * x * x,
> Cons(1, Cons(2, Cons(3, Nil)))
> )
> \\
Cons(1, Cons(8, Cons(27, Nil)))// 述語を満たす要素のみのリストを返す関数
pub def filter(p: a -> Bool \ ef, l: List[a]): List[a] \ ef =
flatMap(x -> if (p(x)) Cons(x, Nil) else Nil, l)// 利用例
flix> \\
> filter(
> // ℹ️ バッククォートで括ると中置記法に
> x -> x `Int32.remainder` 2 != 0,
> Cons(1, Cons(2, Cons(3, Nil)))
> )
> \\
Cons(1, Cons(3, Nil))-
他にも多様な高階関数を定義できる
-
他のデータ構造についても同様に整備できる
pub enum Option[a] with Eq, ToString {
case None // 値がない場合
case Some(a) // 値がある場合
}-
例外は参照透過性を損なう副作用なので避ける
- 代わりに代数的データ型で表す
- ⇒ コレクションと同様に豊かな操作の対象に
-
Flix標準ライブラリ:
Option
pub enum Either[e, a] with Eq, ToString {
case Left(e) // 失敗の場合: エラー値を持つ
case Right(a) // 成功の場合: 成功値を持つ
}- Flix標準ライブラリ:
Result
pub def flatMap(f: a -> Option[b] \ ef, o: Option[a]):
Option[b] \ ef =
match o {
case None => None
case Some(x) => f(x)
}pub def flatMap(f: a -> Either[e, b] \ ef, et: Either[e, a]):
Either[e, b] \ ef =
match et {
case Left(x) => Left(x)
case Right(y) => f(y)
}pub def map(f: a -> b \ ef, o: Option[a]):
Option[b] \ ef = ???
pub def filter(p: a -> Bool \ ef, o: Option[a]):
Option[a] \ ef = ???// 利用例
flix> map(x -> x / 2, Some(42))
Some(21)
flix> map(x -> x / 2, None)
None
flix> filter(x -> x `Int32.remainder` 2 != 0, Some(42))
None
flix> filter(x -> x `Int32.remainder` 2 != 0, None)
Nonepub def map(f: a -> b \ ef, et: Either[e, a]):
Either[e, b] \ ef = ???
// ⚠️ Eitherのfilterは無条件には定義できない
// (述語を満たさない場合に採用するエラー値が定まらないため)// 利用例
flix> map(x -> x / 2, (Right(42): Either[String, Int32]))
Right(21)
flix> map(x -> x / 2, Left("falsity"))
Left(falsity)// 2引数関数を2個のOption値に適用してOptionを返す関数
pub def map2(f: (a, b) -> c \ ef, o1: Option[a],
o2: Option[b]): Option[c] \ ef =
flatMap(x -> map(y -> f(x, y), o2), o1)// 利用例
flix> map2((+), Some(1), Some(2))
Some(3)
flix> map2((+), None, Some(2))
None
flix> map2((+), Some(1), None)
None
flix> map2(((+): (Int32, Int32) -> Int32), None, None)
NoneEitherについても同様に定義できる
// Optionを返す関数をリストの各要素に適用してリストのOptionを返す関数
pub def traverse(f: a -> Option[b] \ ef, l: List[a]):
Option[List[b]] \ ef =
List.foldRight(
(x, acc) -> 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> traverse(x -> if (x `Int32.remainder` 2 != 0) Some(x)
else None, List#{1, 2, 3})
None
flix> traverse(x -> if (x `Int32.remainder` 2 != 0) Some(x)
else None, List#{1, 3, 5})
Some(1 :: 3 :: 5 :: Nil)Eitherについても同様に定義できる
pub enum Validated[e, a] with Eq, ToString {
case Valid(a) // 有効な場合: 有効値を持つ
case Invalid(Nel[e]) // 無効な場合: エラー値の非空リストを持つ
}- Flix標準ライブラリ:
Validation
-
非正格性(nonstrictness)、非正格評価(nonstrict evaluation)
- ↔ 正格性(strictness)、正格評価(strict evaluation)/先行評価(eager evaluation)
- 遅延性(laziness)、遅延評価(lazy evaluation)
-
計算の記述(description)と評価(evaluation)の分離
-
余再帰関数(corecursive function)
pub enum LazyList[a] {
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 > 1 =>
// ℹ️ lazyは直後の式を遅延評価されるようにし、
// forceは直後の遅延評価式を実体化する
Cons(head, lazy take(n - 1, force tail))
case Cons(head, _) if n == 1 =>
Cons(head, lazy Empty)
case _ => Empty
}// 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> LazyList.intsFrom(0) |> LazyList.map(x -> x * x)
|> LazyList.take(5) |> LazyList.toList
0 :: 1 :: 4 :: 9 :: 16 :: Nil
flix> LazyList.fibs() |> LazyList.take(10) |> LazyList.toList
0 :: 1 :: 1 :: 2 :: 3 :: 5 :: 8 :: 13 :: 21 :: 34 :: Nil// シード値を持つ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)
|> Int64.truncateToInt32;
(n, nextRng)
// Rngを受け取って任意のランダム生成値と新たなRngを返す関数の型エイリアス
pub type alias Rand[a] = Rng -> (a, Rng)- Flix標準ライブラリ:
Math.Random(エフェクト)
// 自然に定義できるユーティリティ関数の例
pub def unit(a: a): Rand[a] = ???
pub def map(f: a -> b, ra: Rand[a]): Rand[b] = ???
pub def map2(f: (a, b) -> c, ra: Rand[a], rb: Rand[b]):
Rand[c] = ???
pub def flatMap(f: a -> Rand[b], ra: Rand[a]): Rand[b] = ???
// ランダム関数の例
pub def int(): Rand[Int32] = rng -> nextInt(rng)
pub def boolean(): Rand[Bool] =
rng ->
let (i, rng2) = nextInt(rng);
(i `Int32.remainder` 2 == 0, rng2)// 利用例
flix> map2((i, b) -> "int: ${i}, boolean: ${b}", int(),
boolean())(Rng(42i64))
(int: 16159453, boolean: false, Rng(197491923327988))
flix> map2((i, b) -> "int: ${i}, boolean: ${b}", int(),
boolean())(Rng(197491923327988i64))
(int: -340305902, boolean: true, Rng(149370390209998))- 純粋関数型の並列処理
- プロパティベーステスト
- パーサーコンビネータ
// マルチスレッド実行管理オブジェクトを受け取り非同期処理の結果を返す関数
enum Par[a](ExecutorService -> Future[a] \ IO)
pub def unit(a: a): Par[a] =
Par(_es -> /* ExecutorServiceを利用せず直ちにaを返す */)
pub def fork(pa: Lazy[Par[a]]): Par[a] =
Par(es -> /* ExecutorServiceを利用して非同期的にタスク実行 */)
pub def runPar(es: ExecutorService, pa: Par[a]):
Future[a] \ IO =
let Par(f) = pa;
f(es)- Flix標準機能:
par-yield構文による並列処理- (Go言語に似た)軽量スレッドと
ChannelによるCSPベースの並行処理
// 自然に定義できるユーティリティ関数の例
pub def flatMap(f: a -> Par[b], pa: Par[a]): Par[b] = ???
pub def map2(f: (a, b) -> c, pa: Par[a], pb: Par[b]):
Par[c] = ???
pub def map(f: a -> b, pa: Par[a]): Par[b] = ???
// 並列処理を実行する関数の例
pub def runWithMap2(f: (a, b) -> c, pa: Par[a], pb: Par[b]):
c \ IO =
let es = Executors.newFixedThreadPool(4);
let p = map2(f, pa, pb);
let v = runPar(es, p).get();
es.shutdown();
v// 利用例
flix> runWithMap2((a, b) -> a + b, unit(2), unit(3))
5
flix> runWithMap2((a, b) -> a ++ b, unit(List#{1, 2}),
unit(List#{3, 4, 5}))
1 :: 2 :: 3 :: 4 :: 5 :: Nil;; TODO: 実装に立ち入らず、コアのモデル(型定義)とできることを手短に紹介
;; TODO: 実装に立ち入らず、コアのモデル(型定義)とできることを手短に紹介
- モノイド
- モナド
- アプリカティブ/トラバーサブルファンクター
- ???
;; TODO
- ???
;; TODO
- ???
;; TODO
- 外部エフェクトとI/O
- 局所エフェクトと可変状態
- ストリーム処理とインクリメンタルI/O
;; TODO: 実装に立ち入らず、コアのモデル(型定義)とできることを手短に紹介 + Flixでのエフェクトベースのアプローチを紹介
;; TODO: 実装に立ち入らず、コアのモデル(型定義)とできることを手短に紹介 + Flixでのエフェクトベースのアプローチを紹介
;; TODO: 実装に立ち入らず、コアのモデル(型定義)とできることを手短に紹介
書籍 FP in Scala から見えてくる、また、Flix言語でも推奨されている段階的な習熟/活用のステップ:
- 再帰、代数的データ型とパターンマッチに親しむ
- よくある操作パターンを高階関数として定義/利用できるようになる
- 様々な型で繰り返し現れる(高階)関数を型クラスとして抽象化できるようになる
🐬< FP in Scala もFlixも(純粋)関数型プログラミングの深淵への入口として非常にオススメ😈
-
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 🐬
-
The Flix Programming Language: Flix公式サイト




