我如何从我的 maybe<> monad 中获取价值?

How do I get the value OUT of my maybe<> monad?

出于教育原因,我正在尝试在 C++14 中实现一个 maybe monad。我(也许过于简单化)对 monad 的理解是,它们允许您将计算定义为一系列可组合的函数调用。维基百科关于 monads 的文章称它们为 "programmable semicolons" 因为它们可以让你定义在一组谨慎的函数调用之间会发生什么。 maybe monad 是一个在发生故障时中断计算的 monad。

template<class T>
struct maybe
{
    maybe( const T& t ) : argument( t ), valid( true ) {}
    maybe() : argument(), valid( false ) {}

    T argument;
    bool valid;
};

template<class T>
maybe<T> just( const T& t ) { return maybe<T>(t); }

template<class T>
maybe<T> nothing() { return maybe<T>(); }

auto terminal_maybe = [] ( auto term ) {
    return [=] ( auto func ) {
        return func( term );
    };
};

auto fmap_maybe = [] ( auto f ) {
    return [=] ( auto t ) {
        if( t.valid ) {
            try {
                t.argument = f( t.argument );
                printf("argument = %d\n",t.argument);
            }
            catch(...) {
                t.valid = false;
            }
        }

        return (t.valid) ? terminal_maybe( just( t.argument ) ) : terminal_maybe( nothing<decltype(t.argument)>() );
    };
};

int main( int argc, char* argv[] )
{
    auto plus_2 = [] ( auto arg ) { return arg + 2; };
    auto minus_2 = [] ( auto arg ) { return arg - 2; };

    maybe<int> forty = just(40);

    terminal_maybe(forty)
        (fmap_maybe( plus_2 ))
        (fmap_maybe( plus_2 ));

    printf("result = %d\n",forty.argument);

    return 0;
}

如你所见,我超级亲近!我可以单次将多个调用链接在一起(我可以从 printf 中看出我的值符合我的预期(从 40 增加到 42,然后从 42 增加到 44))。问题是我没有办法得到最终的价值!我尝试让 terminal_maybe 接受引用 (auto&),这迫使我修改 fmap 的 return 语句(只修改为 return terminal_maybe( t ) 而不是新的 maybe)。但它仍然没有最终 printf 的正确值。

这行得通,但我不知道从 FP 的角度来看是否有意义。

auto unwrap = [](auto const &f) {
    return f;
};

int main( int argc, char* argv[] )
{
    auto plus_2 = [] ( auto arg ) { return arg + 2; };
    auto minus_2 = [] ( auto arg ) { return arg - 2; };

    maybe<int> forty = just(40);

    auto const &outv = terminal_maybe(forty)
        (fmap_maybe( plus_2 ))
        (fmap_maybe( plus_2 ))
        (unwrap);

    std::printf("result = %d\n",outv.argument);

    return 0;
}