Потому что лучше всего это получается, когда некоторое время (пару лет) работа — это жизнь (в понимаемом исходными комментаторами смысле, то бишь, когда на неё тратится «слишком много» времени).
Мне вот хочется отпуск на пару лет периодически, и в Европе он не очень достижим, а в Штатах — вполне. В итоге в Штатах получается работать меньше, а получать — больше.
Тем что это может стать нормой и для крупных корпораций и вообще везде нормой для всех.
Однако, не стало за многие десятки лет наличия таких компаний.
И нормой даже не потому, что крупными корпорациями руководят злодеи, а потому что те, где работают меньше не будут выдерживать конкуренции.
Лейтмотивом всех таких дискуссий идёт, что изнашивающие интеллектуальных работников компании на самом деле проигрывают, потому что люди не могут так работать, выгорают, ошибаются, и так далее. Поэтому тезис о том, что неизнашивающие компании проиграют гонку, мягко скажем, сомнителен.
Но поинт интересный, конечно. Давайте проведём мысленный эксперимент. Если бы это было реализуемо, то выступали ли вы бы за законодательный запрет, скажем, саморазвития по вечерам и выходным? Ведь те, кто не впахивает, и у кого семья-хобби-етц, может проиграть конкуренцию тем, кто впахивает, и впахивать в итоге придётся всем, разве нет?
Мы в айти как-то привыкли к благам узкого периода, когда айтишники были в остром дефиците и могли выставлять свои условия работодателям, но это время уходит в прошлое, статей про проблемы с наймом масса и на Хабре и в англоязычных источниках и дальше будет хуже.
По личному впечатлению от рынка и англоязычных источников — во-первых, коррекция ковидного найма всё ещё не случилась до конца. Во-вторых, основные проблемы у тех, кто пришёл только за деньгами, а не за интересом: оказывается, что стучать по клавиатуре недостаточно, надо ещё иногда думать, а без желания этого делать как-то тяжко. В-третьих, конкретно сейчас есть некоторый экономический спад, напрямую с айтишечкой не связанный.
Собственно называть Google стартапом несколько наивно - это та самая крупная корпорация и 60 часов там уже не пределом считаются, а "золотой серединой" судя по высказыванию их начальства.
Рекомендую судить не по высказываниям начальства, а по делам. Там работающие люди всё ещё не обязаны работать по 60 часов.
Я работал в одной похожей компании (и по объёму, и по позиции на рынке, и так далее). Ну, да, там был некий дискурс одобрения послеурочной работы и демонстрации впахивания, и CEO компании как-то разослал письмо с текстом вроде «Хотите знать, как я пришёл к успеху? Приходил на работу раньше всех и уходил позже всех.» Но собака лает — караван идёт, и можно было спокойно продолжать работать с 9 до 5 (ну или с 12 до 8, в моём случае). Не могу сказать, что на той работе я как-то выгорел.
А вот, кстати, когда пришлось с французами поработать, там было как-то на самом деле потогонненько местами, да. Хотя, казалось бы, евросоциализм, все дела.
Ваше представление о сопоставимости результата может сильно отличаться от реального. Утверждать не буду, надо знать детали.
Ну это ж не поэзия и не философия, тут есть объективно измеримые критерии успеха.
Например, есть набор тестовых программ, которые компилятор (эта штука на 5к строк — компилятор для предметно-специфичного языка, да) должен успешно компилировать. Если реализация A компилирует, скажем, 90% набора в правильно исполняемый код, а реализация B — 40%, и на пересечении этих множеств исполняемый код от реализации A работает настолько-то быстрее, то вполне можно сказать, что результат не просто сопоставим, а кратно лучше.
Нет. Это ошибочная логика. Впрочем на вкус и цвет. Гугл создал Го, в котором не разгуляешься и не выстрелишь себе в ногу. Но в этом же его беда. Впрочем, гуглу виднее - наверное так быстрее набирать кодеров.
Я тоже не люблю го. Но да, задачи гугла го решает отлично: это хороший инструмент по критерию «можно взять вчерашнего выпускника и посадить писать микросервисы». Плюсы — хороший инструмент по критерию «широкая доступность работы и интеграция с легаси-системами». JS — хороший инструмент по критерию «работает в браузере». Хаскель — хороший инструмент по критерию «о коде легко рассуждать, глядя только на типы, код сразу работает после рефакторинга, поддерживается type-driven development, и так далее». Он не обязан при этом быть хорошим для использования вчерашними выпускниками, или работы в браузере, или интеграции с легаси-системами.
Инструментарий - это не только типы и конструкции языка. Это дебаггер, компилятор, это выбор гуя. Со всеми этими пунктами у хаскеля плохо.
У меня всё больше впечатление, что вы не знаете, о чём говорите. Почему с компилятором-то плохо, например? ghc — это лучшее, что у меня было.
Выбор гуя — да, здесь согласен. Гуй на хаскеле я бы делать не стал. Правда, я бы его ни на чём бы делать не стал, но это другой вопрос.
Неужели вы хотите прожить жизнь, занимаясь неудовлетворяющей вас деятельностью? И вы готовы с этим мириться, если вам за это немного платят?
Нет. Я хочу проработать несколько лет, занимаясь неудовлетворяющей меня деятельностью, так как за неё достаточно платят, чтобы потом несколько лет не работать.
Счастье для человека - когда он за деньги делает то, что делал бы и бесплатно. А для компании счастливый сотрудник - это надёжное вложение, потому что он будет эффективно работать в компании до самой пенсии.
Проблема в том, что компании нужна предсказуемость, ответственность, и далеко не всегда там выходит заниматься только лично интересным тебе.
Не существует компаний, которые могли бы конвертировать, скажем, моё желание читать книжки, изучать теоркат и писать всякий код по фану (до уровня proof of concept, не доводя до продакшена), в деньги, которые они бы могли бы потом мне платить.
Это пример, показывающий, почему «крупные проекты» по вашим определениям вам не видно.
У меня есть ещё одно объяснение, но оно вам не понравится. Я промолчу, потому что не знаю деталей.
Мне скоро спать пора, а как я уснуть смогу, не зная, что там за объяснение такое?
Пример худшего - плохой пример. ООП - инструмент. Им можно хорошо уметь пользоваться, можно пользоваться во вред себе.
Если инструмент устроен так, что им легко отрезать себе пальцы, то это плохой инструмент. Если в инструмент добавлены защиты, чтобы обе руки не попадали в рабочую зону при работе — это хороший инструмент.
Говорите, как будто это что-то плохое.
Хорошее, конечно. Мне потом неплохо платят, чтобы с результатами такой стратегии разбираться.
У чистого ФП гораздо хуже инструментарий управления сложностью.
Потому что ведь нельзя, чтобы просто в типах всё было видно, да? Надо, чтобы мышкой надо было поелозить в IDE, и чтобы комментарии были устаревшие. Ведь основная сложность для нас — это поиск работы, соответственно борьба со сложностью — это обеспечение job security.
Там низкий порог входа за счёт отсутствия ограничений по типам, необязательного структурирования данных. Что вижу - о том и пою. Шарп, java - это языки другой весовой категории, где есть продвинутый инструментарий для управления сложностью.
Потому что 5 и 50 - это цифры разного порядка. Я смотрю сейчас на свой проект. Вот основная часть, где 36к без тестов. Вы её никогда в жизни не уложите в 3600 строк.
А я смотрю на свой проект, где 5к делало больше, чем несколько десятков к соседней команды. И? Какой вывод?
Иначе мы бы видели засилие ФП в больших и сверхбольших проектах. А там традиционно ООП.
Если под «традиционным ООП» понимать «нафигачили паттернов по книжкам, в итоге вообще ничего не понятно», то да, с таким я встречался.
Если чуть серьёзнее, то разгадка одна: на мейнстримных императивных языках можно быстрее выдать что-то, как-то более-менее работающее. Разработка софта — это жадный алгоритм, поэтому имеем то, что имеем.
Я вот читаю весь этот снисходительный пафос и не пойму... А где решение в 10 раз короче?
Сразу после списка.
То, что я написал - работает.
Не работает, и я написал, почему, в половине пунктов списка.
И не работает с точки зрения поддерживаемости крупных проектов в долгосрочной перспективе, и об этом вторая половина списка.
У вас loadComments использует готовые структуры языка/библиотек и выдаёт данные, подогнанные к условиям задачи.
А если бы там хэшмапы ещё участвовали, то вы бы сказали, что пример подогнан под хэшмапы? Или хэшмапы типа можно, потому что они в C# есть из коробки?
Ну возьмите готовую библиотеку с реализацией деревьев, я разве против?
Вот так говорить вообще не надо. Это в крайней степени некорректно.
По причинам несоответствия этого утверждения объективной реальности, или потому, что звучит неприятно?
Если второе, то я-то думал, что мы тут с вами открыто и смело и прямо в лицо, 359!
Делать выводы по оторванному от реальности куску кода - это какая-то специальная олимпиада. Нет, серьёзно. Это действительно похоже на какую-то олимпиаду, когда люди соревнуются в написании куска кода по условиям задачи.
Демонстрационный пример про выразительность языка.
Говоришь про примеры из практики (которые в комменты не влезают) — не подходит. Приводишь примеры, которые в комменты влезают — не подходит. Ну вы скажите, что это всё для вас вопрос веры, и я отстану тогда, ё-моё.
Это тоже некрасивый приём. В моём коде таких конструкций нет. Вы её сами продумали.
Конечно, лучше писать «типичной ФП-сплющенной чёрной магией, где за смысл отвечают знаки препинания, а не слова».
Меня это не оскорбляет, я вообще так-то матершинник, вырос на двач-adjacent-культуре, и кожа у меня толстая. Единственное, что оскорбляет и задевает лично меня — это двойные стандарты, поэтому я оставляю за собой право отвечать в том же стиле, в котором говорит мой собеседник. Есть шанс, конечно, что я интерпретирую стиль неправильно, но тогда уж прошу простить. Искренне.
Я вижу, что чьё-то самолюбие задето. Настолько, что потребовалось вот как-то доказать превосходство хаскеля над шарпом.
Любое предметное обсуждение — задетое самолюбие и потребность в доказательстве чего-то там? Лол.
Вот это мне вообще не понравилось. Прямо какой-то дон в белом плаще смотрит, как там крестьяне палкой землю ковыряют.
Есть удачные базисы, по которым можно раскладывать сложность, и неудачные. Если обсуждение и сравнение этих базисов для вас выглядит как вот описанная сцена, то это, конечно, печально, я бы так относиться к этому не стал.
Сначала по коду — это, конечно, лол. Код превзошёл мои ожидания, но не в ту сторону, в которую вы думаете.
У вас почему-то связана концепция «рекурсивная структура данных такой-то формы» и «что может лежать в каждом узле этой структуры»: Node (в ваших ООПных терминах) сильно связано и с хранением детей, и с хранением конкретных данных в узле. Сравните с определением Tree/Node, которые я давал выше.
Концепция «обойти дерево» тоже связана с содержимым этого дерева. Но я всё эти связности могу понять: книги по ООП учат разделять кошечек, собачек, квадратики и прямоугольнички, а не действия. В ООП вообще не принято думать в терминах действий, потому что действия не являются first-class. Чтобы выразить действие, апологет ООП (ООПологет?) навернёт десяток паттернов из фабрик визиторов стратегий.
У вас никак не выражаются инварианты на уровне типов (да и типов нет, stringly-typed комментарий и ошибка не считаются). Комментарий может быть, а может и не быть. Ошибка может быть, а может и не быть. Они могут не быть оба сразу. Они могут быть оба сразу. По чтению определения данных ничего непонятно, надо читать алгоритм, чтобы понять, что происходит. И в вызывающем коде надо проверять явно их наличие (забыл проверить ошибку — твоя проблема). Сравните с Either NetworkError Comment — там всё сразу видно, и обратиться к комментарию, если его нет, невозможно.
Собсна, дерево до загрузки комментов и дерево после загрузки комментов на уровне типов тоже никак не отличаются. Сравните с Tree Int и Tree Comment (или Tree (Int, Comment), если вашей душе так угодно). Type-driven development? Не, не слышали.
LoadComments почему-то является нестатическим членом класса, хотя к полям экземпляра класса никак не обращается. Как читающему только тип функции понять, как связана та Node, которая this, и та Node, которая передана как node?
Func<int, string>, конечно, понятнее и синтаксически чище, чем Int -> String (который у меня с лигатурами выглядит как Int → String) Читать Func<int, List<int>, string> тем более легче, чем Int → [Int] → String — заодно глаза тренируются скобочки считать и различать, в старости пригодится как защита от Альцгеймера.
Кстати, как у вас выражается rank-n-полиморфизм? Как будет выглядеть тип функции, который на хаскеле выражается как forall a. Show a => a -> a -> String, скажем? Ну, то есть, функция, которая принимает два аргумента (одного и того же) любого типа, реализующего интерфейс IShow(и возвращает строку). Что нужно будет написать вместо ... в Func<...>? Ой, я по ассоциациям ещё про multiparam typeclasses вспомнил, но не стоит вскрывать эту тему, это уже будет просто избиение младенцев.
Я не могу посмотреть на результат LoadComments и понять, произошла хотя бы одна ошибка в дереве или нет (это вообще было одним из условий, про которые я регулярно говорил). Мне после вызова LoadComments надо будет обойти дерево ещё раз. И чтобы собрать ошибки в список, его тоже надо будет обойти.
try/catch ловит все ошибки и теряет информацию о типе, тупо записывая Message. Я просил весь тип ошибки целиком. Олсо, это совершенно неподдерживаемый код: если раньшеloadComment по каким-то причинам не кидала ошибок (например, потому что разработчик как настоящий фанат OOP, TDD, SOLID, CBT и SPH сначала всё мокал, а моки ничего не кидали, скажем), и поэтому вы не написали try/catch, то после того, как loadComment таки начнёт кидать ошибки, вы, конечно, свой код не обновите, потому что компилятор вам про это не скажет. Зачем так жить в 2025-м году?
Что-то ещё было, но пока я мучался с интерфейсом firefox'а, чтобы отделить вкладку и перенести её на второй вертикальный экран, потому что на моём горизонтальном экране список вытеснил ваш комментарий с кодом, я это что-то забыл, но, думаю, и так хватит. И так уже число комментариев совпало с числом непустых и не-чисто-синтаксических строк вашего кода.
Сравните с моим вариантом, который я себе позволю одной строкой, раз цепочки функций вам норм:
Всё. Дерево (Tree, это тип из стандартной либы) обходится (в traverse, это стандартная функция). Ошибки собираются (в sequenceA, это стандартная функция). Разделение на ошибки, комментарии и структуру есть. Validation (единственная не оч стандартная, но достаточно известная штука, которую я подключил: вопрос дописывания validation в package.yaml) гарантирует семантику «либо ошибки, либо дерево комментов». NonEmpty (тоже тип из стандартной либы) гарантирует, что есть хотя бы одна ошибка.
Господи, да этот код даже тестировать не надо, он просто работает. Там нечему ломаться и негде ошибиться.
Если не наглеть и сделать, как я бы сделал для прода, то я вынес бы обёртку для loadComment отдельно, и всё вообще читабельно без лишних скобочек:
loadComments :: Tree Int → IO (Validation (NonEmpty NetworkError) (Tree Comment))
loadComments = fmap sequenceA . traverse loadCommentValidating
where loadCommentValidating = fmap (liftError pure) . loadComment
Современные IDE показывают, какие эксепшены может кинуть та или иная функция.
Это алгоритмически неразрешимая задача. И я обычно пулл-реквесты, например, ревьювлю в интерфейсе гитхаба, и диффы смотрю там же (или в консоли c git show / git diff). Там IDE как-то не оч работает.
В каких-то языках в комментах пишут.
Комменты-то у нас, конечно, не устаревают и проверяются компилятором при каждой сборке.
Ещё в комментариях можно типы переменных писать (как в старом JS или во Flow, только без запуска тайпчекера Flow). Можно даже имена там писать, как в ассемблере!
mov ecx, [ebx] ; в cx счётчик цикла
В java принудительно нужно указывать в сигнатуре, какие эксепшены можешь кинуть.
Да, checked exceptions — самая близкая из упомянутых вами вещей к Either. Правда, костыльная, без полиморфизма по экзепшонам, и так далее, поэтому на практике малоюзабельная.
Всмысле перевести? Мне функцию дали. По условию. Где сигнатура?
А тут вам в условии дали фору: сигнатуру можете выбрать как вам удобнее.
Если по своему, то во-первых, возвращение NetworkError - чушь собачья. При живых эксепшенах.
Это чтобы потом вместо того, чтоб просто посмотреть на тип функции, ходить спрашивать «а что функция кидает?», как вы уже сделали выше?
Но, впрочем, я и про это написал! Во:
Но если вам удобнее с исключениями, то можете и с исключениями, конечно, но тогда вам придётся отобрать те, которые соответствуют толькоNetworkError, и собрать их все.
Во-вторых, запрос коммента по сети по одному - чушь собачья.
Вы реально по кругу ходите. Мы с вами это уже обсудили.
Например ошибка кредов - вот зачем дальше выбирать эти комменты?
Вы точно не понимаете, что такое модельная задача.
Решите уже эту, наконец, и обсудим границы применимости, усложнения и прочее.
Никак. Это не хаскель.
ФП что-то быстро сдулось. А аналог уже даже в плюсы завезли в виде std::expected.
И шарп статически типизированный. Там нельзя инт поменять на коммент.
Ну хаскель-то известен своей динамической типизацией, конечно.
В данном случае это значит «вернуть такое же по форме дерево, но в котором на данной позиции вместо инта лежит соответствующий этому инту комментарий». Сорян за функциональный жаргон.
Чтобы перевести её на шарп так, как вы считаете наиболее идиоматичным. Я на шарпе не писатель, только читатель, и так как вы говорили, что используете ФП в шарпе (следовательно, знакомы с концепциями) и не переносите синтаксис хаскеля (следовательно, знакомы с ним), мне представляется разумным предположить, что вы её прочитать и перевести сможете.
Впрочем, возможную сигнатуру шарпа я тоже приводил выше.
У шарпа есть тип возвращаемого значения. Входит в сигнатуру функции.
Пока ничего нового, всё как на хаскеле.
Там ещё входные параметры, но допустим на входе int id. Какой тип возвращаемого значения?
У загружатора одного комментария? Либо NetworkError, либо Comment. Как выражается «либо» в шарпе идиоматически, я не знаю. В хаскеле это Either.
Вы по кругу просто ходите, что ли? Сигнатуру я уже тоже выше писал:
loadComment :: Int -> IO (Either NetworkError Comment)
Чтобы два раза не вставать. Int нельзя заменить на строку. И на Comment тоже.
Не понял. Кто запретил?
GHCi, version 9.12.2: https://www.haskell.org/ghc/ :? for help
λ> import Data.Tree
λ> t = Node 10 [Node 20 [], Node 30 []]
λ> :t t
t :: Num a => Tree a
λ> :t fmap show t
fmap show t :: Tree String
λ> fmap show t
Node {rootLabel = "10", subForest = [Node {rootLabel = "20", subForest = []},Node {rootLabel = "30", subForest = []}]}
А, чисто для протокола — на исключения можете забить и считать, что их нет. Но если вам удобнее с исключениями, то можете и с исключениями, конечно, но тогда вам придётся отобрать те, которые соответствуют толькоNetworkError, и собрать их все.
Полный формат функции. Что принимает, что выдаёт, чем кидается.
В типах же описано! Принимает Int, выдаёт либо объект типа NetworkError (дан свыше), либо объект типа Comment (тоже).
Вам нужно получить Tree Int (дерево с интами в узлах), для каждого инта загрузить комментарий через loadComment, и выдать либо список возникших ошибок (если возникла хотя бы одна), либо дерево, где каждый Int заменён на соответствующий комментарий.
Я очень рад, но проект на 5к строк нельзя назвать большим. И средне-большим. И даже средним. Никак.
Функция loadComment. Пришла из васиной библиотеки, которую он написал для работы со своим сервисом.
Если это какой-то бекенд, то давайте сначала ПОЛНЫЙ текст на хаскеле обработки апи. Вместе с загрузкой дерева, вместе с отправкой комментов. Вместе с сериализацией. А потом будем сравнивать именно полный текст.
Вы не понимаете, что такое модельная задача. Ну ок.
Я так и сделал.
Нет, не сделали, потому что нет законченного кода, используемого данную вам loadComment из васиной библиотеки и любой удобный вам тип данных для представления деревьев.
В чём проблема? Вы даже не потрудились сказать, где может ошибка возникнуть.
В функции loadComment, как это было ясно из её типа.
Потому что там нет места функциональщине. Там выключен Нейгл и включено на максимум байтоёбство.
Оххх бро…
Сколько раз я занимался байтоёбством на хаскеле и получал код, работающий на уровне плюсов (а иногда чутка быстрее) — не перечесть. В худшем случае объём вычислительного кода сравним с объёмом кода на плюсах.
Да, ещё раз скажу. 5000 строк - это даже не маленький проект. Это крохотный. Не надо говорить, что хаскель компактный. Это не так.
Он делал больше, чем реализация соседней группы, делавшей ту же задачу на плюсах, которую до меня припрягли делать то же, которая делала это втроём, которая делала это пару лет (я справился за пару месяцев чистого времени как сайд-проект), и которая это в итоге так и не доделала до конца. Сколько там строк кода было точно, я не знаю, но точно больше нескольких десятков тысяч.
Потому что лучше всего это получается, когда некоторое время (пару лет) работа — это жизнь (в понимаемом исходными комментаторами смысле, то бишь, когда на неё тратится «слишком много» времени).
Мне вот хочется отпуск на пару лет периодически, и в Европе он не очень достижим, а в Штатах — вполне. В итоге в Штатах получается работать меньше, а получать — больше.
Американцы вообще так в целом живут. ЕМНИП они переезжают в среднем около 12 раз за жизнь, и с 18 до 35 меняют съёмную квартиру как раз в 2-3 года.
Кажется, вы обсуждаете слегка разные вещи.
Однако, не стало за многие десятки лет наличия таких компаний.
Лейтмотивом всех таких дискуссий идёт, что изнашивающие интеллектуальных работников компании на самом деле проигрывают, потому что люди не могут так работать, выгорают, ошибаются, и так далее. Поэтому тезис о том, что неизнашивающие компании проиграют гонку, мягко скажем, сомнителен.
Но поинт интересный, конечно. Давайте проведём мысленный эксперимент. Если бы это было реализуемо, то выступали ли вы бы за законодательный запрет, скажем, саморазвития по вечерам и выходным? Ведь те, кто не впахивает, и у кого семья-хобби-етц, может проиграть конкуренцию тем, кто впахивает, и впахивать в итоге придётся всем, разве нет?
По личному впечатлению от рынка и англоязычных источников — во-первых, коррекция ковидного найма всё ещё не случилась до конца. Во-вторых, основные проблемы у тех, кто пришёл только за деньгами, а не за интересом: оказывается, что стучать по клавиатуре недостаточно, надо ещё иногда думать, а без желания этого делать как-то тяжко. В-третьих, конкретно сейчас есть некоторый экономический спад, напрямую с айтишечкой не связанный.
Рекомендую судить не по высказываниям начальства, а по делам. Там работающие люди всё ещё не обязаны работать по 60 часов.
Я работал в одной похожей компании (и по объёму, и по позиции на рынке, и так далее). Ну, да, там был некий дискурс одобрения послеурочной работы и демонстрации впахивания, и CEO компании как-то разослал письмо с текстом вроде «Хотите знать, как я пришёл к успеху? Приходил на работу раньше всех и уходил позже всех.» Но собака лает — караван идёт, и можно было спокойно продолжать работать с 9 до 5 (ну или с 12 до 8, в моём случае). Не могу сказать, что на той работе я как-то выгорел.
А вот, кстати, когда пришлось с французами поработать, там было как-то на самом деле потогонненько местами, да. Хотя, казалось бы, евросоциализм, все дела.
Ну это ж не поэзия и не философия, тут есть объективно измеримые критерии успеха.
Например, есть набор тестовых программ, которые компилятор (эта штука на 5к строк — компилятор для предметно-специфичного языка, да) должен успешно компилировать. Если реализация A компилирует, скажем, 90% набора в правильно исполняемый код, а реализация B — 40%, и на пересечении этих множеств исполняемый код от реализации A работает настолько-то быстрее, то вполне можно сказать, что результат не просто сопоставим, а кратно лучше.
Я тоже не люблю го. Но да, задачи гугла го решает отлично: это хороший инструмент по критерию «можно взять вчерашнего выпускника и посадить писать микросервисы». Плюсы — хороший инструмент по критерию «широкая доступность работы и интеграция с легаси-системами». JS — хороший инструмент по критерию «работает в браузере». Хаскель — хороший инструмент по критерию «о коде легко рассуждать, глядя только на типы, код сразу работает после рефакторинга, поддерживается type-driven development, и так далее». Он не обязан при этом быть хорошим для использования вчерашними выпускниками, или работы в браузере, или интеграции с легаси-системами.
У меня всё больше впечатление, что вы не знаете, о чём говорите. Почему с компилятором-то плохо, например? ghc — это лучшее, что у меня было.
Выбор гуя — да, здесь согласен. Гуй на хаскеле я бы делать не стал. Правда, я бы его ни на чём бы делать не стал, но это другой вопрос.
И вы говорите «гуй, авалония и хот релоад», а я говорю «репл, беготня по деревьям, и готовые алгоритмы унификации или монадический байесовский инференс».
Нет. Я хочу проработать несколько лет, занимаясь неудовлетворяющей меня деятельностью, так как за неё достаточно платят, чтобы потом несколько лет не работать.
Проблема в том, что компании нужна предсказуемость, ответственность, и далеко не всегда там выходит заниматься только лично интересным тебе.
Не существует компаний, которые могли бы конвертировать, скажем, моё желание читать книжки, изучать теоркат и писать всякий код по фану (до уровня proof of concept, не доводя до продакшена), в деньги, которые они бы могли бы потом мне платить.
Это пример, показывающий, почему «крупные проекты» по вашим определениям вам не видно.
Мне скоро спать пора, а как я уснуть смогу, не зная, что там за объяснение такое?
Если инструмент устроен так, что им легко отрезать себе пальцы, то это плохой инструмент. Если в инструмент добавлены защиты, чтобы обе руки не попадали в рабочую зону при работе — это хороший инструмент.
Хорошее, конечно. Мне потом неплохо платят, чтобы с результатами такой стратегии разбираться.
Потому что ведь нельзя, чтобы просто в типах всё было видно, да? Надо, чтобы мышкой надо было поелозить в IDE, и чтобы комментарии были устаревшие. Ведь основная сложность для нас — это поиск работы, соответственно борьба со сложностью — это обеспечение job security.
Кажется, вы начинаете до чего-то догадываться.
А я смотрю на свой проект, где 5к делало больше, чем несколько десятков к соседней команды. И? Какой вывод?
Если под «традиционным ООП» понимать «нафигачили паттернов по книжкам, в итоге вообще ничего не понятно», то да, с таким я встречался.
Если чуть серьёзнее, то разгадка одна: на мейнстримных императивных языках можно быстрее выдать что-то, как-то более-менее работающее. Разработка софта — это жадный алгоритм, поэтому имеем то, что имеем.
А где и зачем здесь наследование типов?
Да и как явное преобразование типов — результат эволюции идей ООП, непонятно. Это вообще частный случай функции из одного типа в другой.
«Не принято воспринимать X как Y» — это не характеристика личных качеств.
Где я написал про книги, по которым вы учились?
Почему? Практика показывает, что это достаточно точная оценка.
Сразу после списка.
Не работает, и я написал, почему, в половине пунктов списка.
И не работает с точки зрения поддерживаемости крупных проектов в долгосрочной перспективе, и об этом вторая половина списка.
А если бы там хэшмапы ещё участвовали, то вы бы сказали, что пример подогнан под хэшмапы? Или хэшмапы типа можно, потому что они в C# есть из коробки?
Ну возьмите готовую библиотеку с реализацией деревьев, я разве против?
По причинам несоответствия этого утверждения объективной реальности, или потому, что звучит неприятно?
Если второе, то я-то думал, что мы тут с вами открыто и смело и прямо в лицо,
359!Демонстрационный пример про выразительность языка.
Говоришь про примеры из практики (которые в комменты не влезают) — не подходит. Приводишь примеры, которые в комменты влезают — не подходит. Ну вы скажите, что это всё для вас вопрос веры, и я отстану тогда, ё-моё.
Конечно, лучше писать «типичной ФП-сплющенной чёрной магией, где за смысл отвечают знаки препинания, а не слова».
Меня это не оскорбляет, я вообще так-то матершинник, вырос на двач-adjacent-культуре, и кожа у меня толстая. Единственное, что оскорбляет и задевает лично меня — это двойные стандарты, поэтому я оставляю за собой право отвечать в том же стиле, в котором говорит мой собеседник. Есть шанс, конечно, что я интерпретирую стиль неправильно, но тогда уж прошу простить. Искренне.
Любое предметное обсуждение — задетое самолюбие и потребность в доказательстве чего-то там? Лол.
Есть удачные базисы, по которым можно раскладывать сложность, и неудачные. Если обсуждение и сравнение этих базисов для вас выглядит как вот описанная сцена, то это, конечно, печально, я бы так относиться к этому не стал.
Нет, это скучно :(
Причём тут кошечки и собачки, когда это просто система типов, а ООП до потери смысла нетипизирован [по замыслу создателей smalltalk]?
Сначала по коду — это, конечно, лол. Код превзошёл мои ожидания, но не в ту сторону, в которую вы думаете.
У вас почему-то связана концепция «рекурсивная структура данных такой-то формы» и «что может лежать в каждом узле этой структуры»:
Node(в ваших ООПных терминах) сильно связано и с хранением детей, и с хранением конкретных данных в узле.Сравните с определением
Tree/Node, которые я давал выше.Концепция «обойти дерево» тоже связана с содержимым этого дерева.
Но я всё эти связности могу понять: книги по ООП учат разделять кошечек, собачек, квадратики и прямоугольнички, а не действия. В ООП вообще не принято думать в терминах действий, потому что действия не являются first-class. Чтобы выразить действие, апологет ООП (ООПологет?) навернёт десяток паттернов из фабрик визиторов стратегий.
У вас никак не выражаются инварианты на уровне типов (да и типов нет, stringly-typed комментарий и ошибка не считаются). Комментарий может быть, а может и не быть. Ошибка может быть, а может и не быть. Они могут не быть оба сразу. Они могут быть оба сразу. По чтению определения данных ничего непонятно, надо читать алгоритм, чтобы понять, что происходит. И в вызывающем коде надо проверять явно их наличие (забыл проверить ошибку — твоя проблема).
Сравните с
Either NetworkError Comment— там всё сразу видно, и обратиться к комментарию, если его нет, невозможно.Собсна, дерево до загрузки комментов и дерево после загрузки комментов на уровне типов тоже никак не отличаются.
Сравните с
Tree IntиTree Comment(илиTree (Int, Comment), если вашей душе так угодно). Type-driven development? Не, не слышали.LoadCommentsпочему-то является нестатическим членом класса, хотя к полям экземпляра класса никак не обращается. Как читающему только тип функции понять, как связана таNode, котораяthis, и таNode, которая передана какnode?Func<int, string>, конечно, понятнее и синтаксически чище, чемInt -> String(который у меня с лигатурами выглядит какInt → String) ЧитатьFunc<int, List<int>, string>тем более легче, чемInt → [Int] → String— заодно глаза тренируются скобочки считать и различать, в старости пригодится как защита от Альцгеймера.Кстати, как у вас выражается rank-n-полиморфизм? Как будет выглядеть тип функции, который на хаскеле выражается как
forall a. Show a => a -> a -> String, скажем? Ну, то есть, функция, которая принимает два аргумента (одного и того же) любого типа, реализующего интерфейсIShow(и возвращает строку). Что нужно будет написать вместо...вFunc<...>?Ой, я по ассоциациям ещё про multiparam typeclasses вспомнил, но не стоит вскрывать эту тему, это уже будет просто избиение младенцев.
Я не могу посмотреть на результат
LoadCommentsи понять, произошла хотя бы одна ошибка в дереве или нет (это вообще было одним из условий, про которые я регулярно говорил). Мне после вызоваLoadCommentsнадо будет обойти дерево ещё раз. И чтобы собрать ошибки в список, его тоже надо будет обойти.try/catch ловит все ошибки и теряет информацию о типе, тупо записывая
Message. Я просил весь тип ошибки целиком.Олсо, это совершенно неподдерживаемый код: если раньше
loadCommentпо каким-то причинам не кидала ошибок (например, потому что разработчик как настоящий фанат OOP, TDD, SOLID, CBT и SPH сначала всё мокал, а моки ничего не кидали, скажем), и поэтому вы не написали try/catch, то после того, какloadCommentтаки начнёт кидать ошибки, вы, конечно, свой код не обновите, потому что компилятор вам про это не скажет.Зачем так жить в 2025-м году?
Что-то ещё было, но пока я мучался с интерфейсом firefox'а, чтобы отделить вкладку и перенести её на второй вертикальный экран, потому что на моём горизонтальном экране список вытеснил ваш комментарий с кодом, я это что-то забыл, но, думаю, и так хватит. И так уже число комментариев совпало с числом непустых и не-чисто-синтаксических строк вашего кода.
Сравните с моим вариантом, который я себе позволю одной строкой, раз цепочки функций вам норм:
Всё. Дерево (
Tree, это тип из стандартной либы) обходится (вtraverse, это стандартная функция). Ошибки собираются (вsequenceA, это стандартная функция). Разделение на ошибки, комментарии и структуру есть.Validation(единственная не оч стандартная, но достаточно известная штука, которую я подключил: вопрос дописыванияvalidationвpackage.yaml) гарантирует семантику «либо ошибки, либо дерево комментов».NonEmpty(тоже тип из стандартной либы) гарантирует, что есть хотя бы одна ошибка.Господи, да этот код даже тестировать не надо, он просто работает. Там нечему ломаться и негде ошибиться.
Если не наглеть и сделать, как я бы сделал для прода, то я вынес бы обёртку для
loadCommentотдельно, и всё вообще читабельно без лишних скобочек:Это алгоритмически неразрешимая задача. И я обычно пулл-реквесты, например, ревьювлю в интерфейсе гитхаба, и диффы смотрю там же (или в консоли c git show / git diff). Там IDE как-то не оч работает.
Комменты-то у нас, конечно, не устаревают и проверяются компилятором при каждой сборке.
Ещё в комментариях можно типы переменных писать (как в старом JS или во Flow, только без запуска тайпчекера Flow). Можно даже имена там писать, как в ассемблере!
Да, checked exceptions — самая близкая из упомянутых вами вещей к
Either. Правда, костыльная, без полиморфизма по экзепшонам, и так далее, поэтому на практике малоюзабельная.А тут вам в условии дали фору: сигнатуру можете выбрать как вам удобнее.
Это чтобы потом вместо того, чтоб просто посмотреть на тип функции, ходить спрашивать «а что функция кидает?», как вы уже сделали выше?
Но, впрочем, я и про это написал! Во:
Вы реально по кругу ходите. Мы с вами это уже обсудили.
Вы точно не понимаете, что такое модельная задача.
Решите уже эту, наконец, и обсудим границы применимости, усложнения и прочее.
ФП что-то быстро сдулось. А аналог уже даже в плюсы завезли в виде
std::expected.Ну хаскель-то известен своей динамической типизацией, конечно.
В данном случае это значит «вернуть такое же по форме дерево, но в котором на данной позиции вместо инта лежит соответствующий этому инту комментарий». Сорян за функциональный жаргон.
Мы разные вещи понимаем под работой.
Юзабельный опенсорс (ядро линукса, llvm, и так далее) пишут люди за зарплату. Скажем 80% контрибьюторов в ядро — корпы, это тупо по емейлам видно.
А хобби-опенсорс уровня «сделал пердюльку для себя и выложил в гитхаб» — это не работа.
Чтобы перевести её на шарп так, как вы считаете наиболее идиоматичным. Я на шарпе не писатель, только читатель, и так как вы говорили, что используете ФП в шарпе (следовательно, знакомы с концепциями) и не переносите синтаксис хаскеля (следовательно, знакомы с ним), мне представляется разумным предположить, что вы её прочитать и перевести сможете.
Впрочем, возможную сигнатуру шарпа я тоже приводил выше.
Пока ничего нового, всё как на хаскеле.
У загружатора одного комментария? Либо
NetworkError, либоComment. Как выражается «либо» в шарпе идиоматически, я не знаю. В хаскеле этоEither.Вы по кругу просто ходите, что ли? Сигнатуру я уже тоже выше писал:
Не понял. Кто запретил?
Вот я вам заменил число на строку.
Что именно неправильно-то?
А, чисто для протокола — на исключения можете забить и считать, что их нет. Но если вам удобнее с исключениями, то можете и с исключениями, конечно, но тогда вам придётся отобрать те, которые соответствуют только
NetworkError, и собрать их все.В типах же описано! Принимает
Int, выдаёт либо объект типаNetworkError(дан свыше), либо объект типаComment(тоже).Вам нужно получить
Tree Int(дерево с интами в узлах), для каждого инта загрузить комментарий черезloadComment, и выдать либо список возникших ошибок (если возникла хотя бы одна), либо дерево, где каждыйIntзаменён на соответствующий комментарий.Так а проект на 50к строк можно назвать средним?
Функция
loadComment. Пришла из васиной библиотеки, которую он написал для работы со своим сервисом.Вы не понимаете, что такое модельная задача. Ну ок.
Нет, не сделали, потому что нет законченного кода, используемого данную вам
loadCommentиз васиной библиотеки и любой удобный вам тип данных для представления деревьев.В функции
loadComment, как это было ясно из её типа.Оххх бро…
Сколько раз я занимался байтоёбством на хаскеле и получал код, работающий на уровне плюсов (а иногда чутка быстрее) — не перечесть. В худшем случае объём вычислительного кода сравним с объёмом кода на плюсах.
Он делал больше, чем реализация соседней группы, делавшей ту же задачу на плюсах, которую до меня припрягли делать то же, которая делала это втроём, которая делала это пару лет (я справился за пару месяцев чистого времени как сайд-проект), и которая это в итоге так и не доделала до конца. Сколько там строк кода было точно, я не знаю, но точно больше нескольких десятков тысяч.