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
93
堅牢なシステム開発を目指して 関数型で学ぶイミュータブル ワールド
関数型プログラミング の概要を 30 分で学びます
命令型(手続き型)プログラミング と比較しながら関数型の基礎となる イミュータブル の堅牢性をできる限り分かりやすくお伝えします
aoyagi
August 12, 2022
Tweet
Share
More Decks by aoyagi
See All by aoyagi
Google Cloud ネットワーク 勉強会
aoyagi9936
0
44
Other Decks in Programming
See All in Programming
GitHub Copilotのススメ
marcy731
1
200
PHP8.3の機能を振り返る / Review of PHP 8.3 features
seike460
PRO
1
110
DMMプラットフォームがTiDB Cloudを採用した背景
pospome
8
4.1k
1BRC--Nerd Sniping the Java Community
gunnarmorling
0
340
Milestoner
bkuhlmann
1
410
Prepare for Jakarta EE 11 - Performance and Developer Productivity
ivargrimstad
0
810
Komplexe Oberflächen mit SVG und der Web Animation API
joergneumann
0
670
Behind VS Code Extensions for JavaScript / TypeScript Linnting and Formatting
unvalley
5
920
dbtのドメイン分割による データ基盤の改善とDigdagとの連携
sakama
0
350
AWS Application Composerで始める、 サーバーレスなデータ基盤構築 / 20240406-jawsug-hokuriku-shinkansen
kasacchiful
1
260
Git Lint
bkuhlmann
4
750
冗長なエラーログを削減し、スタックトレースを手に入れる / Reducing Verbose Error Logs and Obtaining Stack Traces
upamune
0
770
Featured
See All Featured
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
125
32k
Scaling GitHub
holman
457
140k
Being A Developer After 40
akosma
57
580k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
274
13k
We Have a Design System, Now What?
morganepeng
43
6.8k
Designing with Data
zakiwarfel
96
4.8k
The Invisible Customer
myddelton
114
12k
Building a Modern Day E-commerce SEO Strategy
aleyda
17
6.4k
Visualization
eitanlees
136
14k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
221
21k
How to train your dragon (web standard)
notwaldorf
73
5.2k
It's Worth the Effort
3n
180
27k
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