6

std::transform から <algorithm> ヘッダ 範囲に適用され、それが範囲をファンクターとして使用することを「可能にする」ものです (圏論の意味で(¹))。 std::transform はい、イテレータベースですが、 std::ranges::views::transform はそうではなく、その署名は関数型言語の対応する関数の署名とほぼ一致します (2 つの引数の順序が異なるモジュロですが、これは大した問題ではありません)。

私が見たとき この質問 (そしてその過程で それに答える)、C++23 で導入されていることを知りました。 std::optional<T>::transform、これにより std::optional ファンクターも。

これらすべてのニュースは本当に私を興奮させますが、ファンクターは一般的なものであり、統一されたインターフェースを持っているといいだろうと思わずにはいられません。 transform たとえば、Haskell の場合のように、任意のファンクター。

これは、オブジェクトに似ていると思います std::ranges::views::transform (ほのめかしていない別の名前で ranges) にすることができます カスタマイズポイント STL は範囲だけでなく、 std::optional また、STL 内の他のファンクタに対しても、プログラマはユーザー定義クラス用にカスタマイズできます。

まったく同じように、C++23 でも導入されています。 std::optional<T>::and_then、これは基本的にのモナドバインディングです std::optional. 範囲のモナド結合を実装する同様の関数は知りませんが、C++20 の some_range | std::ranges::views::transform(f) | std::ranges::views::join は本質的に のモナド束縛です some_rangef.

そして、これは、いくつかの一般的なインターフェースが存在する可能性があると私に思わせます。名前を付けます mbind、任意のタイプでオプトインできます。 STL は、 std::optional の観点からそれを実装することによって std::optional<T>::and_then、 例えば。

言語がいつの日かそのような汎用性をサポートする可能性はありますか、または計画はありますか?


私は確かにいくつかの問題を見ることができます。 今日 std::ranges::views::transform(some_optional, some_func) は無効なので、一部のコードは SFINAE 経由でそれに依存している可能性があります。 突然動作させると、コードが壊れます。


(¹) 言葉に関しては オペレーター、圏論で与えられた定義を参照します (以下も参照)。 これ)、「持つクラスのオブジェクト」の概念ではありません operator() 定義された”; 後者は標準のどこにも定義されていません そして言及さえされていません cppリファレンス、代わりに用語を使用します 関数オブジェクト 参照する

関数呼び出し演算子の左側で使用できるオブジェクト

6

  • これにより、 std::optional もファンクターになります。「これはどうやって作るの? optional それは機能しますか?

    1 月 9 日 11:12

  • オプションの値を表すデータ型 ファンクタ。 Haskell はそれに名前を付けます Maybe、C++ で名前を付ける std::optional. Haskell はそれがファンクターであることを実装することによってエンコードします fmap 為に Maybe秒。 C++ではこれがあります std::optional<T>::transform これは同じ目的を果たしますが、Haskell のものとの唯一の違いです。 fmap それは memebr 関数です。

    1 月 9 日 11:20

  • これらの変更を採用することが不可能/難しいと思わせる技術的な理由はありますか?

    平均値

    1 月 9 日 11:38

  • @康煥魏はありません 断定的な意味で 曖昧さを解決する?

    1 月 9 日 11:59

  • @idmean、考えられる問題の1つについて言及しました。 一部のコードの非有効性に依存する SFINAE コードは機能しなくなります。

    1 月 9 日 13:32

1

範囲のモナド結合を実装する同様の関数は知りませんが、C++20 の some_range | std::ranges::views::transform(f) | std::ranges::views::join は本質的に のモナド束縛です some_rangef.

ranges::views::for_each 範囲のモナドバインド (読んだ)、そして私はそれを好む views::transform | views::join range-v3 の (過去の?) 複雑さ/入力の問題が原因です。

Functor と Monad のジェネリック インターフェイスを取得するかどうかについて。 そのような汎用性がテンプレートを作成するライブラリ作成者にとって有用でない限り、私はそれを疑っています。 std::expiremental::future もモナドです(そして、エグゼキュータもそうであると思います)ので、次のような一般的なアルゴリズムを書くことができます foldM この 3 つのタイプについて説明します。 Erik Niebler が range-v3 で、すべてのパイプ演算子を手作業でコーディングすることを犠牲にして Functor/Monad ライブラリを作成できることを示したと思います。

#include <fp_extremist.hpp>
template <typename M> requires Monad<M>
auto func(M m)
{
    return m
        | fp::extremist::fmap([](auto a) { return ...; })
        | fp::extremist::mbind([](auto b) { return ...; })
        ;
}

私が実際に可能だと思うのは、UFCS と |> 演算子を使用して、次の利点を得ることができます invocable |> east 構文とアルゴリズムにパイプする機能。 から バリーのブログ:

範囲アダプターはパイプ可能ですが、アルゴリズムはそうでないため、機能しません。 …

つまり、x |> f は以前と同様に f(x) として評価されますが、x |> f(y) は f(x, y) として評価されます。

PS c++ で Functor の定義を与えることは難しくありません。 <typename T> struct 提供する transform.

PSS
編集: applicative の扱い方に気づきました。

Applicative<int> a, b, c;
auto out = a
    | zip_transform(b, c
        , [](int a, int b, int c){ return a + b + c; })
    ;

zip_transform ジッパー付きだから abcApplicative<std::tuple<int, int, int>> そしてそれを変換します(考えてください optionalfuturerange)。 部分的に適用された関数の Applicative をいつでも使用できますが、それには C++ のスタイルではない多数のネストされた関数が含まれ、上から下への読み取り順序が乱れます。

    あなたの答え

    ゲストとして投稿する

    必須ですが表示されません


    「回答を投稿する」をクリックすることにより、あなたは私たちの 利用規約プライバシーポリシークッキーポリシー

    あなたが探している答えではありませんか? タグ付けされた他の質問を閲覧する c++ 関数型プログラミング モナド オペレーター c++23 また 自分の質問をする.