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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
120
JavaDoc 再入門
nagise
0
320
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
270
Vite+ Unified Toolchain for the Web
naokihaba
0
240
Webフレームワークの ベンチマークについて
yusukebe
0
160
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
150
New "Type" system on PicoRuby
pocke
1
810
Signal Forms: Beyond the Basics @ngBaguette 2026 in Paris
manfredsteyer
PRO
0
230
Copilot CLI の継戦能力を高める コンテキスト管理
nozomutu
1
1.2k
「エンジニアインターン、どうやって取った?」準備のリアルを語るLT会 Progate BAR
akiomatic
0
130
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
490
net-httpのHTTP/2対応について
naruse
0
470
Featured
See All Featured
Mobile First: as difficult as doing things right
swwweet
225
10k
エンジニアに許された特別な時間の終わり
watany
107
250k
4 Signs Your Business is Dying
shpigford
187
22k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
200
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.3k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
330
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
440
How Software Deployment tools have changed in the past 20 years
geshan
0
34k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.4k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
200
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はいいぞ!!