вторник, 15 октября 2013 г.

Мысли об ФП

ФП - это программирование без побочных эффектов. На этом определение ФП заканчивается. А лямбды, функции как объекты первого класса, композиция и прочие функциональные штуки - это всё второстепенно.

Вот вполне себе функциональный код на Java:

String reverseString(String str) {
    if (str.length() <= 1) {
        return str;
    } else {
        return
        str.charAt(str.length() - 1) +
        reverseString(str.substring(1, str.length() - 1)) +
        str.charAt(0);
    }
}

Побочных эффектов нету, соблюдается referential transparency.
Проблемы начинаются, когда код становится чуть сложнее, чем просто инвертирование строки. Попробуем написать функцию, которая инвертирует строки текста:

String reverseText(String text) {
    String[] lines = text.split("\n");
    String[] invertedLines = reverseLines(lines, 0, new String[0]);
    return Joiner.on("\n").join(invertedLines);
}

String[] reverseLines(String[] lines, int i, String[] invertedLines) {
    if (lines.length - i == 0) {
        return invertedLines;
    } else {
        return reverseLines(lines, i + 1,
            ObjectArrays.concat(invertedLines, reverseString(lines[i])));
    }
}

Справились, но пришлось добавить вспомогательный метод reverseLines. Естественным образом возникает мысль, что неплохо было бы его спрятать внутрь reverseText. И тогда можно не передавать lines в метод reverseLines, потому что она и так будет видна через замыкание. Таким образом, в функциональном языке мы должны уметь определять функции внутри функций.

Теперь надо написать функцию, которая не просто инвертирует строки текста, а, скажем инвертирует и ещё переводит их в верхний регистр. Очевидно, что функция будет отличаться от предыдущей одной лишь заменой reverseString на reverseStringAndToUpperCase. А дублировать код плохо. Тогда возникает мысль, а можно ли как-нибудь абстрагировать функцию reverseString от инвертирования? Назовём её transformTextа функцию reverseString  будем передавать как аргумент. Вот и возникло желание иметь функции как объекты первого класса.

Далее, наверное не сильно хочется писать функцию reverseStringAndToUpperCase? Ведь у нас уже есть reverseString и toUpperCase. Неплохо было бы уметь писать что-нибудь вроде reverseString.andThen(toUpperCase). Т.е. ещё возникает необходимость композиции функций в нашем языке.

Таким образом, писать без побочных эффектов - это трудная задача. А функциональные языки предоставляют инструменты, с помощью которых эту задачу можно значительно упростить.

2 комментария:

  1. Хорошая статья.

    Я считаю, это правильный подход - объяснять функциональную парадигму, используя простые конструкции из императивных языков типа Джавы. Я пару лет назад тоже писал статью в подобном духе: http://habrahabr.ru/post/122919/

    -------
    https://twitter.com/eliah_lakhin

    ОтветитьУдалить