Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
堅牢なシステム開発を目指して 関数型で学ぶイミュータブル ワールド
Search
aoyagi
August 12, 2022
Programming
0
100
堅牢なシステム開発を目指して 関数型で学ぶイミュータブル ワールド
関数型プログラミング の概要を 30 分で学びます
命令型(手続き型)プログラミング と比較しながら関数型の基礎となる イミュータブル の堅牢性をできる限り分かりやすくお伝えします
aoyagi
August 12, 2022
Tweet
Share
More Decks by aoyagi
See All by aoyagi
Google Cloud ネットワーク 勉強会
aoyagi9936
0
58
Other Decks in Programming
See All in Programming
GitHub Copilot and GitHub Codespaces Hands-on
ymd65536
1
120
関数型まつり2025登壇資料「関数プログラミングと再帰」
taisontsukada
2
850
ニーリーにおけるプロダクトエンジニア
nealle
0
570
プロダクト志向なエンジニアがもう一歩先の価値を目指すために意識したこと
nealle
0
110
AIコーディング道場勉強会#2 君(エンジニア)たちはどう生きるか
misakiotb
1
250
PHPでWebSocketサーバーを実装しよう2025
kubotak
0
220
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
370
型付きアクターモデルがもたらす分散シミュレーションの未来
piyo7
0
810
WebViewの現在地 - SwiftUI時代のWebKit - / The Current State Of WebView
marcy731
0
100
PicoRuby on Rails
makicamel
2
110
Webの外へ飛び出せ NativePHPが切り拓くPHPの未来
takuyakatsusa
2
430
たった 1 枚の PHP ファイルで実装する MCP サーバ / MCP Server with Vanilla PHP
okashoi
1
200
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
1.8k
The Language of Interfaces
destraynor
158
25k
What’s in a name? Adding method to the madness
productmarketing
PRO
23
3.5k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Thoughts on Productivity
jonyablonski
69
4.7k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
181
53k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
20
1.3k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
46
9.6k
Transcript
堅牢なシステム開発を目指して 関数型で学ぶ イミュータブル ワールド 2022 年 8 月 12 日
本発表について • スライドでは細かな説明は省略しています • 難しい領域なので手探りですが間違い等あればご指摘ください • 発表時間: 30 分 •
発表者 ◦ @aoyagi9936 ◦ クラウドエンジニア(主に Google Cloud を扱っています) ◦ 環境問題を解決する新規事業の立ち上げ&プラットフォーム構築中です ◦ メンバー大募集中です 2
イミュータブルとは? • イミュータブル = 不変 ◦ 一度作成した後は変更は不可 ◦ 分かりやすく言えば Read
Only(読み取り専用)や Constants(定数) • アプリケーション開発/インフラ構築のどちらの領域にも登場 ◦ 私の最初の出会いは Scala の不変コレクション → scala.collection.immutable ◦ その後インフラの勉強中に Chef を触り始め再び出会う → immutable infrastracture ◦ DB設計では CRUD で Update と Delete を排除した概念を知る → immutable data 3
なぜイミュータブルが良い? ① • 月並みですがシステム開発はとても複雑です ◦ 『プログラミングにおける変数や処理結果 』『インフラ構築におけるパラメータ 』『データベー スにおけるデータモデル』など、システムは多種多様な要素の相互接続の上に成り立って います
◦ システムの規模が大きくなればなるほど1つ1つの要素も組み合わせも複雑になっていき ます ◦ 複雑性の定義として Complex と Complicated があり対処は異なりますが、ここでは詳し く触れないので気になる人は “カネヴィンフレームワーク” で調べましょう 4
なぜイミュータブルが良い? ② • 私達は更新したくない ◦ “既にあるもの” を更新することは危険を伴います ▪ リスト構造更新時の Index
out of range(要素数を超えた添字の指定) ▪ バージョンアップ時の Destructive Change(破壊的変更) ◦ イミュータブルでも変化は避けられません ▪ 1つ1つの要素がイミュータブルでも相互作用による変化は発生します ▪ ですが “新規作成” は “更新” よりも変化を予測しやすく安全です ◦ 既存の要素を更新する時、私達はその要素を “移行” しています 5
関数型プログラミングとは? • 関数を主軸にしたプログラミングを行うスタイルである(Wikipedia) ◦ ここからはプログラミングについてのみ触れます ◦ 関数型を学ぶには以下の概念を知る必要があります ▪ データを不変なものとして扱うこと •
元の要素を変更せず、新しい要素を返します ▪ 同じ引数に対して必ず決まった値を返すこと(参照透過性) • 引数に与える変数への再代入は行わない • 関数自体が状態を持たないということではない 6
関数型プログラミング言語 先ほどのルールに則ったプログラ ミングを行える言語は『関数型プロ グラミング言語』と呼ばれます。 参照透過性が成り立ち副作用の 生じない言語は『純粋関数型』と呼 ばれます。※副作用については後述 出典:Wikipedia - 関数型プログラミング
7
関数型プログラミングの堅牢性 • 関数型プログラミングは従来の命令型プログラミングに比べバグの入り込む余地が少ない記述 となります。以下はリスト内の偶数を出力するプログラムの例です。 val list = List[Integer](1, 2, 3,
4, 5) list.filter(n => n % 2 == 0) .foreach(println(_)) filter は条件によって要素を抽出するメソッドです。意図も分かりやす く無駄のないシンプルな記述です。println の (_) は Scala 特有のシ ンタックスシュガーですが v => println(v) と書き直すこともできます。 val list = List[Integer](1, 2, 3, 4, 5) for (s <- list) { if (s % 2 == 0) { println(s) } } for の「ループする」命令と if の「もし〜だったら...する」命令を組み 合わせて1つの処理を表現しています。 構文を見ただけでは内容が分かりにくく「for 〜 if」「if 〜 println 」の 前後関係に拘束力もないため、間に処理が入った場合は複雑性が 増し「偶数で絞り込む」という目的が不透明になってしまいます。 Scalaによる命令型プログラミング Scalaによる関数型プログラミング 8
Functional Programming Level 1 • 命令型から関数型へ ◦ 「まだ良さがよく分からない」という方はリスト構造を扱う際に関数型に置き換えてみることから 始めると良いと思います ◦
様々なプログラミング言語も関数型プログラミングに対応しています ▪ C# → LINQ ラムダ式 ▪ Javascript / Typescript → ES6 以降のアロー関数(ES5 以前は Lodash.js / Unserscore.js を利用) ▪ Java SE8 → java.utils.function パッケージ、Stream API ▪ Python → iterator、generator、itertools ▪ Rust → Iterator、Closure ▪ Golang → 未対応だが 1.18 から Generics に対応したことで各種ライブラリも誕生 9
Functional Programming Level 2 • 関数型が分かってくると全てを関数型で書きたくなってきます ◦ しかし、暫くするとその難しさに気がつきます ◦ 現実のシステムは多種多様な要素の相互接続によってビジネスを表現します
◦ 関数の外側で発生する相互作用は「副作用」と呼ばれます ▪ ファイルの入出力、データベースの更新、HTML(DOM)の更新、API通信など ◦ 繰り返しになりますがシステムには多種多様な要素があり相互に影響を与えます ◦ システム全体に関数型を取り入れるには言語だけでは “表現力” に限界を感じるようになりま す 10
Functional Programming Level 3 • 関数型をより強固にするライブラリ/フレームワークの導入 ◦ React(Javascript / Typescript)
▪ React Hooks によりコンポーネントを関数型で扱うことができます ▪ React Hook Form や GraphQL Code Generator による Hooks 自動生成など、エコシス テムもとても充実しています ▪ Storybook との相性も良い ◦ ZIO(Scala) ▪ 純粋関数型プログラミングベースで非同期・並列処理を実現する Scala ライブラリ ▪ quill, tapir, sttp など既存のライブラリも続々と ZIO に対応しています ▪ DI や Test など大規模システム開発で必要な機能がオールインワンになっています 11
React による イミュータブル ワールド import { useState } from 'react'
import { useGetUsers } from ‘./api/generated/schema/’ const ExampleComponent = () => { const { msg, setMsg } = useState(‘’) const { data, fetching, error } = useGetUsers() return ( <> <p>{msg}<p> { fetching ? <p>loading…</p> : error ? <p>error</p> : data ? data.getUsers.map(v => <input type=’button’ value={v.name} onClick={(e) => setMsg(e.target.value} /> ) } </> ) } export const ExampleComponent 関数コンポーネント内は全て const (定数) によって イミュータブ ル に宣言されています。 自動生成しておいた useGetUsers Hooks を使って非同期通信 処理 (GraphQL) を 関数コンポーネント に接続しています。 GraphQL ライブラリである Urql では data (結果) , fetching (取 得中) , error (失敗) がそれぞれ格納されるので三項演算子を使 うことで結果に応じた表示の変更を シンプル に表現できます。 useState は状態を保存するための React の機能で ステートフ ル な値とそれを更新するための関数を取得できます。 更新用の関数を通すことに回りくどさを感じるかもしれませんが、 React は Hooks での更新を検知して 再レンダリング を スケ ジュール する際に更新前と同じ値の場合は処理を終了するなど の判断も担ってくれます。 12
ZIO によるイミュータブルワールド object ExampleService { trait ExampleService { def getUsers():
Task[List[User]] } def getUsers(): RIO[ExampleService, List[Users]] = ZIO.serviceWithZIO(_.getUsers()) lazy val live: ZLayer[Repo, Nothing, ExampleService] = ZLayer { for { repo <- ZIO.service[Repo] } yield new ExampleService { def getUsers(): Task[List[User]] = for { list <- repo.getUserList() res <- ZIO.succeed( list.map(v => User(v.id, s”${v.lastName} ${v.firstName}”))) } yield res }} } ZIO (2.0) の DI パターンです。 前半はこの オブジェクト を DI するための インター フェース と アクセサ です。 「lazy val live = ...」がこのオブジェクトの実装となりま す が val はイミュータブルな宣言であり不変です (ちなみ にミュータブルは var )。 Scala の for 式 は yield をつけることで変数を束縛し ながら最終的な値を生成するジェネレーターとして動作 させることができます。 getUsers() では ZIO によって依存性が注入された Repo を利用してデータを取得し加工 (姓と名を結合) して返却しています。 一連の流れを関数を繋げた形式で記述でき、余計な処 理が入る余地が限りなく少なくなっていることが分かり ます。 13
参考リンク • 関数型プログラミングについて ◦ Functional programming is finally going mainstream
◦ Functional Programming 101 • スライド内で紹介した技術 ◦ Fundamentals of functional programming with React ◦ Structuring ZIO 2 applications • 併せて学ぶと良いもの ◦ リアクティブ プログラミング ▪ Reactive X 14