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
非侵入的モナド的操作導入ライブラリ harmony とコンセプト・CPO
Search
onihusube
August 09, 2021
Programming
230
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
非侵入的モナド的操作導入ライブラリ harmony とコンセプト・CPO
talk.cpp スライド
onihusube
August 09, 2021
More Decks by onihusube
See All by onihusube
return文におけるstd::moveについて
onihusube
1
2.8k
Other Decks in Programming
See All in Programming
ランチタイムLT会3周年!ランチタイムLT会を3年間続けられたお話
y0hgi
1
140
そのテスト、説明できますか?~LWテスト戦略FW~のご紹介
nakahara
0
200
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
150
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
220
霧の中の代数的エフェクト
funnyycat
1
350
Honoでのサプライチェーン侵害対策 〜 3つのライブラリに学ぶ
yusukebe
7
1.8k
Generative UI & AI-Assistants for Your Angular Solutions
manfredsteyer
PRO
0
100
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
120
Creating Composable Callables in Contemporary C++
rollbear
0
200
吝嗇家のためのAI活用 / AI development for miser - ChatGPT + Issue Driven Development
tooppoo
0
180
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
170
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
220
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
22k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
360
The Cult of Friendly URLs
andyhume
79
6.9k
The Invisible Side of Design
smashingmag
301
52k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
1.9k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
340
Documentation Writing (for coders)
carmenintech
77
5.4k
Music & Morning Musume
bryan
47
7.3k
How to Ace a Technical Interview
jacobian
281
24k
GraphQLとの向き合い方2022年版
quramy
50
15k
First, design no harm
axbom
PRO
2
1.2k
Transcript
非侵入的モナド的操作導入 ライブラリ harmony とコンセプト・CPO @onihusube9
おしながき 1. harmony について 1. 動機付け 2. harmonyのMonadic Interface 2.
harmonyのコンセプト、その実装 1. harmonyの対象とコンセプト 2. harmonyのコンセプト、実装 3. コンセプトベースの設計について
harmonyの動機1 • どこからかもらってきた std::optional • ifチェックだるくないです か? • ネストすると見辛い •
連続しても見辛い • 忘れたり間違えたり・・・ auto f() -> std::optional<int>; void success(int&); void failure(); int main () { auto opt = f(); if (opt) { success(*opt); } else { failure(); } }
harmonyの動機2 auto f() -> std::optional<int>; // ポインタ型 auto g() ->
int*; int main () { // こう書けたとしたら f() | and_then([](int&) { ... }) | and_then([](int&) { ... }) | or_else([]() { ... }); // こう書きたくないですか? g() | and_then([](int&) { ... }) | and_then([](int&) { ... }) | or_else([]() { ... }); } auto f() -> std::optional<int>; // std::expected auto g() -> std::expected<int, std::errc>; int main () { // expectedでも同じように書きたくないですか? g() | and_then([](int&) { ... }) | and_then([](int&) { ... }) | or_else([](std::errc) { ... }); } • モナド的に扱いたいのはstd::optionalだけですか?
harmonyの動機付け • std::optionalのnullチェックを隠蔽したい! • コードの可読性の向上 • 条件記述ミスやチェック忘れの回避 • 便利な意味論を持つ、モナド的操作をしたい!! •
コード可読性の向上 • 条件記述ミスを減らす • 同じことを同じように書きたい!!! • 異なる型に対して同じ操作で同じことを行いたい • コードの可読性の向上 • ライブラリの利便性向上
おしながき 1. harmony について 1. 動機付け 2. harmonyのMonadic Interface 2.
harmonyのコンセプト、その実装 1. harmonyの対象とコンセプト 2. harmonyのコンセプト、実装 3. コンセプトベースの設計について
monas | func • harmony::monasで包み|で後続処 理を接続する • monasはラッパ型、`|`による パイプを提供する •
optionalに対して|で接続された 処理は、有効値を保持している場 合にのみ実行される • monas | funcの結果は、また monasに写される #include "harmony.hpp" auto f() -> std::optional<int>; auto func1(int&) -> std::optional<int>; auto func2(int&) -> int; auto func3(int&) -> void; int main() { harmony::monas(f()) | func1 | func2 | func3; }
Monadic Interface - map/map_err • mapは有効値を変換し、 map_errは無効値を変換する • 変換は指定された処理(関数) によって行われる
• それぞれ、有効値を保持して いる時/無効値を保持してい る時にしか変換しない • 結果はまたmonasに写される auto to_string(int&) -> std::string; auto error(std::nullopt_t) -> int; auto func1(std::string_view) -> void; auto func2(int) -> void; int main () { using namespace harmony::monadic_op; harmony::monas(f()) | map(to_string) // int -> std::string | func1; harmony::monas(f()) | map_err(error) // std::nullopt_t -> int | func2; }
Monadic Interface - and_then/or_else auto to_string(int&) -> std::optional<std::string>; auto error(std::nullopt_t)
-> std::optional<int>; auto func1(std::string_view) -> void; auto func2(int) -> void; int main () { harmony::monas(f()) | and_then(to_string) | func1; harmony::monas(f()) | or_else(error) | func2; } • and_then/or_elseは値を包 む型ごと変換を行う • 例えば、optional->expected の様な変換が可能なほか、無 効値(有効値)から有効値 (無効値)の様な変換が可能 • 変換先の型は変換した値とし ない値両方を表現できなけれ ばならない
Monadic Interface - match • matchは有効値と無効値両方 に対する処理を指定する • 戻り値は1つの型に集約され た値
• 有効値と無効値に対する処理結 果の間にcommon_typeが必要 • そのcommon_typeがモナド的型 であるとき、結果はまたmonas に写される • これさえあれば他いらない説 がある、とても便利 auto f() -> std::optional<int>; auto to_string(int&) -> std::string; auto error(std::nullopt_t) -> std::string; auto func(std::string_view) -> void; int main () { harmony::monas(f()) | match(to_string, error) | func; }
Monadic Interface – その他色々 • try_catch • 例外->eitherモナドへの変換 • map_to
• 値への直接変換 • fold_to • 有効値と無効値を集約しつつ変換する • value_or • 有効値を取り出し、なければ代わりの値を返す • harmonize • モナド的とみなせない型をeitherモナド的に扱えるようにする • 例えばbool型でmatchとかできるようにする
harmony • Githubに公開しています • onihusube/harmony: C++ Monadologie (github.com) • ヘッダオンリー
• 要C++20コンパイラ • GCC 10.3/11.1 • MSVC 2019 latest/MSVC 2022 • Clang 12.0?
おしながき 1. harmony について 1. 動機付け 2. harmonyのMonadic Interface 2.
harmonyのコンセプト、その実装 1. harmonyの対象とコンセプト 2. harmonyのコンセプト、実装 3. コンセプトベースの設計について
harmonyの対象 • harmonyの対象はstd::optionalだけではない • std::optional • ポインタ型/スマートポインタ型 • コンテナ型(range) •
巷のresult-likeな型 • boost::outcome/boost::outcome::resultとか • std::expected (C++23?) • std::future/std::shared_future • boostにある同等のものも • std::error_code • ただし、意味論が他と逆 • その他… • Harmonyはこれらの型をコンセプトを通して扱うことで、型の持つ 意味論の一部にのみ着目し統一的に扱う
harmonyの対象とコンセプトの対応 • unwrappable コンセプト • maybe コンセプト • either コンセプト
result<T, E> expected<T, E> variant<L, R> • list コンセプト rangeコンセプトを満たすもの T* unique_ptr<T> optional<T> future<T>
おしながき 1. harmony について 1. 動機付け 2. harmonyのMonadic Interface 2.
harmonyのコンセプト、その実装 1. harmonyの対象とコンセプト 2. harmonyのコンセプト、実装 3. コンセプトベースの設計について
コンセプト - unwrappable template<typename T> concept unwrappable = requires(T&& m)
{ { cpo::unwrap(std::forward<T>(m)) } -> not_void; }; • unwrap CPOによって値を取得可能であること! • そして、結果がvoidではないこと
CPO? • CPO = Customization Point Object • いくつものカスタマイゼーションポイントの集積点となる関数 オブジェクト
• カスタマイゼーションポイントにまつわるいくつもの問題に対 処した、C++20以降必修のイディオム • 同じ意味論に対応する操作が複数存在しているとき、それをま とめ上げるのにCPOを利用できる
CPO - unwrap • unwrap CPOは次のいずれかの手段によって型の内包する値を 取り出す 1. oprator* :
ポインタ型、std::optional等 2. .value() : std::expected<T, E> 3. .unwrap() : 在野のresult/expected-likeな型 4. std::views::all() : range 5. get<1>() : variant<L, R> 6. .get() : future<T>
unwrappable 再掲 template<typename T> concept unwrappable = requires(T&& m) {
{ cpo::unwrap(std::forward<T>(m)) } -> not_void; }; • この1つの制約式には、少なくとも6つの操作に対する要求が含 まれている • このコンセプトを使用する所ではunwrap CPOを利用すること で、統一的な意味論の下で複雑な手続きなしに6種の操作にア クセスできる
CPO -> コンセプト • コンセプト定義においては、CPOを利用することで対応する複 数の操作に関する制約をシンプルに表現できる • そのコンセプトを利用する所では、そのCPOを用いることでコ ンセプトの枠内で型に応じた最適な操作を自動で選択できる •
CPOによる操作の統一とそれを用いたコンセプト定義は、 C++20 Rangeライブラリのrangeコンセプトにも見ることがで きる
コンセプト - maybe template<typename T> concept maybe = unwrappable<T> and
requires(const T& m) { { cpo::validate(m) } -> std::same_as<bool>; }; • unwrappableかつ、validate CPOによって有効性(内包する値の有無) を取得可能であること!
CPO - validate • validate CPO (いいお名前募集中)は次のいずれかの手段に よって型の内包する値の存在を判定する • boolへの明示的変換
: ポインタ型、std::optional等 • .has_value() : std::expected等 • .is_ok() : 在野のresult/expected-likeな型 • std::ranges::empty() : range • .index() == 1 : variant<L, R> • .valid() : future<T>
コンセプト - either template<typename T> concept either = maybe<T> and
requires(T&& t) { {cpo::unwrap_other(std::forward<T>(t))} -> not_void; }; • maybeかつ、unwrap_other CPOによって無効値を取得可能で あること!
CPO – unwrap_other • unwrap_other CPOは次のいずれかの手段によって型の内包す る無効値(あるいはもう片方の値)を取得する • std::nullopt :
std::optional • nullptr : ポインタ型・スマートポインタ型 • .error() : std::expected • .unwrap_err() :在野のresult/expected-likeな型 • get<0>() : variant<L, R>
|の簡易実装例 tempalte<typename M> class monas { M m_monad; public: template<typename
F> friend constexpr auto operator|(monas& self, F&& f) -> monas<T>& { self.m_monad = f(cpo::unwrap(self.m_monad)); return self; } template<typename F> requires maybe<M> friend constexpr auto operator|(monas& self, F&& f) -> monas<T>& { if (cpo::validate(self.m_monad)) { self.m_monad = f(cpo::unwrap(self.m_monad)); } return self; } };
mapの簡易実装例 template<typename T> struct map_impl { T fmap; template<unwrappable M>
friend constexpr auto operator|(M&& m, map_impl self) { return monas(self.fmap(cpo::unwrap(std::forward<M>(m)))); } template<either M> friend constexpr auto operator|(M&& m, map_impl self) { using R = /* 変換後の型を取得 */; if (cpo::validate(m)) { return monas<R>(self.fmap(cpo::unwrap(std::forward<M>(m)))); } else { // 無効値の変換処理 return monas<R>(cpo::unwrap_other(std::forward<M>(m))); } } };
おしながき 1. harmony について 1. 動機付け 2. harmonyのMonadic Interface 2.
harmonyのコンセプト、その実装 1. harmonyの対象とコンセプト 2. harmonyのコンセプト、実装 3. コンセプトベースの設計について
コンセプトベースライブラリデザイン • まずライブラリ対象についてをコンセプトを用いて定義する • コンセプトによってある概念に対応する型の性質を記述する • ライブラリの処理はコンセプトにのみ依存して記述する • 型の具体性に依存しないためジェネリックに書ける •
想定している具体的な対象以外に、幅広い型にライブラリを開放でき る • 型の具体性に踏み込む場合、それをもコンセプトで表現する • コンセプトの包含関係を意識してコンセプトを定義し それを用いて処理を特殊化すれば、特殊化対象を自動的に絞り込める
コンセプトベースライブラリデザイン • やっていることは、数十年前から言われているSOLID原則とか その辺の抽象設計のおはなし • たとえば、原初のSTLにも見ることができる • C++20からはコンセプトを中心とした設計によって、それらの 原則をこのように書くことができる •
これらの設計は、Rangeライブラリ及びExecutorライブラリ (提案中)に特に見ることができる
おわり • コンセプトはいいぞ! • C++20はいいぞ!!