3 ноября 2009 в 12:16

Изучай Haskell ради… Haskell'а

Я долго (несколько лет) не решался составить окончательное мнение о Haskell'e: слишком противоречивы были мысли. И вот, наконец, благодаря этой записи о разборе программки определения двудольности графа я могу это сделать :)

Я понял, что Haskell-программисты — в основном, нужно сказать, хобби-программисты — это те, кто программирует не решение задачи, алгоритм, систему, а Haskell! [1] Посмотрите, какой простой алгоритм описан в заметке, а сколько вокруг него нагромождено языковых конструкций, объяснений и дискуссий. (Чтоб понять, насколько алгоритм прост, можете посмотреть в комментариях варианты на Lisp'е, Python'е).

Мне хорошо знакомо это умонастроение — когда в погоне за максимальным использованием мощи языка забываешь о самой задаче,— поскольку в Lisp-мире оно тоже часто встречается: есть языки, которые способны действительно увлечь. И выражение «это взорвало мне мозг» часто звучат и по поводу Lisp'а, и по поводу Haskell'а. Но это же — фигня! Конечно, не может не радовать узнать что-то новое, но не нужно же радоваться этому, как ребенок новой игрушке. Хороший язык программирования должен быть максимально понятен и прост, должен давать человеку свободу самовыражения. Честно говоря, именно этому я обрадовался, когда открыл для себя Lisp: что нашел то, что искал. А не тому, что увидел какую-то конструкцию или изворот, который не доводилось встречать раньше.


Haskell — очень интересный язык, у которого есть как плюсы, так и минусы. Плюсы: это интересная семантика и сильная теоретическая база, хорошая скорость выполнения у бинарного кода, который дают современные компиляторы. Минусы: ужасный синтаксис [2], искусственная ограниченность, которая приводит к необходимости задействовать сложные подходы там, где отлично справятся и простые. И им просто обязательно стоит заниматься, если вас интересует тема языков программирования как таковых, их развития и исследований. Из Haskell берут многое другие более практичные языки: яркий пример тому Clojure. Но он не для написания больших систем и даже не для исследования алгоритмов в общем случае. У языков программирования кроме синтаксиса и семантики есть еще третий аспект, пожалуй даже важнейший, о котором часто забывают — прагматика. То, как язык используется, для чего он предназначается, чем живет сообщество его разработчиков и пользователей. Прагматика Haskell'а заключается в том, что он существует прежде всго для исследования… Haskell'а.

Примечания:
[1] Есть, конечно, исключительные, прекраснейшие Haskell-программисты, написавшие на нем много полезного кода для реального мира, но это, как говорится в нелюбимом мной афоризме, только подтверждает правило.

[2] Для современного языка нерегулярный синтаксис — это неуважение к своим пользователям. Ведь никто в современном мультиязыковом мире не программирует на одном языке, поэтому нельзя требовать от человека держать в голове идиосинкразии каждого. И этих общеупотребимых языков будет все больше и больше, а количество legacy кода уменьшаться не будет. Я сейчас имею дело с Lisp, Python, Php, C, JavaScript, Shell, Java. И это ведь не самый яркий пример.

PS. Изначально, я поднял тему в своем блоге. Однако решил вынести ее на более широкое обсуждение, чтобы услышать мнение на этот счет как раз Haskell программистов
vseloved @vseloved
карма
33,0
рейтинг 0,0
Самое читаемое Разработка

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

  • +4
    Вывод о сложности кода на Хаскеле почти всегда делают те, кто на нём особо и не писал. Язык должен быть понятен тем, кто его знает, а не всем. В этом плане Haskell как раз очень хорош, чужой код на нём читается достаточно просто, именно благодаря Bondage&Discipline.
    А благодаря искусственным ограничениям решение «в лоб» часто оказывается слишком неэстетичным (мутабельность-то есть, но десять раз подумаешь, прежде чем её вставить), а потому приходится думать над более простым решением. Я нередко сам сталкивался с тем, что в итоге решение получается наоборот простым. Т.е. всё с точностью до наоборот. Подходы-то сложные, зато решение проще и понятнее.
    Это, конечно, субъективное мнение, но основано на достаточном опыте, а не на поверхностном знакомстве.
    • 0
      в принципе, вывод о сложности кода касался непосредственно того примера, где сложность была излишня, как показывают решения на других языках.

      на счет понятности языка: согласен, что он не должен быть понятен всем. Но все же останусь при том мнении, что регулярный синтаксис лучше ad hoc. В Haskell'e есть тенденция использовать очень специфичные нотации для кажой отдельной конструкции, а также довольно много иероглифов. Это приводит к тому, что не фулл-тайм Haskell-программистам сложно вникать в код, соответственно им это и не нравится (все естественно, не так ли?)

      мой основной вывод: я понял, зачем существует язык Haskell :) Это, конечно, в шутку, но в каждой шутке есть доля правды.
      • +2
        Сложность там излишня для конкретного примера — написанного на скорую руку простого алгоритма, в котором и ошибиться-то негде. На Питоне следовало бы для более полного сравнения наваять тестов, а потом проверить возможность отрефакторить, чтобы ничего не сломалось. Я так понимаю, пугает всех наличие всяких fmap runStateT, ну так это разумная плата за мощную типизацию и контроль.
        Типы тоже иногда приходится указывать, некоторые даже говорят, что они не нужны, однако бенефиты от типов многим видны.
        Здесь аналогично, да, приходится явно указывать — тут у меня состояние, тут исключения, однако лично мне это помогает понимать код, плюс ощущение, что практически любой скомпилированный код будет работать сразу же, у меня появилось только при использовании Haskell.
        А рефакторинг на нём делать вообще сказка. Так что экстраполировать на средние и большие проекты точно не стоит. Там всё совсем по-другому. Я не говорю, что там сплошные плюсы, но уж слишком разные параметры имеют значение.
        • 0
          На счет типизации. Используется представление графа в виде списка пар вершина-список соседей. Вы уверенны, что программа будет правильно работать, если задать некорректно этот список (например, не соблюсти симметричность)?
          • 0
            Пардон, промазал, ответ ниже.
  • 0
    Не понял, несоблюсти какую симметричность?
    Я по крайней мере точно уверен, что элементы пары случайно никто не поменяет местами, и уж тем более не передаст нечисла. Можно получить и более строгую гарантию при желании.
    • 0
      я там в комментариях привел пример плохих входных данных для Python версии ({1: [2], 2: []} Свойство симметричности для ненаправленного графа: если вершина А соединена с B, то B соединена с A), но глючить будут все (и Haskell, и Lisp, и Python).

      Это я к тому, что проверка типов не гарантирует отсутствия ошибок даже на уровне типов, поскольку:
      * подчас очень сложно описать реальный тип (т.е. исчерпывающий список ограничений) на языке системы типов. (Хотя есть, конечно, Тьюринг полная система вывода типов в Qi, но что толку, если программирование системы типов становится эквивалентной собственно решению задачи)
      * даже если у нас будет язык для полноценного описания типов (см. Qi), нет возможности enforcement'а того, чтобы программист делал исчерпывающее описание

      Короче говоря, type checker — хороший инструмент, но не серебрянная пуля в плане проверки корректности программы. Тестировать нужно и Haskell программу.
      • 0
        Честно говоря, аргумент «не серебряная пуля» и «не гарантирует» на самом деле всё больше начинает радовать. Это ли не доказательство (ну, не доказательство, конечно, но на мысли наводит) того, что больше аргументов нет, раз прибегают к неоспоримым, но при этом совершенно бессмысленным?

        Никто и не утверждает, что типизация — серебряная пуля. Это просто другой инструмент проверки программ, но у Haskell есть одно преимущество — у него этот инструмент есть. Ещё более мощный инструмент есть у Agda2, например. А вот когда выгодно воспользоваться одним, а когда другим (тестированием), решать уже программисту.
        Тестировать Haskell программу нужно, только для других языков нужны в том числе огромное кол-во и тех тестов, которые typechecker'ом Haskell отметаются, а в другом языке выбора нет.
        • 0
          про серебрянную пулю — это было замечание к «ощущению, что практически любой скомпилированный код будет работать сразу же» (работать то будет, но вот корректно ли? Python'овский код и компилировать не нужно — тоже сразу заработает ;)

          Вообще, type-checker, конечно, полезный инструмент. Только, как по мне, он должен быть опциональным (поскольку далеко не для всех программ корректность стоит на первом месте).

          И про тестирование забывать не нужно. И совсем не факт, что программу не Python нужно тестировать больше, чем Haskell'овскую. Как по мне, все зависит от уровня программиста, который ее напишет, и его понимания underlying концепций. (Хотя да, в среднем, наверно, для Python программы больше возможностей сделать ошибку, хотя и, наверно, больше возможностей попробовать разные варианты, поскольку программы, опять же в среднем, пишутся быстрее).

          Тем более, что есть еще вопросы изменяемости программы со временем, интерактивной разработки, кодогенерации и т.д., на которые статическая проверка типов на этапе компиляции не дает хороших ответов. Но давайте о них не будем :)
          • +1
            Дык на то оно и ощущение. Понятное дело, что ошибки могут быть, но то, что несколькодневный код работает сразу же, поражает. Уж который раз, а всё никак не привыкну.

            Как это не факт? Всё, что придётся тестировать на Haskell, придётся тестировать и на Python'е. Плюс как минимум типы. Я тут на днях переворачивал вверх дном код, так повылазило очень много ошибок типизации (где-то недосмотрел, где-то что-то перепутал), заткнув которые, я получил работающую программу.
            Я, конечно, на Python столько не писал, но боюсь представить, как бы эти ошибки затыкались на Python'е. Либо тесты на это дело таки должны быть (и всё равно проверка только после запуска), либо будет страшный страх.
            • 0
              совершенно не обязательно на Python'е (или любом другом динамическом языке) тестировать типы. Типы все равно проверяются на этапе выполнения и самое страшное, что может произойти — Exception (это не С, где будет segfault), который можно отловить выше по стеку (вполне себе адекватная стратегия для многих программ). Т.е. семантика поведения все равно полностью определена, программа вполне может быть корректной, если она написана с учетом этого.

              И, кстати, какой дурак будет передавать в функцию проверки двудольности графа нечисла? Точнее, скажу так, такой дурак найтись может, только он скорее всего будет не дурак, а тот, кто решит переписать существующую программу немного иначе: например, заменить числовые идентификаторы вершин на какие-то структуры. И разные вспомагательные функции (типа нашей bipartiteness-test) он пока не готов менять (хочет сначала проверить, как оно все храниться в базе будет). Не тут то было: чтобы проверить свой новый подход, ему придется еще преписать кучу всего в программе, где хоть как-то фигурируют эти вершины (ведь есть propagation типов, не так ли). И, если подход не подошел, усилия выброшены на ветер. Это называется coupling, а все говорят, что нужно стремится к de-couling.

              В то же время, вернемся к тому, что никто случайно не передаст сюда строки, а вот передать неправильное описание графа (где не соблюдена симметричность) случайно может каждый (а как раз это то мы не проверяем). В итоге получается, что мы «боремся» с проверкой типов, чтобы защититься от дурака, в то время как реальную проблему то и не адресовали. Хм…

              А как может быть так, что программу на Python тестировать нужно меньше, чем на Haskell? Дело в том, что тестировать нужно не все, а то, что не до конца понимаешь, в чем не до конца уверен. В программе проверки двудольности на Python мало таких мест — все довольно таки очевидно,-- а вот в Haskell-варианте у нас 2 монады, монадные трансформеры. Хорошо, если вы это понимаете, себе можно доверять. А Васе Пупкину, который живет в Сибири, и написал этот код вы доверяете, что он в точности понимает логику работы этих монад? (Вы тут скажете: тут есть только 1 решение — правильное, остальные, просто, не скомпиллируются. Не согласен: как на счет нашего примера с неправильно заданным графом?) Тут мы и подходим к вопросу разработки больших систем…

              Решение: дисциплина должна быть в головах, а в языках программирования — инструменты, доступные для использования.
              • 0
                Тестировать надо не только для того, чтобы убедиться, что не ошибся сейчас. Вы не ответили на самое интересное с рефакторингом. Вместо получения 20 ошибок от компилятора, я буду отлавливать exception'ы и по очереди искать, где же я при изменении старого кода ошибся? Это не решение. Или, может, я и есть тот дурак, а умные и рефакторят без ошибок?

                Тот, кто захочет заменить способ хранения графа, конечно, не дурак, но что же, все недураки перед рефакторингом сидят и помечают все места, где надо не забыть заменить код? А в старый код ещё и вникать придётся. Какая-то monkey-work, честное слово. Достаточно где-то не заменить функцию, и ловим ошибки во время работы программы. Я же иногда даже просто беру и меняю тип данных. Всё. Далее мне компилятор сам выдаст все места, где надо поправить (тут, кстати, играет роль не только система типов, но и различие написания конструкторов и переменных и прочее), а там я уже решаю, нужно тут вникать, или сделать тривиальное изменение.

                Разные вспомогательные функции в Haskell'е легко и просто описываются такой замечательной штукой, как undefined. Если вы не случайно ошиблись, а именно что хотите временно отложить написание или исправление — undefined вам в руки, сам пользуюсь, так что это надуманная проблема.

                Я не доверяю ни Васе Пупкину, ни Пете Васечкину, но в Haskell'е я по типу вижу (без всяких доверий), что функция не меняет глобальных переменных и не пишет в лог. А захоти я изменить тип графа, я его просто поменяю, а потом исправлю ошибки типизации. И с большой вероятностью, ничего не сломается. Тут мы и подходим к вопросу разработки больших систем, когда я предпочту не вникать в код, если этого можно избежать.
                • 0
                  Тот, кто захочет заменить способ хранения графа, конечно, не дурак, но что же, все недураки перед рефакторингом сидят и помечают все места, где надо не забыть заменить код? А в старый код ещё и вникать придётся. Какая-то monkey-work, честное слово. Достаточно где-то не заменить функцию, и ловим ошибки во время работы программы. Я же иногда даже просто беру и меняю тип данных. Всё. Далее мне компилятор сам выдаст все места, где надо поправить (тут, кстати, играет роль не только система типов, но и различие написания конструкторов и переменных и прочее), а там я уже решаю, нужно тут вникать, или сделать тривиальное изменение.

                  > Далее мне компилятор сам выдаст все места, где надо поправить
                  да, хорошо, когда есть такая возможность

                  > Разные вспомогательные функции в Haskell'е легко и просто описываются такой замечательной штукой, как undefined
                  ну, хорошо, что есть хоть элемент опциональности. Но вы зря приципились к слову вспомагательный. Может быть, что это важная часть, но мы пока ее не трогаем. Еще раз повторяю, зачем все сразу менять, если не уверен, что нужно. Или вы такой ситуации не допускаете?

                  > функция не меняет глобальных переменных и не пишет в лог.
                  Глобальные переменные и побочные эффекты — не единственные проблемы в программировании. В том, что вы говорите, конечно, есть свои плюсы, не спорю, но все равно это trade-off. Было бы неплохо, если бы решить мог сам программист, как вы считаете?
                  • 0
                    Компилятор указывает вам места, которые работать не будут, так как тип не совпадает. Конечно, это место может быть важным, но оно всё равно не заработает. Я пишу undefined и заключаю в комментарий весь код. Так что слово «вспомогательный» там было просто процитировано, это не суть. undefined имеет любой тип, я могу его поставить на место некоторой «преобразовательной» функции, которой пока что не написал. В общем, ей можно заткнуть вообще любое место, так что тут проблемы на самом деле нет.

                    > Глобальные переменные и побочные эффекты — не единственные проблемы в программировании
                    Дык! Поэтому хотят вообще возможность в типе функции указать, что она не просто осуществляет ввод-вывод, а ровно в тот Handle, который ей передали, а не куда-то ещё, но это пока слишком круто ;)
                    Программист может решать сам, мутабельные переменные есть, dynamic есть, template haskell есть. Другое дело, что выглядит это страшновато, но тут на самом деле вопрос баланса. Сделай удобным — начнут пользоваться, где попало, что плохо. А вот где эта золотая середина — сказать сложно.
                    Т.е. если очень нужно, оно есть, пусть решает программист, но сначала подумает, а дейсвительно ли нужно.
  • 0
    >Прагматика Haskell'а заключается в том, что он существует прежде всго для исследования… Haskell'а

    Brainfuck почему-то вспомнился.

    А еще Ломоносов: «Математику уже затем учить надо, что она ум в порядок приводит.»

    Выбирал с каким из функциональных языков познакомиться в первую очередь, чтоб моск свой поломать, выбрал почему-то Erlang…
    • 0
      Почитайте комментарии к заметке Льва — мы там и brainfuck обсудить успели.
      А вам совет не моск ломать, а понять, что вы хотите сделать, и подумать, на каком языке это будет инетреснее всего для вас — так и выбирать. Эволюция эффективнее революции ;)
      • 0
        Всегда самое сложное придумать задачу. Чтоб и по душе, и для дела. :) А коммерческое программирование к тому же накладывает кучу ограничений — нужно быстрее, дешевле, проще, прямее.
    • +5
      Brainfuck писали не ради исследования Brainfuck'а, а ради brain fuck'а.
  • 0
    > Хороший язык программирования должен быть максимально понятен и прост, должен давать человеку свободу самовыражения. Честно говоря, именно этому я обрадовался, когда открыл для себя Lisp: что нашел то, что искал.

    Отчего же только LISP? Навскидку могу вспомнить три таких языка. LISP, TCL и FORTH. Каждый в своей области ценен. FORTH достаточно быстрый и весит считанные килобайты. TCL проще остальных в изучении, т.к. использует более понятный большинству синтаксис и императивный подход. А главное — все они не ограничивают «свободу самовыражения». На остальных языках, с которыми я встречался, любой алгоритм приходится вписывать в рамки возможностей языка. На этих трёх возможно всё.
  • 0
    > Я понял, что Haskell-программисты — в основном, нужно сказать, хобби-программисты — это те, кто программирует не решение задачи, алгоритм, систему, а Haskell!

    А что тут такого? A programmable programming language is a win. ;)

    Между прочим, многие мейнстримные языки тоже вплотную подбираются к некоторым формам «программируемости» (точнее сказать, выразительности) Хаскеля. Чего стоит только соответствие классы+generics и GADT.

    > Посмотрите, какой простой алгоритм описан в заметке, а сколько вокруг него нагромождено языковых конструкций, объяснений и дискуссий. (Чтоб понять, насколько алгоритм прост, можете посмотреть в комментариях варианты на Lisp'е, Python'е).

    Вы так говорите, как будто код на Лиспе и Питоне никогда не обсуждается, в отличие от. Очевидно, что это не так.

    > Но он не для написания больших систем и даже не для исследования алгоритмов в общем случае.

    Разумеется. Вы же прекрасно знаете, что для написания больших систем и «исследования алгоритмов в общем случае» нужно что-то совсем другое.

    > То, как язык используется, для чего он предназначается, чем живет сообщество его разработчиков и пользователей. Прагматика Haskell'а заключается в том, что он существует прежде всго для исследования… Haskell'а.

    Хаскель используется, например, для исследований в разных областях CS. Надеюсь, это что-то объясняет?

    > Есть, конечно, исключительные, прекраснейшие Haskell-программисты, написавшие на нем много полезного кода для реального мира, но это, как говорится в нелюбимом мной афоризме, только подтверждает правило.

    Охохо, ну давайте по аналогии. Назовите хотя бы один проект, скажем, на PHP, который представляет научный интерес? Иными словами, есть ли исключительные, прекраснейшие PHP-программисты, написавшие на нем много полезного кода для доказательства теорем, например?

    Как-то нечестно сравнивать академический (по большей части) язык с промышленным, принимая во внимание только одну (интересующую) сторону, не правда ли?

    > Для современного языка нерегулярный синтаксис — это неуважение к своим пользователям.

    Нерегулярный синтаксис (точнее, нерегулярная грамматика, которая и задает синтаксис) наверное, у всех языков программирования, если мы, конечно же, беседуем об одном и том же.

    > Ведь никто в современном мультиязыковом мире не программирует на одном языке, поэтому нельзя требовать от человека держать в голове идиосинкразии каждого. И этих общеупотребимых языков будет все больше и больше, а количество legacy кода уменьшаться не будет. Я сейчас имею дело с Lisp, Python, Php, C, Javascript, Shell, Java. И это ведь не самый яркий пример.

    А приходится, что поделать?
    • 0
      если для вас то, что я сказал — очевидно, просто вы воспринимаете это по своему, то я не против (так, вообще, всегда :)

      на счет регулярности синтаксиса: я имел в виду, разумеется, не регулярную грамматику, а регулярность в смысле английского слова uniformity, т.е. единообразность. Как пример можно привести… нет, даже не Lisp (с этим понятно), а Python, у которого весь синтаксис следует определенным базовым принципам. У Haskell'а везде операторы: где инфиксные, где префиксные (где, вообще, обволакивающие: типа `fmap` ;). Посмотрите, первая глава Real World Haskell посвящена тому, как правильно писать -1, чтобы не получить ошибки

      ps. и еще посмотрите дискуссию в моем блоге
    • 0
      > А что тут такого? A programmable programming language is a win. ;)

      Кстати о programmable programming. В Хаскеле можно переопределить операцию определения функции?
      Вопрос на(censored) это надо сейчас не рассматривается. :)
      • 0
        Нет, нельзя. Рассматриваю как уход в сторону и не отвечаю. :)

        PS не удержусь: таки зачем?
        • 0
          Уход в сторону от Programmable Programming?

          Определяем свой proc, который будет выводить в консоль регистрируемую команду.

          $ cat test.tcl
          rename proc _proc
          
          _proc proc {name arglist body} {
              puts "Compiling proc $name"
              _proc $name $arglist $body
          }
          
          proc test {} {
              puts "Called test"
          }
          
          test
          $ tclsh test.tcl
          Compiling proc test
          Called test


          Так на TCL реализованиы все профайлеры и новые дабаггеры (после появления команды trace).
  • +1
    Во-первых, решения antilamer'а совершенно прозрачно для тех, кто имеет хоть какой-то опыт написания программ на Хаскелле. Если вы его не можете прочитать, то это ваша проблема и Хаскелль здесь не виноват.

    Заметим, что код на Питоне кажется вам более понятным исключительно потому, что все императивные языки по сути дела похожи.

    Во-вторых, синтаксис у Хаскеля весьма «униформный» — конечно, не настолько простой как у Lisp, но вполне понятный, при наличии определённой культуры чтения.

    Кстати об идиосинкразиях: вы что утверждаете что PHP/Javascript/Shell/Lisp лишены особенностей и имеют настолько одинаковый синтаксис и семантику, что вы легко переключитесь с одного на другой после года разлуки?
    • 0
      > Во-первых, решения antilamer'а совершенно прозрачно для тех, кто имеет хоть какой-то опыт написания программ на Хаскелле
      расскажите это тем, кто комментировал ;)

      > Заметим, что код на Питоне кажется вам более понятным исключительно потому, что все императивные языки по сути дела похожи.
      думаю, нет: потому, что он довольно таки напрямую реализует алгоритм

      > при наличии определённой культуры чтения
      когда в одной строке записано 10 операций — это очень трудно понять не зависимо от культуры. Оно либо клацает в мозгу (т.е. синтаксис просто «на слуху»), либ происходит переполнение стэка

      > вы что утверждаете что PHP/Javascript/Shell/Lisp лишены особенностей
      PHP/Javascript/Shell — полно, Lisp, Forth — почти нет, довольно чистые в плане синтаксиса языки — Python, Java. Вообще, этот вопрос нужно рассматривать по каждому языку отдельно. Проблема Haskell'а в том, что такой синтасис у него не по глупости (как в PHP или JS), а намеренно (из-за максимализма и, я бы даже сказал, аутизма)
      • 0
        >> расскажите это тем, кто комментировал ;)

        ну хорошо это не только ваша проблема. это проблема кучи комментаторов. что изменилось?

        >> думаю, нет: потому, что он довольно таки напрямую реализует алгоритм

        сюрприз! сюрприз! код на хаскле напрямую реализует алгоритм тоже. если вы этого не видите, then goto «во-первых».

        >> когда в одной строке записано 10 операций

        а когда в одной строке записано десять русских слов вас это не смущает?

        кстати по десять операций там нет — максимум восемь.

        вам синтаксис Haskell не нравится? ну не читайте, не пишите… никто вас не заставляет =)
        • 0
          > ну хорошо это не только ваша проблема. это проблема кучи комментаторов. что изменилось?
          изменилось то, что некоторые из них «имеет хоть какой-то опыт написания программ на Хаскелле»

          > сюрприз! сюрприз! код на хаскле напрямую реализует алгоритм
          наверно, мы с вами не совсем одно и то же понимаем под словом напрямую. Я имел в виду количество дополнительной абстрактной информации, которую нужно держать в голове, чтобы понять реализацию. Для варианта Лиспа нужно понимать, что такое анонимная функция, замыкание, map, хеш-таблица, список, пара. Для Python'а это примерно то же ± list-comprehesions. Все это нужно понимать и в случае с Haskell'ом, но тут еще добавляется монады, монадные трансформеры, pattern matching, что я еще забыл? Т.е. концептуальная нагруженность решения возрастает. А что это нам дает? (в данном случае вопрос риторический) И, вы знаете, этот пример очень показательный. Оказывается, что так бывает и не только в таких тривиальных случаях. Мой вывод из этого: не стоит городить абстракции ради самих абстракций. Покажите мне лучше задачу, где их применение дает реальные преимущества (я знаю, что такие есть, но также знаю, что есть такие, где это дает наоборот проигрыш. Согласны?)

          > а когда в одной строке записано десять русских слов вас это не смущает?
          нет, если это не 10 предлогов подряд

          > вам синтаксис Haskell не нравится? ну не читайте, не пишите… никто вас не заставляет =)
          спасибо за совет, я, в общем-то, так и делаю =)
          Но это было одно из замечаний. Другие раскрыты в других ветках обсуждения.
          Вам могу сказать в ответ: не нравится мое мнение, не читайте, не комментируйте ;)
          • +1
            >> изменилось то, что некоторые из них «имеет хоть какой-то опыт написания программ на Хаскелле»

            хоть какой-то не подходит, если человек дальше Hello World не продвинулся или писал игрушечные факториалы.

            из тех, кто там отметился и кто в теме (т.е. писал real world код) никто особо не возмущен сиснтаксисом Haskell.

            или я кого-то пропустил?

            я вам честно скажу: сам когда код антиламера увидел, сказал «прочь демон! прочь!» и пролистал пост во френдленте. Но когда поднялась буча, я к этому коду вернулся аккуратно просмотрел его сверху вниз и понял, что всё достаточно прозрачно.

            >> спасибо за совет, я, в общем-то, так и делаю =)

            но при этом осмеливаетесь делать наброс на Haskell.

            дескать не читал, не писал — но осуждаю.

            у вас основная мысль, кажется, выражается знаменитой фразой: why waste time learning, when ignorance is instantaneous?
            • 0
              Ну уж, простите, что осмелился. Во-первых, если вы вчитаетесь, то наброс был больше на Haskell-комьнити. Во-вторых, то, что не делаю этого сейчас не значит, что вообще никогда не делал. Ну и, в-третьих, если по сути возразить ничего не остается, приходиться переходить на личности, да?

              кстати, я не вижу ответа на вопрос «я знаю, что такие есть, но также знаю, что есть такие, где это дает наоборот проигрыш. Согласны?» желательно с хотя бы парой примеров того, где это дает существенный выигрыш
              • 0
                Вы немного путаете адресатов для своих вопросов. Я не адепт и не фанат Хаскелля, поэтому вопросы о преимуществах мне лучше не задавать (у меня всё пока на уровне ощущений и фана). Спросите лучше Сергея Зефирова.

                Единственный мой «point» в данном споре следующий: ваши набросы не имеют под собой никакой реальной подоплёки. Я считаю это преступлением — троллингом по сути. Совершенно неважно на кого вы делаете набросы на коммьюнити (т.е. переходите на личности ;) ), либо на сам язык — наброс остаётся набором предрассудков, а не набором объективных аргументов.

                Моя личная позиция такова: чтобы оценить преимущества надо одну и ту же задачу решить на разных языках. Для реальных проектов это не применимо. Для игрушечных не даёт достаточно данных. Максимум становится понятно, что программист с минимумом обучения может прочитать программу на Питоне, но не может на Haskell. Ну и что? Аналогичный вывод можно сделать о китайском языке. Никаких далеко идущих последствий (кроме высокого порога вхождения в проект) нет. По сути это мало чем отличается от подсчета закрывающих скобок в LISP программе — метрика интересная, но мало что в реальности показывающая.

                Надеюсь моя позиция стала понятной.

                • 0
                  (Ваша позиция была понятно и до этого).

                  Я как раз делал сравнение на реальном примере данного алгоритма. Можно сделать сравнение на других примерах. Перспективный вариант: сравнить darcs с git и mercurial. Например, на тему того, насколько расширяемые проекты, как проработано API, всякая статистика…

                  А предрассудки есть у всех. В какой-то степени Haskell-комьнити занимается троллингом глобально, поскольку:
                  * присвоило себе функциональную парадигму
                  * претендует на то, чтобы быть языком по-умолчанию в вузах
                  * ну и, разумеется, снобизм по отношению к другим языкам и подходам. «Тут все так круто, что ваш императивный код даже не стоит сравнивать». А давайте сравним, что ли? Есть случаи, когда очень даже выигрывает Haskell, а есть такие, где проигрывает. Проблема в том, что вы закрываете глаза на вторую часть, и считаете, что это все мелочи. «Если вам что-то не нравится в нашем языке, значит вы его, просто, недостаточно изучили, недостаточно понимаете, так что молчите в тряпочку», так?
                  (Ну а если следовать вашей логике, что «Для реальных проектов это не применимо. Для игрушечных не даёт достаточно данных.», то тогда зачем поднимать тему Haskell'а? Ну, еще один obscure язык, ничем не лучше и не хуже других: ведь остальные утверждения — чистая отсебятина тех, кто язык знает или не знает ;)
                  • 0
                    В вашем комментарии можно Haskell заменить на много других слов.

                    Адепты LISP пиарят LISP, адепты boost::mpl пиарят C++, кто-то пиарит Форт, кто-то D, кто-то Lua…

                    Адепты они все примерно одинаковые.

                    Я бы не стал делать Хаскель языком по умолчанию в вузах, ровно как и ЛИСП. (SICP я приэтом читал, сюрприз, сюрприз) Вообще языки по умолчанию = зло.

                    Я не сноб в этом отношении. Пожалуйста сравнивайте, но делайте правильные с логической точки зрения выводы. Я привёл вам то, как на мой взгляд стоит трактовать бучу с данным конкретным алгоритмом: порог высок. Никаких больше выводов я сделать не могу.

                    >> Если вам что-то не нравится в нашем языке, значит вы его, просто, недостаточно изучили, недостаточно понимаете, так что молчите в тряпочку, так?

                    Если вам что-то не нравится в языке, то это субъективный показатель (слово «нравится» намекает как бы на это). Ничего объективного больше из этого «не нравится» я извлечь не могу.

                    Вы же из своего личного «не нравится» (или «не могу прочитать», или «не хочу читать») извлекаете какие-то глобальные истины. Это просто абсурдно.

                    Мне лично совершенно очевидно, что Хаскелл не панацея. У меня самого три любимых языка, писать на которых для меня фан: C, Lua, Haskell. И ни один из них я не возважу в абсолют, как нечто «божественное». Это просто средство общения с компьютером, не более. И мне всегда интересно почитать _адекватную_ критику языка. Критикуйте! Но _адекватно_, _логично_.

                    Заметьте, пожалуйста, я не поднимал тему Haskell, это сделали ровно вы. Так же как и антиламер не пошел в народ с кадилом, а просто в своём ЖЖ поделился своим кусочком кода, который ему понравился.

                    Слово obscure, кстати, лишний раз показывает что вы вкладываете слишком много личных ощущений в данный разговор.
                    • 0
                      естественно, каждый из нас высказывет свою «личную» позицию. Другое дело, что она может быть более или менее обоснованна. И в других ветках я раскрывал свои основания.

                      на счет этого алгоритма: моя трактовка другая и она обоснованна, ваша — как по мне, нет.
                      на счет хаскелл-комьюнити — примерно то же самое. Моя позиция: люди занимаются этим, чтобы разминать мозги, так давайте это и называть своими словами. К реальному программированию пока что имеет мало отношения.

                      На счет ваших придирок к словам, типа «нравится», «obscure». Вы не совсем обращаете внимание на контекст использования. Например, слово obscure было употреблено в контексте, что «нельзя сравнивать». А раз нельзя, то любое слово можно использовать. И obscure, и божественный…
                      • +1
                        galois и прочие это видимо нереальное программирование. что ж, ваше право так думать.
          • 0
            > Я имел в виду количество дополнительной абстрактной информации, которую нужно держать в голове, чтобы понять реализацию.

            > Для варианта Лиспа нужно понимать, что такое анонимная функция, замыкание, map, хеш-таблица, список, пара. Для Python'а это примерно то же ± list-comprehesions. Все это нужно понимать и в случае с Haskell'ом, но тут еще добавляется монады, монадные трансформеры, pattern matching, что я еще забыл? Т.е. концептуальная нагруженность решения возрастает.

            Не используйте же. Кто вам мешает? :)

            > А что это нам дает?

            Это нам дает возможность выражать термины прикладной области в языке программирования. Простой пример: комбинаторный разбор (предиктивный рекурсивный спуск) с помощью Parsec. Сравните то, что написано вручную и то, что написано с использованием такой библиотеки, профит (модульность, reuse, etc.) очевиден.

            > И, вы знаете, этот пример очень показательный. Оказывается, что так бывает и не только в таких тривиальных случаях. Мой вывод из этого: не стоит городить абстракции ради самих абстракций.

            Индуктивное умозаключение. Не засчитывается.

            Алсо, если вы не знаете, зачем нужны абстракции, то это как бы не значит, что эти абстракции возведены ради абстракций. Так-то.

            > Покажите мне лучше задачу, где их применение дает реальные преимущества (я знаю, что такие есть, но также знаю, что есть такие, где это дает наоборот проигрыш. Согласны?)

            Да без проблем же. Напишите интерпретатор Пролога на Си и на Хаскеле и сравните.
            • 0
              Платить будут(те?) за скорость разработки или за скорость работы интерпретатора? ;)
              • 0
                И ассемблерные вставки разрешены? :)
                • 0
                  Интерпретатор Пролога на Хаскеле будет раз в 100 меньше (по сорцам), т.к. у них набор абстракций почти совпадает.
            • 0
              > Не используйте же. Кто вам мешает? :)
              Покажите решение той задачи без оных сложных абстракций

              > Это нам дает возможность выражать термины прикладной области в языке программирования. Простой пример: комбинаторный разбор (предиктивный рекурсивный спуск) с помощью Parsec. Сравните то, что написано вручную и то, что написано с использованием такой библиотеки, профит (модульность, reuse, etc.) очевиден.
              Во-первых, это можно сделать и другими способами. Яркий пример: метапрограммирование. И оно работает, о чем могу сказать на своем опыте и показать, соответственно, на примерах.
              Во-вторых, конечно же, код, написанный с помощью библиотеки будет намного лучше ad hoc кода, написанного вручную. Но это касается не только Parsec/Haskell. Во всех языках программирования есть библиотеки для парсинга. Если бы кто-то нашел самую лучшую библиотеу из другого языка (наверно, даже не функционального) и сравнил, было бы очень интересно. Вполне допускаю, что Haskell-решение намного лучше. Вот это я и хотел бы увидеть, а не алгоритм двудольности графа, который намного хуже аналогов

              > Алсо, если вы не знаете, зачем нужны абстракции, то это как бы не значит, что эти абстракции возведены ради абстракций. Так-то.
              Если их использование не обоснованно реальной потребностью, то они не нужны. Зачем подниматься на 10-й этаж, чтобы спуститься на 2-й?

              > Да без проблем же. Напишите интерпретатор Пролога на Си и на Хаскеле и сравните.
              А почему именно на С? С — это тот же ассемблер. Посмотрите на интерпретатор Prolog'а на Common Lisp (есть 2 варианта: в PAIP и On Lisp).
              • 0
                > Покажите решение той задачи без оных сложных абстракций

                Это будет сложно сделать. (Естественно, что чистота налагает ограничения — не пойму, в чем проблема-то?)

                Про «не используйте» — я говорил о Хаскеле, пардон, если двусмысленно получилось.

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

                Я не заявлял, что других способов не бывает. С чем вы спорите?

                > Во-вторых, конечно же, код, написанный с помощью библиотеки будет намного лучше ad hoc кода, написанного вручную. Но это касается не только Parsec/Haskell. Во всех языках программирования есть библиотеки для парсинга. Если бы кто-то нашел самую лучшую библиотеу из другого языка (наверно, даже не функционального) и сравнил, было бы очень интересно. Вполне допускаю, что Haskell-решение намного лучше. Вот это я и хотел бы увидеть, а не алгоритм двудольности графа, который намного хуже аналогов

                Ну запросите там у кого-нибудь. :) Или почитайте публикации.

                > Если их использование не обоснованно реальной потребностью, то они не нужны. Зачем подниматься на 10-й этаж, чтобы спуститься на 2-й?

                Так там-то реальная потребность. Необходимо сымитировать состояние в языке, который это не поддерживает. Что делать?

                > А почему именно на С? С — это тот же ассемблер. Посмотрите на интерпретатор Prolog'а на Common Lisp (есть 2 варианта: в PAIP и On Lisp).

                Исключительно для контраста.
                • 0
                  > Естественно, что чистота налагает ограничения — не пойму, в чем проблема-то?
                  Чистота — очень абстрактное понятие. Вот философские размышления на близкую тему: groups.google.com/group/comp.lang.lisp/msg/07b915645de0cec7

                  > Необходимо сымитировать состояние в языке, который это не поддерживает.
                  Это необходимость совершенно не связанная с алгоритмом, т.е. искусственная. Если язык не поддерживает состояний, то не нужно ничего пытаться имитировать: решайте без состояний. ;)
  • 0
    Очень много умных слов, особенно в комментах, но всё что могу сказать, что изучал не один язык (программирования, «человеческий» только один — итальянский, если не считать «олбанского») только ради языка, не видя, да так и не получив, никакой практической выгоды и ничуть об этом не жалею.
  • 0
    Ошибки в программах были, есть и будут. Цель программиста при использовании языка — переложить как можно больше работы по обнаружению ошибок на компилятор. Язык более высокого уровня делает такой работы больше.
    В частности, я пытался, но не смог пользоваться языками с динамической проверкой типов, например, Питоном. Потому, что не доверяю программе и лень искать дополнительные ошибки.
    По поводу корявого синтаксиса Хаскеля. Как раз наоборот, инфиксные и др. операторы сделаны для упрощения восприятия. Но! Действительно, к каждому оператору глаз должен некоторое время привыкать.
    По поводу сложности. Монады, функторы и прочая — это не язык как таковой, а шаблоны проектирования над языком, готовые, глубоко интегрированныые, легко соединяемые и хорошо отлаженные. Простые программы могут обойтись и без них. А в сложных даже в С и Ява придется изучать их отдельно.
    • 0
      Хорошая ссылочка по функторам, монадам и прочим встроенным шаблонам Хаскеля
      www.haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf
    • 0
      > Ошибки в программах были, есть и будут. Цель программиста при использовании языка — переложить как можно больше работы по обнаружению ошибок на компилятор.
      С одной оговоркой: при прочих равных условиях

      > По поводу сложности. Монады, функторы и прочая — это не язык как таковой, а шаблоны проектирования над языком, готовые, глубоко интегрированныые, легко соединяемые и хорошо отлаженные. Простые программы могут обойтись и без них
      Без монад не может обойтись ни одна программа на Haskell. И даже простая программа про двудольность графа не смогла обойтись без более сложных конструкций (монадный трансформер). Вот тут то и возник вопрос: зачем? Т.е. каждой задаче должен быть адекватен уровень абстракции (не нужно подниматься на 10-й этаж, чтобы потом спустится на 2-й). Понимаете, если бы я не знал о других подходах (хинт: мета-программирование), я бы сказал, «да, наверно, при по-настоящему сложных задачах по другому не получится. А так я могу сказать: да, это один из подходов, возможно, работающий, но очень трудно проверить (вот сделал бы кто-то разбор того, как устроена библиотека Parseс, и где тут реальное преимущество от монад, которое нельзя бы было получить другим путем, стало бы намного легче об этом размышлять). И, все равно, есть и другие, возможно даже более продуктивные подходы. Стоит сравнивать… (конечно же, не с С, как предлагается в ветке выше)
      • 0
        Я как-то сделал замечание в форуме по Nemerle, что неплохо бы генерировать свёртки и развёртки автоматически, благо не такая сложная задача. В ответ услышал, мол, в каком же порядке обходить дерево, например?
        В Haskell благодаря чистоте это не имеет значения, а благодаря монадам я всегда могу указать «направление».
        Например, распечатка списка в прямом порядке: foldr ((>>).print) (return ()) [1,2,3],
        а вот в обратном: foldr ((flip (>>)).print) (return ()) [1,2,3]
        В остальных языках монады тоже есть, но они вшиты в язык, потому не видны, но и управлять ими возможности нет.
        Чтобы иметь возможность обитать на 10-м этаже, иногда приходится спускаться на 2-й сложным способом. Другие предпочитают подниматься не выше 5-го.

      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          спасибо за ссылку, прочитал не без интереса
          возникли вопросы:
          * причем здесь Хаскелл и его семантика, если:
          — все описано на Python
          — вместо названия «монада» можно было использовать концепцию замыкания (или я плохо понял)
          * где, собственно, разбор библиотеки Parsec? :)
          (т.е. вопрос был в том плане, чтобы увидеть разбор именно реального, а не игрушечного кода)
          • НЛО прилетело и опубликовало эту надпись здесь

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