• Haskell reddit.com

    Вот — чувак задолбался на Хаскеле писать. Говорит, что крутой перец, понял ВСЁ, написал кучу пакеджей на хакадже. Говорит, что да, на хаскеле можно писать и Хаскель очень крутой язык, но блин, как же тяжело.

    Существует, говорит, куча задач, которые на перле пишутся проще чем на Хаскеле — парсить кривые форматы файлов, вызывать всякие либы. Говорит, когда ты написал софтинку, и, как того требует Хаскель, перед этим правильно поставил задачу, понял где space leaks, где можно и нужно юзать unsafePerormIO, оттюнил итд, ты понимаешь, что получил хорошую программу, и она и быстрая и красивая итд. НО СКОЛЬКО Ж ТЫ ПРОТРАХАЛСЯ С НЕЙ!

    Говорит, too much effort.

    Вот я тоже похожее ощущаю. То ли это старость, поднять новый язык в 35 лет, то ли мало практики.... но вот практика есть, и вроде педалится, а выход маловат-с. Вроде пишется быстрее чем на Ц, но значительно медленнее чем на жабо.

    Думаю, это и старость, и чувак тоже не просто так задумался.
    ♡ recommended by @L29Ah, @ndtimofeev, @masai, @odin
  • Ты только что разрушил мои надежды и мечты :)
  • Ну, а я о чём постоянно говорю? Хаскель великолепен для некоторых вещей, но интегрально — too much effort.
  • @tilarids, Кстати, мой дипломный руководитель считает (или считал) так же, поэтому посматривает в сторону того же F# к примеру, который пытается вбирать в себя лучшие фичи из разных языков. Истина, как всегда, где-то посередине.
  • @GooRoo, Тот же чувак из статьи до этого писал и про OCaml много хорошего
  • Проблема хаскеля в том, что это не просто "еще один язык", а язык, который существенно отличается от тех, на которых начинает писать типичный программист. А опыт формирует мышление соответствующим образом. Для программиста будущего, который все время работал с хаскелеподобными языками, наоборот, писать что-то на Java/Perl/F# будет очень тяжело.
  • вот насчёт парсить я не понял, по-моему удобнее удобнее всего что я видел. На перле только однострочники какие-нибудь можно.
  • @klapaucius, это все понятно. но человек который написал статью грокнул хаскель. это не очередной неоссилятор
  • @ramok, Во-первых, нет никаких оснований считать, что это какой-то специалист по хаскелю. Да, он начал писать биндинги к expat и memcached, но доводили их совсем другие люди. Самое подозрительное в данном случае — он пишет, что "понял и даже использовал монадные трансформеры" — будто это рокет сайнс какой-то.
    Во-вторых, я писал не об этом. Хаскель можно "грокнуть" — это даже не сложно. Но паттерны вырезанные временем и опытом в мозгу практикующего программиста в хаскеле — антипаттерны. Язык на котором думает современный практикующий программист — не хаскель. И это помешает большинству практикующих программистов эффективно писать на хаскеле.
  • @SannySanoff, смотря что считать медленнее. жава медленно стартует, но потом работает не сильно хуже C (некоторые утверждают что лучше). Haskell тоже стартует не быстро, но в работе очевидно тормозит, если не делать специальных усилий по оптимизации (д и если делать). А с оптимизацией из него получается чёрт знает что.
  • @SannySanoff, а. ну она просто длинее. а если есть очевидный императивный алгоритм, на жабе по-моему быстрее сделать чем соображать как это через хаскель
  • @klapaucius,

    Внимание: это пост про СКЛЕРОЗ.

    у меня проблемы, после -дцати лет Java IDE с автокомплишном, у меня плохая память на названия функций в глобальном скопе. Это всё жабо виновато.

    Например: fileExists. Где он лежит, что импортировать? Может он называется doesFileExist? может это directoryEntryExists?

    Склероз. Такая вот беда. А когда этих функций много, с этим сложно.

    Более того: в жабо пишешь (утрирую для понятия):

    Document.get[Autocomplete]ChosenParagraph().get[Autocomplete]FirstSentence().toU(AutoComplete)pperCase.sub[Autocomplete]string([parameter help here]1, 20).

    Для хаскеля даже не покомплитишь.

    Ничего блин помнить не надо. Это развращает. Я надеялся, что 2 года средней неспешности педалинга на Хаскиле помогут, но нет, память по-прежнему в жопе. Проблема усугубляется глобальным скопом для record selectors и префиксами в них.

    Далее, всё это вынуждает писать свои названия функций, которые в глобальном скопе теряются. Да, можно экспортировать только набор функций, но этот экспортируемый набор тоже растет, и там где я бы сделал 5 объектов с дитями на жабо, и объект суть namespace для автокомплишна, мне приходится придумывать названия функций, которые я сам плохо вспоминаю через неделю уже 8).

    Мама, что делать?
  • @SannySanoff, Ну, справедливости ради, он не противопоставляет хаскель и перл, когда говорит о написании чего-то большого и сложного. Он противопоставляет, когда говорит, что программа на хаскеле будет читаемее, поддерживаемее и быстрее, но ему это все не нужно, а нужно чтоб просто работало и писать было легче. Писать на перле ему легче, наверняка, потому что он на нем думает, знает все грабли, накопил проверенный набор отполированных костылей, ну и приятные воспоминания о молодости какие-то связаны. Большой проект он собирается писать на Го — тоже идея очень странная.
  • @SannySanoff, ну б. о какой вообще статической оптимизации можно говорить если я руками должен данные анбоксить и залежавшиеся вычисления форсить? Может быть, если бы в хаскель вложили бы столько денег сколько в яву — он бы и работал быстро. А пока что это тилько потенция
  • @SannySanoff, ну примерно так и есть. еще лет пять назад все это усугублялось у меня гуглом. от гугла я себя отучил, комплишеном стараюсь не пользоваться, но все равно, помнить все функции сложно.
    возможно в жава еще очень длинные названия, в кеш не влезают. в python/perl/etc названия короче, их помнить почему-то легче
  • @octo47,
    в питоне перле один fopen, один popen, и один readdir

    а тут скоко либ стоко и названий 8)
  • @SannySanoff,
    в питоне перле один fopen, один popen, и один readdir
    ну это неправда, практически по любому пункту
    хотя на уровне popen и readdir они и правда неплохи
  • @SannySanoff, Ну да, все правильно. Я и сам не помню названия и страдаю от отсутствия автокомплита. Но в хаскеле, правда, почти всегда понятно, какой нужен тип, так что можно хуглом искать. (для всяких лексахов и еклипсФП, кстати, нужно вот какой автокомплит делать — начинаешь тип набирать — выпадает список функций). Ну и "записи" в хаскеле, понятное дело, говно полное. Вот только хаскель я нормально могу читать — а жаву — нет. Пока продираюсь через экраны кода, который если разобраться, вообще ничего не делает, до какого-то осмысленного участка, уже и забываю предыдущий осмысленный фрагмент. Кроме того, большинство кода и прочесть-то нельзя — только инетерпретировать в уме.
  • @max630,

    если подумать, как я раньше на голом Ц писал?
    API были короче? curses, oracle call interface, posix.. как мало было нужно для щастья.
  • @SannySanoff,

    я придумали фичу для лексаха, определяешь, какой символ в конкретном месте имеет смысл, и автокомплетишь по малому набору символов.
    остановка за малым 8)
    надо писать, ей-ей!
  • @SannySanoff,
    придумалЪ.

    хотя в случае let n = [cursor here] сложно что-то улучшить.
  • @SannySanoff, В хаскеле в любом конкретном месте имеет смысл куда больше символов, чем в яве.
  • @max630, Об оптимизации в случае GHC как раз говорить можно. Высокоуровневый код лучше вообще никто не оптимизирует. Ну а то, что на других языках высокоуровневый код почти не пишут — это уже другая история.
  • @klapaucius,

    так отож, как и в китайском языке (говорю с уважением к обоим).

    кстати, еще интересный НЮАНС.

    Вот тут на реддите в каментах говорят: "не гоните на слоган <скомпилилось — значит работает>. Если вы пишете в IO монаде, то может быть и будут глюки. Потому что вы просто не перестроили мышление. Заструячьте такие нехилые типы, что шаг влево шаг вправо у вас будет караться — и компилящиеся программы автоматом будут корректными"

    Мысль хорошая. Вот я грокнул, бахнул, и заструячил нехилый стек с нехилыми данными. И Петя заструячил, и Вася. И у всех всё очень круто.

    Потом пришёл Николай и клянет нас, т.к. ему это всё надо подружить, а мы каждый о своём.

    Вопрос: кто крайний? (вариант: кто делает не так?). Все делали по рекомендациям, а страдает Николай.

    Конкретный пример: одно API возвращает Either в своей милой монаде, другое кидает ексепшны в IO, а третье вообще молчу. А действие у Николая происходит в монаде Handler YESOD-а (кто понимает).

    Вот вопрос!
  • @klapaucius, А рекурсивную функцию в constant space он сможет оптимизировать? А gcc может:http://ridiculousfish.com/blog/posts/will-it-optimize.html
  • @SannySanoff, Это ты хороший вопрос поднял. Как по мне, для того, чтобы язык был успешным, оговариваться должны все аспекты его использования. Языком должен определяться Styling Guide и Indentation (как в Python, например). Должны описываться правильные варианты использования и высокоуровневые паттерны (как в Erlang, например). В Haskell такого нет. Как нет полного набора ни в одном из языков/платформ
  • @tilarids,

    В хаскеле этого нет, потому что язык слишком выразительный. Как подружить между собой несколько творческих личностей? 8)
    В то же время, я не думаю, что "единообразно как в армии" — это выход.

    А когда ответ получается "и то плохо и то плохо", то, как показывает практика, настоящим ответом является "плохо у тебя в башке". Шо же делать-то?
  • @max630,

    я не понял, но кажется это наезд. Чем tail recursion не constant space?
  • @SannySanoff, там нет tail recursion
  • @max630,

    там нет tail recursion

    я не ошибся, и речь идет про GHC?
  • @SannySanoff, речь идет о коде. Там не хвостовая рекурсия. gcc её оптимизирует
  • @SannySanoff, Ну, и что, что выразительный? Вот Python тоже выразительный, но есть же благословлённый Гвидо PEP-8. Почему такого нет в Haskell? Или Erlang — простой, но весьма мощный, функциональный язык. Но при этом есть отличная дока о том, как писать свой код так, чтобы не страдал Николай
  • @max630,
    а, да, я увидел. Я, в общем, думаю, это школьник прикрутил частный случай в gcc, наверное. Low hanging fruits, you know.
  • @tilarids,
    я бы сказал, что питон не выразительный в смысле user defined types. поэтому сравнивать не надо. разные измерения. то же с erlang. Чтобы подтвердить свой point, я напомню, что речь началась о том, что чуваку типы мешают (или не помогают), что в случае указанных языков not applicable.
  • @max630,
    там гцц правильно предположил, что это pure function. Стоит вызвать в ней какую-то другую функцию, как всё пропало.

    функция-факториал погубит авторов компиляторов. Слишком идеальный пример 8)
  • @tilarids, а можно аналог PEP8 для эрланга? помнится на такое ответили, что нет жёстких требований
  • @GooRoo,
    Я вместе с тем думаю, что на хаскеле решать нынешний aichallenge будет very OK!
  • @qrilka, Там нет PEP8. Но там есть Design Principles. То, что спасло бы Николая
  • @tilarids, только вот касается оно OTP, библиотеки (без которой, конечно, эрланг не особо интересен)
  • @SannySanoff, да, что-то такое я видел. По-моему надо делать алгоритмы в наиболее общем виде, а потом склеивать их отдельным кодом.

    А так должен кто-то сесть и переписать весь hackage с использованием одних механизмов. Или хотя бы порешать и расставить флажки — вот тут использовать то, там это.
  • @max630,
    если я руками должен данные анбоксить

    -funpack-strict-fields
  • @PineappleZombie, дада, и "!" ручками расставлять.
  • @SannySanoff, Да я как раз вчера про него узнал и уже навострил по этому поводу... эээ... что тут можно навострить? пальцы что ли :3 В общем, попытаюсь закрепить вновь приобретëнные хаскель-познания. Будет интересно, я думаю.
  • @max630, Да. И анбоксить структуры просто так компилятор права не имеет. Это преодбразование меняет семантику программы

    data T = T Double ⇒ T ⊥ ≠ ⊥
    data T = T !Double ⇒ T ⊥ = ⊥
  • @PineappleZombie, вот это и плохо. А такое изменение семантики — это из разряда a[++i] = ++i. На правильно написанные программы не влияет.
  • @max630, Такое изменение ни на что не влияет только в программе на неполном по тьюрингу языке с проверкой завершимости.
  • @tilarids, "Python тоже выразительный" "Erlang — весьма мощный" Я надеюсь, вы это не всерьез говорите?
  • @klapaucius, я не говорил что это "ни на что ни влияет", я иначе выразился
  • @max630, Ну и что вы подразумевали под словами "На правильно написанные программы не влияет."?
  • @klapaucius, Don't feed the troll!
  • @max630, Ха! Т.е. вы предлагаете f h : map f t превращать в цикл, который будет копить список в аккумуляторе? Чумовая идея! Правда, обычно предполагается, что при оптимизации программа работает быстрее, ну и вообще — работает.
  • @klapaucius, зачем? там же есть конечный код, ничего никуда не копится
  • @max630, Вы про
    int factorial(int x) {
    int result = 1;
    while (x > 1) result *= x--;
    return result;
    }
    ? Ну так result — это и есть аккумулятор. Представьте себе map или foldr, переписанный аналогичным образом. Никаких проблем не видите?
  • @klapaucius, Конкретно, например, мне как-то надо было чтобы в Map value было строго. И map с ленивым value у меня уже есть (и вообще библиотечный). Для превращения его в строгий map мне потребовалось бы скопипастить реализацию Map буква в букву с добавлением "!". Конечно, я не стал этого делать, обошёлся ручным форсированием. Но это очевидный костыль.
  • @klapaucius, я не вполне понимаю зачем мне представлять что-то не то что есть. Функция уже есть, и она вот такая какая есть.

    Кстати, а если бы там был foldR вместо foldL — это как-то повлияло бы на результат? Да, я знаю, | 0 — это совсем не то что 0 |. И поэтому все последующие, кто решил использовать хаскель должны тщательно думать, какую буковку к fold приписать.
  • @max630, Не понял, что значит "какая есть"? Вы же оптимизацию предлагаете, которая делает map 1) медленнее 2) неправильным для некоторых вариантов входных данных, на которых "неоптимизированный" map нормально работал.
    foldr дает результат, отличный от foldl в любом языке (исключение — сворачивающая операция мат.ассоциативна), думать тут особо не над чем.
  • @klapaucius, *code
    исключение — сворачивающая операция мат.ассоциативна
    к + и *, разумеется, это "исключение" не относится
  • @max630, Хотя вообще, разница между foldr и foldl в разных языках разная. В строгих foldl работает плохо, а foldr еще хуже, а в ленивых foldl работает плохо, а foldr — лучше. Но это некоторое упрощение.
  • @max630, Ну почему не относится? да, сложение и умножегние мат.ассоциативны. А конс — нет.
  • @klapaucius, Это, я бы сказал, наговор. ghc всё-таки может для простых типов сообразить, что считать надо строго:

    r = foldr1 (+)
    l = foldl1 (+)
    main = do
    a <- getArgs
    print ((case a !! 0 of
    "r" -> r
    "l" -> l)
    [0 .. (read $ a !! 1)] :: Int

    ./Fold l 500000 0.00s user 0.00s system 48% cpu 0.008 total
    ./Fold r 500000 0.42s user 0.00s system 95% cpu 0.436 total

    При дальнейшем увеличении длины списка и foldr вовсе вылетает за стек.

    Но посмотрим, каковы же возможности высокоуровневой оптимизации:
    m = getSum . mconcat . map Sum

    ./Fold m 500000 0.32s user 0.01s system 99% cpu 0.330 total

    не фонтан. Я бы даже сказал, совсем не фонтан:
    $time ./Fold m 2000000
    Stack space overflow: current size 8388608 bytes.
    Use `+RTS -Ksize -RTS' to increase it.
    ./Fold m 2000000 1.44s user 0.01s system 98% cpu 1.485 total


    Хотя, казалось бы...
  • @max630, Собираешь с -O2 ?
  • @max630, да что ты будешь делать, опять форматирование слетело...

    код тут: pastebin.com
  • @max630, foldl он тебе соптимизировал. Sum не смог.
  • @PineappleZombie, спасибо, Кэп. Так что у нас с беспрецедентной высокоуровневой оптимизацией? Я выл бы рад хоть какому-то её примеру.
  • @max630, stream fusion. DPH
  • @PineappleZombie, stream fusion принимается, хотя появление packed strings наводит на мысль что там не всё так хорошо как хотелось бы (и действительно, единственный раз когда мне нужна была скорость мне так и не удалось добиться быстрого чтения данных). Про DPH не знаю, надо искать задачу и смотреть.
  • @max630, Stream fusion как раз для packed strings (Text) и работает и для массивов. Для списков используется build/foldr fusion
  • @max630, Со строгими операциями типа арифметики проблемы будут всегда и в любом языке. С ленивыми вроде (:) и (&&) эффективно сворачивать можно. Собственно, полезных случаев с консом куда больше, чем с арифметикой (тут только длинна, сумма и произведение, фактически). В строгом языке эффективный map и foldr для алгебраических, иммутабельных списков вообще нельзя реализовать — приходится мутабельные списки использовать.
  • @max630,
    Так что у нас с беспрецедентной высокоуровневой оптимизацией? Я выл бы рад хоть какому-то её примеру.
    rsdn.ru/Forum/Message.aspx?mid=4237532&only=1
    rsdn.ru/Forum/Message.aspx?mid=4327406&only=1
    Какой еще компилятор такое может сделать?
  • @max630, Не забывайте, что packet strings еще и для интеропа нужны
  • @PineappleZombie, для списков есть пакет stream-fusion, кандидат на замену build/foldr в прелюдии.
  • @klapaucius, по-моему, вы издеваетесь надо мной. Ещё 2 фолда, на сей раз со штрихами?
  • @max630, Какие два фолда? С какими еще штрихами?
  • @klapaucius, ok, один:
    foldl' :: (a -> b -> a) -> a -> [b] -> a
    O(n), fusion. A strict version of foldl.
  • @max630, Откуда вы это взяли? Два примера. В первом код
    foo n = S.sum . S.map (+1) . S.unfoldr (\i -> if i > n then Nothing else Just (i, i + 1)) $ 1.0
    оптимизируется в
    loop_sum (acc :: Double#) (i :: Double#) =
    case i >## 1.0e8 of
    True -> acc
    False -> loop_sum (acc +## i +## 1.0) (i +## 1.0)
    Второй пример, код
    or_modern = foldr (||) False
    any_modern p = or_modern . map p
    оптимизируется в
    go = \ds ->
    case ds of
    [] -> False
    y1 : ys1 -> case y1 of D# x2 ->
    case x2 >## (x1 /## 2.0)
    False -> go ys1
    True -> True
    Много вы сможете компиляторов назвать, которые могут что-то аналогичное? Вот в этом беспрецедентность и заключается.
  • @klapaucius, выглядит все унылым кавырянием в гипотетических задачах
  • @ramok, И в чем здесь унылость? В том что язык-который-вы-используете вообще ФВП не оптимизирует, а потому, оптимизация ФВП — это "гипотетическая задача" и ненужно? Ну так и пишите тогда.
  • @klapaucius, я про документацию на Data.List.Stream.
  • @klapaucius, только сейчас догадался запустить свои примеры с трейсом gc. вы хотите сказать, что списки по умолчанию не фузятся вообще?
  • @max630, По какому умолчанию? Полная дефорестация при build/foldr-fusion будет только в том случае, когда есть хорошие продьюсеры и хороший консьюмер. foldl — прохой консьюмер, в этом, собственно, и причина того, что для build/foldr подберают лучшую альтернативу.
  • @max630, И чем вам функции со штрихами там не понравились? Это же drop-in для Data.List
  • @klapaucius, да, действительно, проглядел. Ничего хорошего по этому поводу сказать не могу.
  • @max630, Ну я и спрашиваю — почему?
  • @klapaucius, например, потому, что если (+) строгий по обоим параметрам, то почему же foldl (+) может вообще оказаться ленивым?
  • @max630, Он, при включенном оптимизаторе, и не окажется.
  • @klapaucius, тогда зачем отдельный строгий вариант
  • @max630, 1) Для обратной совместимости 2) Считается, что кроме GHC еще еще какие-то "компиляторы" хаскеля, что конечно, фантастика.
  • @max630, И да, вопрос, скорее, не в том, зачем строгий вариант, а зачем кто-то в свое время написал нестрогий foldl. Нет, какие-то экзотические применения для него можно найти, но все равно, есть в этом что-то абсурдное.
  • @klapaucius, Сам пакет не смотрел, но насколько помню из статьи, там были проблемы с ленивостью. Поведение функций отличалось от указанного в стандарте
  • @PineappleZombie, Странно, ничего такого не припоминаю. Помню, что для concatMap нужныбыли каике-то хитрые правила перезаписи, с которыми GHC не справлялся и потому в некоторых случаях build/foldr работал лучше.
  • @klapaucius, Посмотрел статью (§6.1). Там, в зависимости от строгости функции stream получались немного разные следствия для корректности правила перезаписи. Похоже можно сделать.

    vector, впрочем, похоже стал the array library
  • @PineappleZombie, ну vector-то уже пару лет — the array library, по крайней мере, мне сложно понять, зачем использовать что-то другое (ну да, с Ix не так удобно, но это богатством api компенсируется). Сейчас, собственно, предполагается, что bytestring заменят на vector. Но пакет stream-fusion — оптимизация для списков. Или она никому не интересна потому, что если кого-то скорость интересует — он vector использует?
  • @klapaucius, Может выигрыш не стоит затрат.
  • @PineappleZombie, В некоторых случаях выигрыш очень существенный. Понятно, что делался обычный тест nofib который ничего не показал, но, как верно замечено в каком-то докладе с прошлой кажется haskell implementors workshop, nofib такой бенчмарк, который имеет тенденцию показывать, что оптимизация бесполезна.
  • @klapaucius, Это надо на ghc-users спрашивать, наверное. Вариантов, собственно, три: какой-нибудь шоустоппер, некому, и то и другое.
  • @klapaucius, Ясно. Некому.