📜 ⬆️ ⬇️

Functional programming is unpopular because it is strange.

I know people who are genuinely perplexed that functional programming is not very popular. For example, I am currently reading the book Out of the Tar Pit, in which the authors, after arguments in favor of functional programming, say:

Nevertheless, the fact remains: these arguments were not enough for functional programming to become widespread. Therefore, we must conclude that the main drawback of functional programming is the flip side of its main virtue, namely that problems arise when (as is often the case) the system being designed must maintain some sort of state.

And I think that the reason for the lack of popularity is much simpler: programming in the functional style often happens “backwards” and looks more like solving puzzles than explaining the problem to a computer. Often, when I write in a functional language, I know what I want to say, but in the end I solve puzzles in order to express it in the language. In short, functional programming is simply too unnatural.

To further discuss functional programming, let's try to bake a cake. Take the recipe from here . Like this, we will bake an imperative cake:
  1. Preheat oven to 175 ° C. Grease with butter and sprinkle with flour baking sheet. In a small bowl, mix flour, baking soda, and salt.
  2. In a large bowl, beat butter, granulated sugar, and brown sugar until the mass is light and airy. Beat the eggs one at a time. Add bananas and grind to a smooth consistency. Alternately add to the resulting creamy mass the basis for the dough from p. 1 and kefir. Add chopped walnuts. Put the dough in the prepared baking sheet.
  3. Bake in preheated oven for 30 minutes. Remove the baking tray from the oven, put on a towel so that the cake is cold.

I allowed myself a few liberties with numbering (obviously, every step is actually a few steps), but let's better see how we will bake a functional cake:
  1. A pie is a hot pie cooled on a towel, where a hot pie is a prepared pie baked in a preheated oven for 30 minutes.
  2. A preheated oven is an oven preheated to 175 ° C.
  3. The prepared cake is the dough laid out in the prepared baking sheet, where the dough is the creamy mass to which chopped walnuts are added. Where the cream mass is butter, granulated sugar and brown sugar, whipped in a large bowl until they are light and airy, where ...

Oh, to hell with it — I can't finish it! ( comment. transl. in fact, if you follow the logic, even the above points should be even more difficult). I do not know how to transfer these steps to a functional style without using a variable state. Either the sequence of steps is lost, or it is necessary to write “add bananas”, but then the current state changes. Maybe someone in the comments will end? I would like to look at the versions using monads and without using monads.
In the comments to the original article offered several options.
There were no monads, but there was a pipe forward operator.

Without using pipe forward operator:
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), , .

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

. , ++ — , , , , .

, , ? . . . , .

')

Source: https://habr.com/ru/post/303312/


All Articles