関数型プログラミングは奇妙だから人気がない

関数型プログラミングはあまり人気がないと心から戸惑う人々を知っています。 たとえば、今、Out of the Tar Pitという本を読んでいます。著者は、関数型プログラミングを支持する議論の後に、次のように述べています。



それにもかかわらず、事実は残ります。これらの議論は、関数型プログラミングが普及するのに十分ではありませんでした。 したがって、関数型プログラミングの主な欠点はその主な利点の裏返しであると結論付けなければなりません。つまり、設計されたシステムが何らかの状態を維持しなければならない場合に問題が発生します。



そして、人気がない理由ははるかに単純だと思います。機能的なスタイルでのプログラミングはしばしば「バックツーフロント」で行われ、コンピューターにタスクを説明するよりもパズルを解くように見えます。 多くの場合、関数型言語で書くとき、私は言いたいことを知っていますが、最終的には言語を使用してそれを表現するパズルを解きます。 要するに、関数型プログラミングは単に不自然すぎます。



関数型プログラミングについてさらに議論するために、ケーキを焼いてみましょう。 ここからレシピ取ります 。 このようなものは、必須のケーキを焼きます:

  1. オーブンを175°Cに予熱します。 天板に油を振りかけます。 小さなボウルに、小麦粉、重曹、塩を混ぜます。

  2. 大きなボウルで、バター、グラニュー糖、黒糖を軽く固めます。 卵を一度に一つずつ打ちます。 バナナを追加し、滑らかになるまでこする。 代わりに、得られたクリーミーな塊に、アイテム1の生地とケフィアのベースを追加します。 みじん切りのクルミを追加します。 準備したパンに生地を入れます。

  3. 予熱したオーブンで30分間焼きます。 オーブンからフライパンを取り出し、タオルの上に置いてケーキを冷やします。



私は番号付けでいくつかの自由を許可しました(明らかに、各ステップは実際には複数のステップです)が、機能的なケーキをどのように焼くかを見てみましょう:

  1. パイはタオルで冷やされたホットケーキで、ホットケーキは準備されたケーキで、予熱されたオーブンで30分間焼かれます。

  2. 予熱されたオーブンは、175°Cに予熱されたオーブンです。

  3. 準備されたパイは、準備された天板に配置された生地であり、生地は、砕いたクルミが追加されたクリーミーな塊です。 クリーミーな塊がバター、グラニュー糖、黒糖である場合、軽くて風通しが良くなるまで大きなボウルにホイップします...



ああ、それで地獄に—私はそれを終えることができません! (実際、ロジックに従うと、与えられたポイントでさえさらに複雑になるはずです)。 可変状態を使用せずにこれらの手順を機能的なスタイルに移行する方法がわかりません。 ステップのシーケンスが失われるか、「バナナを追加」と記述する必要がありますが、現在の状態は変わります。 コメントの誰かが終了するかもしれませんか? モナドを使用し、モナドを使用しないバージョンを調べたいです。

元の記事へのコメントでいくつかのオプションを提案しました
モナドはありませんでしたが、パイプフォワード演算子の有無にかかわらず。



パイプフォワード演算子を使用しない場合:

cake = cooled(removed_from_oven(added_to(30min, poured(greased(floured(pan)), stirred(chopped(walnuts),
alternating_mixed(buttermilk, whisked(flour, baking soda, salt), 
mixed(bananas, beat_mixed(eggs, creamed_until(fluffy, butter, white sugar, brown sugar)))), 
preheated(175C, oven))))))

      
      





C pipe forward operator:

cake = bake(cake_mixture, 30min, prepare(pan, (grease, flour)), preheated(175C, oven))
where cake_mixture =
creamed :until_fluffy ‘butter’ ‘white’ ‘sugar’ ‘brown sugar’
|> beat_mixed_with ‘eggs’
|> mixed_with ‘bananas’
|> mixed_with :alternating ‘buttermilk’ ‘dry_goods’
|> mixed_with chopped ‘walnuts’
where dry_goods = whisked ‘flour’ ‘baking soda’ ‘salt’

      
      







- , . , , . , , , . . , . . , , , . ( . , .)



, . , ++ — . ++ , , , , . , . ( , , yacc bison , boost spirit, , , .)

ParseResult<V> VParser::parse_impl(ParseState state)
{
    ParseResult<A> a = a_parser.parse(state);
    if (ParseSuccess<A> * success = a.get_success())
        return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
    ParseResult<B> b = b_parser.parse(state);
    if (ParseSuccess<B> * success = b.get_success())
        return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
    ParseResult<C> c = c_parser.parse(state);
    if (ParseSuccess<C> * success = c.get_success())
        return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
    ParseResult<D> d = d_parser.parse(state);
    if (ParseSuccess<D> * success = d.get_success())
        return ParseSuccess<V>{{std::move(success->value)}, success->new_state};
    return select_parse_error(*a.get_error(), *b.get_error(), *c.get_error(), *d.get_error());
}

      
      





variant type V, A, B, C D.
. .
, variant type « / », : V A, B, C, D. ( ).
, . : . C++, -, - , , , :

template<typename Variant, typename... Types>
ParseResult<Variant> parse_variant(ParseState state, Parser<Types> &... parsers)
{
    boost::optional<ParseError> error;
    template<typename T>
    for (Parser<T> & parser : parsers)
    {
        ParseResult<T> result = parser.parse(state);
        if (ParseSuccess<T> * success = result.get_success())
            return ParseSuccess<Variant>{{std::move(success->value)}, success->new_state};
        else
            error = select_parse_error(error, *result.get_error());
    }
    return *error;
}
ParseResult<V> VParser::parse_impl(ParseState state)
{
    return parse_variant<V>(state, a_parser, b_parser, c_parser, d_parser);
}

      
      





- , , . , ++. , . :

template<typename Variant, typename First>
ParseResult<Variant> parse_variant(ParseState state, Parser<First> & first_parser)
{
    ParseResult<First> result = first_parser.parse(state);
    if (ParseSuccess<First> * success = result.get_success())
        return ParseSuccess<Variant>{{std::move(success->value)}, success->new_state};
    else
        return *result.get_error();
}
template<typename Variant, typename First, typename... More>
ParseResult<Variant> parse_variant(ParseState state, Parser<First> & first_parser, 
    Parser<More> &... more_parsers)
{
    ParseResult<First> result = first_parser.parse(state);
    if (ParseSuccess<First> * success = result.get_success())
        return ParseSuccess<Variant>{{std::move(success->value)}, success->new_state};
    else
    {
        ParseResult<Variant> more_result = parse_variant<Variant>(state, more_parsers...);
        if (ParseSuccess<Variant> * more_success = more_result.get_success())
            return std::move(*more_success);
        else
            return select_parse_error(*result.get_error(), *more_result.get_error());
    }
}
ParseResult<V> VParser::parse_impl(ParseState state)
{
    return parse_variant<V>(state, a_parser, b_parser, c_parser, d_parser);
}

      
      





, , . , , , , … std::tuple<std::reference_wrapper<Parser>…>. - ( variadic sized std::tuple), , .



, : , . , . . . , . - . . . , , , , , .



. , ++ — , , , , .



, , ? . . . , .



All Articles