От переводчика
Это вольный перевод статьи о том, почему люди боятся функциональных языков, присутствует ирония и юмор, для тех кто может и любит читать на английском оригинал здесь.Про автора
Я разработчик и архитектор британской не софтверной компании. Имею 20 летний опыт в различных областях от высоко-уровневых UX/HCI до низко-уровневых реализаций баз данных.
Несмотря на то что я использую C# в своей повседневной работе, я писал промышленный код на многих языках, мои любимые Smalltalk, Python и совсем недавно F# (поэтому я и создал этот сайт).
Разглагольствования по поводу того чего я не понимаю
Вам надоела вся эта шумиха вокруг функциональных языков? Мне тоже! Я выскажу по этому поводу несколько причин почему разумные люди, такие как мы с вами, должны держаться подальше от этого.
Немного поясню: когда я говорю «статически типизированный функциональный язык программирования», я подразумеваю языки, которые поддерживают вывод типов по-умолчанию и прочее. На практике это означает Haskell и семейство языком ML (включая OCaml и F#).
Причина 1. Я не хочу следовать последним тенденциям
Как и большинство программистов я консервативен и не люблю изучать новые вещи. Собственно поэтому я выбрал карьеру в IT.
Я не поддаюсь массовым увлечениям просто потому, что все «крутые парни» делают это, сначала я жду пока вещь дозреет, и я смогу увидеть кое-какие перспективы.
Для меня, функциональное программирование существует ещё слишком малое время, чтобы убедить меня, что оно здесь всерьёз и надолго.
Да я полагаю, что некоторые педанты будут утверждать, что ML и Haskell существуют почти столько же сколько старые фавориты Java и PHP, но я услышал про Haskell совсем недавно, так что этот аргумент совсем неубедительный.
Посмотрим ка на новичка в этой семье: F#. Господи, да ему всего лишь 7 лет! Конечно, это достаточное время для геолога, но в эпоху интернета, 7 лет — это просто мгновение ока.
Таким образом, я определённо был бы осторожным и подождал несколько десятилетий, чтобы посмотреть либо функциональное программирование останется, либо не оправдает надежд.
Причина 2. Мне платят за строки кода
Я не знаю как вы, но чем больше строк кода я написал, тем более продуктивным я себя чувствую. Если я смогу наштамповать 500 строк в день, то работа сделана хорошо. Мои коммиты большие и мой босс может видеть, что я был занят делом.
Но когда я сравниваю код написанный на функциональном языке с хорошим олдскульным C-подобным языком, то я вижу так мало кода что меня это пугает.
Просто посмотрите что я имею ввиду, вот код написанный на привычном языке:
public static class SumOfSquaresHelper
{
public static int Square(int i)
{
return i * i;
}
public static int SumOfSquares(int n)
{
int sum = 0;
for (int i = 1; i <= n; i++)
{
sum += Square(i);
}
return sum;
}
}
а теперь сравните с этим:
let square x = x * x
let sumOfSquares n = [1..n] |> List.map square |> List.sum
17 строк против только 2-х строк. А теперь представьте разницу, если это умножить на размер проекта.
Если бы я использовал это, моя производительность бы резко упала. Извините, но я не могу себе этого позволить.
Причина 3. Я люблю фигурные скобки
И ещё одна вещь. Что происходит со всеми этими языками, которые избавились от фигурных скобок? Как они могут называться настоящими языками программирования?
Я покажу вам, что я имею ввиду. Вот пример кода с привычными фигурными скобками.
public class Squarer
{
public int Square(int input)
{
var result = input * input;
return result;
}
public void PrintSquare(int input)
{
var result = this.Square(input);
Console.WriteLine("Input={0}. Result={1}", input, result);
}
}
А вот похожий код, только без фигурных скобок.
type Squarer() =
let Square input =
let result = input * input
result
let PrintSquare input =
let result = Square input
printf "Input=%i. Result=%i" input result
Посмотрите на разницу! Я не знаю как вы, но что-то меня беспокоит во втором примере, как будто здесь не хватает чего-то важного.
Если быть честным, я чувствую себя немного потерянным без фигурных скобок.
Причина 4. Я люблю видеть явные типы
Сторонники функциональных языков утверждают, что вывод типов делает код чище, не давая вам всё время загромождать его определениями типов.
Вообще-то, между прочим, я хочу видеть объявления типов. Я чувствую себя неловко, если не знаю точный тип каждого параметра. Вот почему Java мой любимый язык.
Вот сигнатура функции какого-то ML-подобного кода. Определения типов не требуются, и всё выводится автоматически.
let GroupBy source keySelector =
...
А вот сигнатура функции для похожего кода на C#, с явными определениями типов.
public IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
...
Может быть я и в меньшинстве, но вторая версия мне нравиться гораздо больше. Для меня важно знать, что функция вернёт тип IEnumerable<IGrouping<TKey, TSource>>
.
Конечно, компилятор сделает проверку типов для вас и предупредит, если выявит несоответствие типов. Но зачем позволять компилятору делать эту работу если её могут сделать ваши
Хорошо я допускаю, что если вы используете generic'и, лямбды, функции которые возвращают функции и все эти новомодные штуки, тогда да, ваши определения типов действительно могут стать чрезмерно сложными и запутанными. И становится очень трудно ввести их правильно.
Но у меня есть лёгкое решение для этого — не используйте generic'и и не передавайте функции где попало, ваши сигнатуры станут намного проще.
Причина 5. Я люблю исправлять баги
Ничто не нравится мне так, как охота на мерзкие баги. Ну а если баг в продакшене, то это ещё лучше, ведь вы заодно станете героем, когда исправите его.
Но я прочитал, что в программах на статически типизированных функциональных языках гораздо сложнее допустить баг. Вот облом.
Причина 6. Я живу в отладчике
Кстати, об исправлении багов, я провожу значительную часть моего дня в отладчике, пошагово выполняя код. Да, я знаю, мне следует использовать юнит-тесты, но ведь проще сказать чем сделать? Ведь так?
В любом случае очевидно, что если ваш код на статически типизированном функциональном языке компилируется, то обычно он работает.
Я сказал, что вы должны потратить много времени чтобы привести типы в соответствие, но когда это сделано и код успешно компилируется, то нечего отлаживать. Ну и какое в этом удовольствие?
Эта причина приводит меня к следующей…
Причина 7. Я не хочу думать о каждой мелочи
Проверить типы, убедиться что всё в порядке, всё это звучит так утомительно для меня.
На самом деле, я слышал, что вы вынуждены думать обо всех возможных краевых случаях, и обо всех возможных ошибках вызванных неверными входными данными, и вообще о всём что может пойти не так. И вы должны сделать всё это в начале, вы не можете полениться и отложить всё это на потом.
Мне гораздо больше нравиться получить программу полностью (ну почти полностью) работающую по позитивному сценарию (где все возможные ошибки не учитываются), ну а уже потом исправлять баги по мере того как они появляются.
Причина 8. Я люблю проверять на null
Я очень добросовестно делаю проверки на null в каждом методе. Это даёт мне большое удовлетворение, я знаю, что в результате мой код полностью пуленепробиваемый.
void someMethod(SomeClass x)
{
if (x == null) { throw new NullArgumentException(); }
x.doSomething();
}
Ха-ха! Я просто пошутил. Конечно же, я не могу вставлять проверки на null везде, иначе бы я никогда не закончил ни один настоящий проект.
Кроме того, за свою практику я всего лишь раз сталкивался с серьёзной проблемой вызванной NullPointerException. И бизнес потерял не так уж много денег, в течении нескольких недель которые я потратил на поиск проблемы. Так что я не уверен, что это такое уж необходимое дело.
Причина 9. Я везде применяю шаблоны проектирования
Впервые я прочитал о шаблонах проектирования в книге Head First Design Patterns (на которую почему-то ссылаются как на «книгу Банды четырёх», но я не уверен почему), и с тех пор я всегда прилежно использовал их для решения любых проблем. Уверен, что мой код после этого выглядит серьёзно, «энтерпрайзно» и это впечатляет моего босса.
Но я не вижу никаких упоминаний о шаблонах в функциональном проектировании. Как я могу сделать что-нибудь полезное без использования Стратегии, Абстрактной Фабрики, Декоратора, Прокси и всего остального?
Возможно функциональные программисты не знают о шаблонах?
Причина 10. Слишком много математики
Вот код для вычисления суммы квадратов. Этот способ записи слишком сложно понять из-за всех этих странных символов.
ss=: +/ @: *:
Ой, извините! Я ошибся, это был код на J.
Но зато я слышал, что функциональные языки используют странные символы типа <*>
и >>=
, или непонятные концепции типа «монад» и «функторов».
Я не знаю почему функциональные программисты не смогли придерживаться уже известным мне вещей, таких очевидных как символы типа ++, != и лёгких концепций типа «наследование» и «полиморфизм».
Итог. Я не понимаю этого
Вы знаете, я не понимаю этого. Я не понимаю, чем функциональное программирование полезно.
Я очень хочу чтобы кто-нибудь просто показал мне настоящие преимущества на одной странице, вместо того чтобы заваливать меня информацией.
Обновление: Хорошо, сейчас я прочитал статью «все что вам нужно знать на одной странице». Но она очень короткая и простая для меня.
Всё таки я ищу что-то немного более глубокое, что-то с чём я мог бы продуктивно поработать.
И нет, не говорите мне, что мне следует прочитать руководства, поиграть с примерами и написать свой собственный код. Я просто хочу посмотреть, не делая всего этого.
Я не хочу менять своё мышление, просто для того чтобы изучить новую парадигму.
Для тех, кто думает, что я преувеличиваю или троллю. Всё в этой статье основано на том, что я прочитал или лично услышал от других людей (лишь с небольшим преувеличением). Я просто сделал явным то, что было неявным в их реакции.
Так же поясню, я не пытаюсь быть снобом или гнобить тех, кто предпочитает не использовать функциональное программирование. Люди могут использовать те инструменты, которые им нравятся. И есть множество хороших причин не использовать функциональные языки, те же причины которые привёл я, с этим не связаны. Я сталкиваюсь с подобными защитными и нерациональными «аргументами» всё время, и их не стоит воспринимать всерьёз.
Некоторые предпосылки по каждому пункту:
Причина 1: «мода». Не могу сказать сколько раз я это слышал. Пожалуйста почитайте немного о истории computer science. «Я консервативен и не люблю изучать новые вещи. Собственно поэтому я выбрал карьеру в IT.» Я думал это должно быть смешно. Ну ладно.
Причина 2: «оплата за строки кода». Никто не говорил этого прямо, но если вы посмотрите на любое обсуждение, скажем, Python vs Java, некоторым людям определённо комфортней писать длинный и скучный код.
Причина 3: «фигурные скобочки». Речь не о фигурных скобках как таковых. Многие люди утверждают, что отсутствие фигурных скобок делает код нечитаемым, и вообще они к ним привязаны. Вот комментарий к этой самой статье, который показывает мою точку зрения.
Причина 4: «я люблю явные типы». Основано на реальных комментариях
Причина 5: «я люблю исправлять баги» и причина 6: «я живу в отладчике». Опять же никто не говорил этого напрямую, но слабое понимание инструментов статического анализа кода говорит об этом. Людей сложно заставить писать даже юнит-тесты. Я думаю, что прерывистое подкрепление играет неявную роль в этом поведении.
Причина 7: «я не хочу думать о каждой мелочи». Никто из нас этого не делает. Вот почему мы должны использовать инструменты, которые не позволяют нам быть ленивыми. Иначе всё может закончиться для вас как одна из историй с The Daily WTF.
Причина 8: «я люблю проверять на null». Почитайте любые дискуссии по поводу полезности опциональных типов и вскоре вы найдёте людей говорящих что проверка на null это не проблема.
Причина 9: «шаблоны проектирования везде». Почти дословно. Увы.
Причина 10: «слишком много математики». Ещё одна распространённая причина, и в какой-то мере я согласен (посмотрите мою просьбу на этом сайте). Но нравится вам это или нет теоретическая часть computer science очень важна. SQL основан на теории. Лямбды основаны на теории. Алгоритмы для множеств, графов, шифрования и всё остальное тоже основано на теории. Количество математиков сделавших вклад в computer science огромно.
Для практического использования SQL, для манипуляции с множествами, вы не обязаны глубоко знать математику. По сути, «математика» которую вы должны изучить для функционального программирования не намного сложнее, чем принципы SOLID в ООП. Просто это разные вещи.
Итог. Я знаю, что изучение функционального программирования это сложно, и это требует некоторых изменений в том, как вы думаете о коде. Вот почему я сделал этот сайт, чтобы помочь тем, кому любопытно и кто хочет учиться.
А если вы не хотите изучать что-то новое, что ж, это тоже хорошо.
Только пожалуйста, не отвергайте того что вы никогда не пробовали основываясь на неубедительных доводах, или вы можете закончить как этот парень.
Автор: DarkCoder