понедельник, 19 августа 2013 г.

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

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



Про автора

Я разработчик и архитектор британской не софтверной компании. Имею 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.
Но зато я слышал, что функциональные языки используют странные символы типа <*> и >>=, или непонятные концепции типа «монад» и «функторов».
Я не знаю почему функциональные программисты не смогли придерживаться уже известных мне вещей, таких очевидных как символы типа ++, != и лёгких концепций типа «наследование» и «полиморфизм».

Итог. Я не понимаю этого


Вы знаете, я не понимаю этого. Я не понимаю, чем функциональное программирование полезно.
Я очень хочу чтобы кто-нибудь просто показал мне настоящие преимущества на одной странице, вместо того чтобы заваливать меня информацией.
Обновление: Хорошо, сейчас я прочитал статью «все что вам нужно знать на одной странице». Но она очень короткая и простая для меня.
Всё таки я ищу что-то немного более глубокое, что-то с чём я мог бы продуктивно поработать.
И нет, не говорите мне, что мне следует прочитать руководствапоиграть с примерами и написать свой собственный код. Я просто хочу посмотреть, не делая всего этого.
Я не хочу менять своё мышление, просто для того чтобы изучить новую парадигму.

Комментариев нет:

Отправить комментарий