Пользователь
0,0
рейтинг
18 августа 2013 в 23:13

Разработка → Десять причин не использовать статически типизированный функциональный язык программирования перевод

От переводчика
Это вольный перевод статьи о том, почему люди боятся функциональных языков, присутствует ирония и юмор, для тех кто может и любит читать на английском оригинал здесь.

Про автора

Я разработчик и архитектор британской не софтверной компании. Имею 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 в ООП. Просто это разные вещи.

Итог. Я знаю, что изучение функционального программирования это сложно, и это требует некоторых изменений в том, как вы думаете о коде. Вот почему я сделал этот сайт, чтобы помочь тем, кому любопытно и кто хочет учиться.

А если вы не хотите изучать что-то новое, что ж, это тоже хорошо.

Только пожалуйста, не отвергайте того что вы никогда не пробовали основываясь на неубедительных доводах, или вы можете закончить как этот парень.
Перевод: Scott W.
G-man @DarkCoder
карма
23,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (50)

  • +4
    Спасибо, достойный перевод, статья и правда наделала много шума какое то время назад ))! Кстати так же отличный тест на наличие ч.ю.
    • +3
      Спасибо, первый раз перевожу, очень старался.
  • +7
    Статья забавная. Но, думаю, зачастую проблема в не в том, что «я не хочу», а в том, что в ентерпрайзе не так просто перейти на функциональный язык в проекте, которому 5+ лет.
    • 0
      Я считаю в этом нет смысла вообще. Тот же LINQ сводит все преимущества ФП на нет.
      • +8
        Я большой любитель linq-а, и с вами частично согласен. Но, все-таки, он один не спасает от излишней многословности. Или, например, методы-расширения, которые также должны бы были привнести преимущества функциональщины, по факту не так хорошо работают из-за внушительного синтаксического шума.

        Но, в конечном счете, вопрос действительно сводится к тому, кому нравятся скобочки, а кому — нет.
      • +2
        LINQ вообще-то очень функциональная штука, гуглите «functional reactive programming». Пчелы против мёда прям.
        • +3
          Я не спорю. И по этому с C# слезать нет смысла.
          • +2
            Лично мне не хватает в C# вывода типов функций. Прямо ужас как не хватает. Композиции функций без этого писать тоска зеленая.
            • 0
              Вы о generic'ах? Это просто кривизна компилятора. Есть шанс что когда-нибудь это поправят.
              • 0
                Нет, я о том, что перед объявлением анонимной функции нельзя поставить var, необходимо явно указывать типы аргументов и результата.
      • +2
        А как же ADT?
        • –5
          Есть кортежи
          • +3
            Кортежи без сопоставления с образцом не юзабельны. Может вы про анонимные типы?

            И да, ADT — это несколько другое. Я бы указал на отличие, если бы понял в каком сценарии ADT и кортежи взаимозаменяемы.
          • +3
            Вынужден спросить, каким ФП вы пользовались для оценки преимуществ ФП?
            • 0
              Пытался осилить F#. Боюсь карма не выдержит больше натиска.
      • 0
        Сомневаюсь что именно «все». Но многие. Скорее это напоминает другой потенциально холиваристый тезис «что только не придумают, чтоб не заставлять людей изучать ФП»;-).
      • 0
        В том же LINQ не хватает очень многих вещей по сравнению с модулем Seq из F#. Seq.pairwise, Seq.scan, Seq.windowed, например.

        Это все можно достаточно легко добавить самому, но отсутствие их из коробки удручает.
  • +1
    Спасибо, порадовало
  • +2
    А я прочитал первые две причины и сразу же возмутился, собрался уже было писать комментарий с критикой статьи. Потом перестал тупить и ещё раз перечитал предисловие =D
    • 0
      Чтобы почувствовать то же самое, мне хватило прочитать заголовок, :)
  • +1
    Почувствовал себя Шелдоном.
  • 0
    Пункт 2 убил.
    Пункт 3 тоже неплох, тот ещё холивар ведь.
  • +8
    Прямо захотелось взять и продолжить ударными темпами изучение Хаскеля!
  • 0
    Конечно, это достаточное время для геолога, но в эпоху интернета, 7 лет — это просто мгновение ока.

    Этот абзац весьма спорный в оригинале, а автор перевода еще и усугубил эффект спорности, выбросив слово «возможно» после слова «конечно».
    • 0
      Пофиксил, здесь какое-то жёсткое преувеличение, но видимо в этом суть их юмора.
      • 0
        Я сейчас всё тщательно обдумал, и решил, что предложение вполне в духе общего сарказма статьи, но без слова «возможно» — явный перегиб палки.
      • +1
        В российской прессе цитировали несколько лет назад одного американского образовательного чиновника, что «Если английский был хорош для господа нашего Иисуса Христа, то он хорош и для меня, и для наших детей», так что поэтому в его (образовательном) раёне иностранные языки не изучаются. Думаю, они такого «воинствующего самоуверенного озарплаченного незнания» (типа кого волнует, что функциональный язык на самом деле LISP появился в 1958 году?) в исполнении своих соотечественников наблюдают раз в 100 больше и стёб и юмор над таким «озарплаченным незнанием» (в стиле «за что платят деньги — то и есть истина») давно стал частью их культуры.
  • +13
    А я вот думаю, что фигурные скобки всё же лучше, чем структурирование отступами. В отечественной печати мне доводилось видеть листинги кода на языке с «отступным структурированием», которые немало пострадали от того, что верстальщики книги не имели понятия о значимости количества начальных пробелов. Имели эти листинги такой вид, что впечатлительный читатель мог бы, вероятно, при виде их тотчас же уткнуть лицо в ладони и разрыдаться в голос. (Правда, впечатлительным людям в программировании вообще бывает тяжко.)

    А ещё я вот думаю, что код «ss=: +/ @: *:» выглядит довольно страшно. Авторы его наверняка вдохновлялися брэйнфаковскою записью кода.
    • 0
      Не страшно, а слишком абстрактно.
      • +1
        Ну, это выглядит почти как регулярка, а регулярки, они это — страшные..
        Под термином «страшно» следует понимать «не читабельно».
        • +1
          Регулярные выражения вынужденно имеют такую форму: для них главным достоинством является предельная (и даже запредельная) компактность, потому что их поневоле приходится засовывать между буквами и другими частями обыкновеннейших строковых данных.

          А вот когда проектанты языков программирования стремятся и остальным операторам придавать запредельно краткую форму, то получаются у них только очередные брэйнфаки, не более.
        • 0
          Что в регулярках добивает, так это отсутствие переменных или других каких-нибудь путей повторного использования;-)…
          • 0
            • 0
              Я не про это.
          • 0
            Ничто не мешает разбить регулярку на куски в виде переменных, дать этим кускам имена, и «повторно использовать» их конкатенацией. Не хватает только рекурсии.
            • 0
              Рекурсия есть, но не везде. Я знаю только о perl и PCRE. Учтите, что рекурсия в регулярных выражениях невозможна по определению (если не ошибаюсь, то использование ссылок ((a*)\1 для нахождения вхождения чётного количества a в строке), также невозможно). Только регулярные выражения в большинстве движков уже давно не регулярные.
              • 0
                Я имел в виду — нет возможности организовать рекурсию между этими кусками, во всяком случае, простым образом. Т.е. чтоб кусок регулярки под именем «a» мог сматчить «b», а тот в свою очередь снова «a».
                • 0
                  Есть:
                  (zyx-desktop:zyx:~) 3 % echo 'abbabaa' | perl -p -i -e 's/(a(?2)*)(b(?1)*)/1($1)2($2)/' 
                  1(abba)2(baa)
                  PCRE тоже поддерживает, но из‐за отсутствия pcresed я не могу показать, какой кусок текста сопоставляется какой скобке.
                  • 0
                    Если что, то их можно также именовать:
                    (zyx-desktop:zyx:~) 3 % echo 'abbabaa' | perl -p -i -e 's/(?<fst>a(?&snd)*)(?<snd>b(?&fst)*)/1($1)2($2)/'
                    1(abba)2(baa)
                    и нумеровать относительно текущей скобки:
                    (zyx-desktop:zyx:~) 3 % echo 'abbabaa' | perl -p -i -e 's/(a(?+1)*)(b(?-2)*)/1($1)2($2)/'
                    1(abba)2(baa)
                    . -2 потому, что считаются открывающие скобки, соответствующие захватывающим группам, в заданном направлении.
          • 0
            В perl и PCRE можно использовать рекурсивные регулярные выражения: простейший пример: echo '((((()))))' | grep --color=always --perl-regexp '\((?R)?\)' подсветит вам все скобки (при условии, что grep собран с поддержкой PCRE). Можно ссылаться как на всё регулярное выражение (как в моём примере), так и на конкретную именованную или нумерованную группу.

            В perl также можно вставлять в регулярное выражение куски кода, которые будут выполнены во время сравнения регулярного выражения со строкой ((?{code}) и (??{code})), но использование этой возможности не является хорошей идеей (в perldoc perlre в первом абзаце описания даже стоит предупреждение).
      • 0
        У меня основная проблема не в абстракции или длине. Как вот эту вот кракозяблину назвать? А как назвать <*>, <**> и >>? Что прекажете делать на ревью, вообще в любом техническом разговоре?

        Ещё часто автары разнообразных вдохновляющих статеек даже не эти безымянные операторы используют а их типографские прототипы, лишая скромного читателя возможности их гениальный код собственно запустить. Вот и думаешь оно для программирования сделано или для повышения ЧСВ?..
    • +1
      По той же причине не понимаю, как питонщики читают код длиной более одного экрана
      Ага, бьют на подподподпроцедурки. Так и приходилось делать

      А что насчёт шаблонов для веб-сайтов? Как понимаете, разметка для удобства чтения выравнена по тегам HTML, без скобок будет сложно
      Решение — использовать шаблонизаторы

      • +1
        Я бы поспорил насчет подразумеваемого факта, что «подподподпроцедурки» — это плохо. И уж тем более что процедуры более одного экрана — это хорошо.
      • 0
        есть еще отличное решение — экран побольше ) Но вообще никаких проблем не имею, хотя не могу сказать что приходилось на питоне писать что то достаточно объемное. Ну и свертка кода помогает в запущенных случаях.
      • 0
        Если метод или функция занимает больше экрана (85 строк в моем случае), то уже и скобочки не помогут. С другой стороны, в нормальных редакторах блоки дополнительно выделяются либо в области номеров строк, либо прямо по отступу блока.

        Поэтому проблема сильно преувеличена.
    • –1
      А ещё я вот думаю, что код «ss=: +/ @: *:» выглядит довольно страшно. Авторы его наверняка вдохновлялися брэйнфаковскою записью кода.
      На самом деле, философский вопрос — что лучше: компактность или читаемость. По мне, лучше IDE с хорошим парсингом и хорошей подсветкой синтаксиса;-) (т.е. компактность и правильность в сочетании со средствами искусственного повышения способности читать;-) ) Не знаком глубоко с J и K, но, подозреваю, пропустить один оператор так, что компилятор не посчитает это ошибкой, и потом нудно отлавливать баг при отладке — в таких языках ситуация реальная. Если же язык стековый, где фактически вся программа — один сплошной оператор и скобок нет, то всё становится ещё веселее.
    • 0
      А в F# «отступное структурирование»? В исходном OCaml его не было.
  • +9
    Так-так-так. Значит мы тут пропагандой функциональщины и оскорблением чувств императивщиков занимаемся?
  • 0
    Только прочитав первую причину, попытавшись потом прочитать оригинал и ухватив глазами «Reason 2: I get paid by the line», я понял, что это ирония…
  • +2
    7-я причина не такая уж и смешная. Прототипы на Haskell-е писать не так удобно, как, к примеру, на том же Ruby.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.