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
Claspは野良GASの夢をみるか
takter00
0
200
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
C# and C++ Interoperability - cho-dotnetnew
harukasao
0
290
技術的負債解消で開発者の未来を開く- AIの力でコード刷新
kmd2kmd
0
110
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
11
4.3k
Lemonade + Foundry Toolkit でお手軽アプリ開発
seosoft
1
360
The ROI of Quarkus for Spring Boot Applications
hollycummins
0
120
Vite+ Unified Toolchain for the Web
naokihaba
0
320
Oxcを導入して開発体験が向上した話
yug1224
4
320
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
550
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
8
3.3k
Oxlintのカスタムルールの現況
syumai
6
1.1k
Featured
See All Featured
sira's awesome portfolio website redesign presentation
elsirapls
0
280
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Color Theory Basics | Prateek | Gurzu
gurzu
0
370
A Soul's Torment
seathinner
6
3k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
Automating Front-end Workflow
addyosmani
1370
210k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
420
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
2
1.1k
Are puppies a ranking factor?
jonoalderson
1
3.6k
Statistics for Hackers
jakevdp
799
230k
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はいいぞ!!