Mandatory не дает fault tolerance, а конфирмов нет
"fault tolerance" чего оно не дает?! Какое такое особое "fault tolerance" дают RMQ'шные basic.ack?!
Они же сами описывают case'ы, когда сообщение будет "доставлено", но при этом ack выгружено не будет. Их там два, емнип... точно есть "нюансы" с persistent. И, насколько я помню, есть "другие нюансы" в случае, когда сообщение должно бы попадать более чем в одну очередь.
Ну и вариант, когда вам прилетает и basic.return, и этот basic.ack - для одного и того же сообщения - в любом случае, надо уметь "держать в уме", имхо :-)
Вообще, интересно... я точно помню, что раньше, вместе со спеками публиковались отдельные доп. документы: RoI по клиенту/серверу и т.п. Но вот полез на OASIS... и спеки без "тулчейнов", и "допников" не обнаружил :-( Теперь даже в RoI "непотыкаешь" :-(
Если у нас нет подтверждения доставки - мы просто не можем ничего гарантировать
Для ясности... мы оба понимаем, что никакие return и ack/nack от "чудес по сети" ну никак не спасают. Так? Если мы говорим о "девятке" то - по спеке - при обнаружении "чудес", нужно asap:
По возможность пульнуть (куда только можно) channel.flow
Кидать соответствующее исключение, и закрывать (переоткрывать) соответствующие каналы, соединения.
Это означает (ко всему прочему), что надо быть готовыми и к тому, что ни ack, ни nack получен не будет. Я это к тому, что если сама реализация клиента не умеет в обработку "такого", то обрабатывать "такое" придется самим. При реализации outbox, или ещё где-то.
"Стандартный" клиент (я для этого RoI и пытался найти) - если я правильно помню - должен на каждый канал держать отдельный "буфер". Он нужен для того, чтобы при "переоткрытии" канала не начинать "жизнь с нуля". Можно - наверное - раскопать клиента apache qpid... они, емнип, были весьма щепетильны в следовании RoI.
А так - если следовать принципу success is silent, and failure is noisy - достаточно же при получении basic.return помечать соответствующее сообщение в outbox как "нужно отправить". Нет?
С одной стороны, какая-то часть утверждений верная... при этом, другая часть - вызывает недоумение :-(
Метод отправки сообщения basic.publish не имеет возвратной части по протоколу AMQP 0-9-1 ...
Это так, в том смысле, что - условного - basic.publish-ok - по протоколу AMQP - действительно нет.
У каждого такого метода есть возвратная часть, которая говорит some-method-ok.
Это, мягко говоря, не так. Basic.publish тут не уникален - полно в протоколе методов без "возвратной части". Даже в классе basic он такой не один. Даже в множестве клиентских команд.
То есть наш basic.publish работает как fire and forget в семантике at most once.
А вот это вот - вообще не так. Механизмы контроля "этого" были ещё в "восьмерке", остались в "девятке"... и только в "десятке" эта часть была кардинально "пересмотрена" в сторону симметричности обмена. Ну так вся "десятка" такая.
Моя основная идея о причинах такого дизайна протокола AMQP в том, что ...
А зачем гадать?! Если вы таки читали спецификации, про "причины такого дизайна" есть прям в самом её начале. Пункт 2.2.3, если уж совсем точно. Можно почитать.
Тогда в протоколе должна быть другая часть, посвященная гарантированной доставке с подтверждениями. И да, она есть – транзакции.
"Другая часть" - в смысле "гарантированной доставки" - действительно есть. И транзакции есть - но они, как вы сами и говорите, про "другое". А вот "подтверждений" - в части basic.publish - действительно нет. By design.
Как это решили ребята из RabbitMQ
"Ребята из RabbitMQ" решили "подзабить" на спеку, в этой части... о чём им говорили, ещё когда они на "восьмерке" сидели.
Смотрим, что нам говорит "спека":
При обнаружении "сетевых проблем" клиент должен "закрывать" канал или соединение целиком;
Брокер, в случае невозможности обслуживать канал, переводит его в inactive через channel.flow;
В basic.publish есть два "флага", которые отвечают за обработку negative case'ов "доставки" на стороне брокера. Можно глянуть спеку, но суть в том, что при наличие этих флагов, брокер должен "вернуть" basic.return для каждого такого сообщения, в случае наступления этих самых negative case'ов.
Там, конечно, в некоторых местах MAY, а не MUST, но та ситуация, что описывается в доке RMQ как "танцы" с их basic.ack/basic.nack вполне может выглядеть как:
Для всех basic.publish с флагами mandatory/immediate - сформировать соответствующих basic.return и направить его в канал соответствующего клиента;
Направить в этот канал cannel.flow c acticve = 0.
без каких-либо противоречий "спеке". Другое дело, что "внутре у неё неонка" - в смысле, есть различные "особенности реализации", скажем так.
И то, что "ребята из RabbitMQ"- емнип, только с выходом "десятки" - часть её "сессионной" механики попытались адаптировать к - это лучше, конечно, чем ничего. До этого-то эта часть "спеки" ими вообще игнорировалась. При том, что сам по себе basic.return был уже реализован, емнип. Но отсылали они его (да и отсылают) только в тех случаях, когда им это "удобно", насколько я помню. Но, имхо, лучше бы уж целиком на "десятку" - в свое время - пересели... гарантий доставки-то, этот их "костылик" всё равно ведь не дает. О чем они сами и говорят. И да - это особенность RMQ, но не AMQP.
С VMными - всё понятно. Они более менее вписываются по "фазам" + их зависимости разрешаются через maven. Но даже тут есть нюансы...
У kotlin с использованием "плюшек" под maven уже есть "проблемы". Тот же ksp - насколько я в курсе - пока в maven нормально "вписать" не удалось. Оф. рекомендация, емнип, запускайте через exec.
Остальное всё - если я правильно понимаю - это всё на уровне "давайте запускать kotlinc через ant-task из maven". Можно, конечно - но в чём смысл-то?! Тут же нет ни управления зависимостями, ни всего остального... это всё вне maven получается. Нет?
Честно говоря - если брать gradle, то там большинство "таких" плагинов тож строятся по этому принципу (в смысле, "давайте запускать ..."). Но в gradle хоть в принципе возможно затащить "всё это" в билд скрипт, и реально "рулить" этим. Как тож самое (пусть хоть гипотетически) проворачивать в maven - лично мне - вообще не понятно :-(
Мы, когда пытались "это" использовать, знатно об это "постукались". Плюс, была ещё странность со сменой кофнигов... которая "то работает, то не работает".
Справедливости ради у нас на проекте (3M+ LOC, 800+ модулей) своя немного кастомная версия.
Ну мы тож пытались это под себя "допилить". В конце концов, "плюнули и забыли" (tm)
К сожалению, про "самое сладенькое" (Maven Artifact Resolver) в статье только пара строчек. А меж тем - если мне не изменяет мой склероз - избавление от Aether и настраиваемый resolver - декларировались одними из главных целей, которые "четверка" должна будет достичь.
Сделано там, действительно, не мало. Но пока, следить за подвижками в этом плане можно только по фотографиисходникам.
Надеюсь "апачи" таки разродятся статьей на этот счёт, и мы таки узнаем много нового и интересного.
Я дико извиняюсь... но, вы сами-то пробовали "этим" пользоваться? Особенно - в CI/CD окружении? Судя по всему - нет. Иначе бы знали о "небольшом баге", который висит чуть ли не со старта на этой "балалайке". Грубо говоря - если вы сидите на "тройке" - что есть этот "кэш" у вас, что его нет.
FYI. Нет нормального кэширования в maven. By desing нет. Даже в "четверке" - это не предусмотрено. А попытки "прикрутить сбоку" - это (хоть так верти, хоть эдак) именно что "костыли со стразами".
Мы - например - и "своё" пытались делать, и эту "балалайку" прикручивали... не работает оно так, как надо. И - в общем случае - не может работать чисто из-за "некоторых особенностей" самого "движка".
:-) "Нечитаемая хрень" - это многоэтажные match'и от которых в принципе не уйти, как только union types начинают "оккупировать наш код".
@Dhwtj уже привел пример... он не слишком удачный, конечно - там вообще нет union... и это всё - более-менее - "съедобно", но "идея" - я думаю - должна быть ясна :-)
Зачем вообще нужен union-type? Очевидно, чтобы выражать "штуки" типа
t<T> -> t<t<T>> -> t<T>
Условно, ну хочется мне чтоб
Option<Option<T>> = Option<T>
и "вот это вот всё", что в принципе не выражается через sum-type.
И тут же вылезает "проблемка"... union-type - by design - некомпозициональный тип. Это - само по себе - значит не мало, но - ко всему прочему - означает так же, что "номинативно" работать с элементами такого типа - неудобно. А со структурной типизацией у jvm возможности, прямо скажем, весьма ограничены. Соответственно, мы гарантированно проваливаемся в match там, где - казалось бы - это и не нужно.
Да, можно сослаться, например, на какой-нибудь reflectiveSelectable... и прочее. Но все эти "штуки" - жрут как не в себя и - соответственно - "на наш путь" :-)
Ну и пошло-поехало... Мыж умные. Мыж не просто так, выбрали union (а не sum, например). У нас причина есть :-) И с огромной долей вероятности, элементами нашего супер-пупер-AA-union-type будут оказываться не менее гига-мега-AA-union-type и т.д. Ну это просто действительно удобно - обобщать через union.
Но вот разбираться с результатом этого обобщения (полученного, например, в результате композиции) в отсутствии структурной типизации - мягко говоря, тяжко.
А чтоб "не было тяжко" - приходится делать над собой усилие и бдеть :-) И других вариантов, к сожалению, просто нет.
При этом, то, что предлагают ребята из Kotlin - лично для меня - не выглядит сколь-нибудь страшно. Просто по той причине, что все элементы, в предлагаемом типе (кроме одного... которого может и не быть) - по определению: имеют эквивалентную семантику (это ошибки), не могут образовывать собственной иерархии (они не open, не могут реализовывать интерфейсы и не могут быть параметризированы) и не имеют пересечений с остальными типами (у них отдельный top type). Т.е., грубо говоря, самая "страшная" часть этого union - гарантированно "плоская". А "как есть таких слонов" (tm) - мы прекрасно знаем.
Да - остается опасность "обобщающего элемента" (в слайдах про "это" ничего нет). Но, я думаю, что там парни "не глупее нас" - самое простое, имхо, тупо запретить включать сам Error в множество.
P.S. На всякий... честно - мне лень тут приводить листинги чтоб "на пальцах" продемонстрировать вам, то, о чём написано выше. Имхо, всё что выше - это и так жуткий оффтопик. Но, судя по всему, scala для вас "родное", а значит - вы и сами должны прекрасно себе представлять все эти "прелести".
Если коротко - каноничный AA-union-type штука сильно уж - скажем так - "обобщающая". Во всех смыслах этого слова.
И очень уж много нужно контроля (на уровне "усилий мозга"), чтобы пользоваться этим "правильно". А чуть "отвлекся" - и всё... "получилась какая-то нечитаемая хрень" :-(
И ещё больше усилий нужно для возврата в "контекст". Читать "это", порой, совсем "грустненько"... хотя и сам "это вчера только написал" :-)
Не понял как "кидается" ошибка. Через throw или через return?
throw никак не меняется. Соответственно, не имеет смысла для подтипов Error. Т.е. "ошибка" возвращается штатно - через return.
Могу ли я создать функцию, которая возвращает и кидает ошибку того же класса одновременно?
Нет. Но если очень хочется, через !! можно кинуть KotlinException, который "обернет" Error.
Если да, то как понять был ли это успешный возврат или ошибка?
Все "ошибки" - это подтипы Error. Error - это новый отдельный top type. Т.е. и он, и его подтипы не приводятся к Any?. Соответственно, на "или ошибка" можно проверять, банально, через is Error.
Пытаться тянуть Result до самого Spring MVC — плохая идея
"Прикол" в том, что тут как раз нет Result'а. Ни в терминах rust'а, ни в терминах kotlin'а.
Result - это "эвфемизм" более общего Either. А Either - by design - скажем так, "хромает" в плане композиции "левой" своей части. Т.е. "левая" часть композиции выводится в что-то осмысленное только при прям очень сильных ограничениях, накладываемых на. Чего - по понятным причинам - делать вообще не хочется.
Result - соответственно - "хромает" на "правую" свою часть.
То, что предлагается ребятами из kotlin - позволяет с одной стороны - избежать такого рода "хромоты". А с другой - не скатится в AA-union-hell.
Насколько - в реальности - окажутся сильными ограничения, накладываемые на подтипы Error - это будем делать посмотреть (с). Дизайн пока не финализирован. Но то, что есть сейчас не выглядит сколь-нибудь "страшным".
Я к тому, что иметь - условный -
private val <T:Any> (T|Error).responseEntity: ResponseEntity<out Any> =
get() -> when { ...}
на уровне контроллера (или даже его пакета) - не выглядит, имхо, чем-то прям "ужос-ужос". Error и upper-bound - понятно, "в реальности" заменяются на какие-то более осмысленные type aliase'ы.
А вот как раз "городьба" отдельной иерархии для передачи "типизации в ошибках" в контроллер - при условии наличия rich errors - будет выглядеть "немножко странно". Нет?
Яб таки дождался финализации. Возможно, оно действительно "негативненько" скажется на interop'е со стороны Java. Возможно (ну а вдруг), придумают как таки обойтись - в этой части - без "костыликов". Но это пока единственное, что хоть сколько-то "пугает".
?! Как раз - наоборот. В кои-то веки, "обшибки" - по честному - часть сигнатуры ф-ции. С нормальной композицией, выводом и т.п.
Если не понятно - User | NetworkError из примера - это полноценный тип. Эдакий AA union type, с единственным ограничением: AA это исключительно для подтипов Error.
Оно - может быть - и не универсально. Зато - сильно утилитарно получается. Как в Haskell - может быть и хочется, но не получится по очевидным причинам. А как в Scala - ну мы видели, к чему "оно" приводит :-)
... обработка будет выглядеть странно
Да ладно?! Даже самый очевидный (но не единственный) вариант обработки - через when выглядит очень органично.
В try catch ты получаешь явно объект ошибки, думаю поэтому его и придумали.
Тут у нас отдельный высший тип для ошибок - Error. Т.е. они ("обшибки") - by design - не пересекаются с подтипами Any? - куда уж явней "объект ошибки" получать?
Единственное преимущество Throwable - это наличие stacktrace. Но и тут - если оно нам таки надо - всегда можно "раскрутиться" через !! оператор.
Я думаю, главной проблемой здесь является то, что выполнение "плана" спринта в стори-поинтах становится метрикой ...
В смысле, "становится"?! Velocity в явном виде декларируется в Scrum как метрика, и отдельно расписано почему она важна, зачем её отслеживать и т.п.
... и в силу закона Гудхарта такая метрика перестает давать какую-либо ценность.
Если в смысле, что высокая Velocity становится целью для команды, и потом (достаточно быстро, на самом деле) приходит понимание того, как проще её набивать/удерживать - то полностью соглашусь с. Но, имхо, менеджмент в этом вопросе немножко вторичен. В том смысле, что если даже он - грубо говоря - играет по правилам, то лучше от этого не становится.
Я поинт автора (в этом плане) воспринял как, не то, что менеджмент "не такой какой-то", а в том, что он вообще есть. Собственно, и мой личный опыт в Scrum показывает что, это действительно работает только когда так и есть.
Scrum со своими стори-поинтами пытается оценить сложность задачи ...
"Сложность" - это слишком упрощенно же. По идее, в estimate входит (должно входить) всё, что влияет на delivery и, при этом, находится в зоне ответственности команды.
В данном случае потеря заметна только когда точечные оценки времени сами по себе уже очень хорошие и хочется высшие моменты распределения, большинство людей и команд не в этой точке.
Вот честно. Вообще не понял к чему это всё было сказано :-(
Вы можете ставить в план задачи, которые кажутся более важными. Затем вы их оцениваете так хорошо как можете.
Мы точно о Scrum? В контексте статьи - план это бэклог спринта. Каким образом у вас в него пролезет "нечто" без estimate?!
Если у вас система поощрений привязана к точности оценки ...
К "точности оценки" привязано другое. Самой методологией, причем. Собственно, поэтому - так или иначе - и "система поощрений" будет привязана (в том числе) и к этой "точности".
Цель - то, про что достигнут консенсус что это цель ...
Бла-бла-бла. Просто вспомните про первоначальный тезис:
I hate estimates, because they don’t actually make the work get done faster or better
А с тем что "оценки" имеют ценность для PO и SM - никто не спорит.
Встречный тезис: всё способно порождать и подпитывать политику, так что "порождает политику" - не работает как критерий выбора между вариантами.
Тезис там в "они [оценки] ... на самом деле не ускоряют выполнение работы или не делают её лучше". Ну и плюс перевод дурацкий.
Правильнее бы сказать что в голове есть распределение, но с не-точечными оценками тяжело осмысленно работать.
Другими словами, мы вынуждены работать не с тем, что действительно важно, а с тем, что можем "осмысленно" оценить. Т.е. - по факту - то, о чём пишет автор - верно. С чем спорим-то?!
Насколько я вижу, главная проблема здесь ...
Он не про это же пишет. Он пишет про то, что на основе этих ("удобных", "осмысленных" и т.п.) оценок, в конечном счёте, производится планирование работы. О том, что - в реальности - если строго следовать этой методологии у вас - буквально - выбор из двух зол. Вы либо не работаете над тем, чем действительно нужно/важно, либо ваши планы - с огромной долей вероятности - накрываются медным тазом. И винить в том, что исполнитель выбирает первое, а не второе нужно не исполнителя, а методологию.
... и позволяют вам лучше оценить, что релиз таки-будет в сентябре, а не к новому году
Ну т.е. цель - "релиз в сентябре"? :-) То, что это хорошо для бизнеса, сомнений не вызывает. А вот для исполнителя - уже далеко не факт. И чем этот исполнитель более опытен - тем более "не факт". Собственно, об этом автор и пишет.
Приведённое мной определение проблемы взято из следующих международных стандартов:
Еще раз, по-русски этот термин (problem), во всех перечисленных вами контекстах, звучит как - та-дам - задача.
Problem solving — determination of a sequence of operations or actions that may lead to a desired goal. ISO/IEC 2382:2015 - Information technology — Vocabulary. (Он же ГОСТ 33707-2016)
Это ни разу не "оно". Вы бы хоть с упомянутым вами же ГОСТ сверились бы.
Термин проблема - в отечественной практике - означает совсем другое. "Так получилось" (с)
Рискну предположить, что вы за и "образец" ИП взяли какое-то описание/определение т.н. engineering problem. Если так, то всё становится на свои места.
Вы же в прямом смысле слова понятий (приведённых в международных стандартах) не имеете.
Вот, кстати, "дико плюсую" за "по уму". Сам-то ETF простой как три копейки... но вот дальше.
Отлично получилось, в целом. Осталось реализовать ETF (ну и ещё "койчего") и можно цепляться как external node к.
"fault tolerance" чего оно не дает?! Какое такое особое "fault tolerance" дают RMQ'шные basic.ack?!
Они же сами описывают case'ы, когда сообщение будет "доставлено", но при этом ack выгружено не будет. Их там два, емнип... точно есть "нюансы" с persistent. И, насколько я помню, есть "другие нюансы" в случае, когда сообщение должно бы попадать более чем в одну очередь.
Ну и вариант, когда вам прилетает и basic.return, и этот basic.ack - для одного и того же сообщения - в любом случае, надо уметь "держать в уме", имхо :-)
Вообще, интересно... я точно помню, что раньше, вместе со спеками публиковались отдельные доп. документы: RoI по клиенту/серверу и т.п. Но вот полез на OASIS... и спеки без "тулчейнов", и "допников" не обнаружил :-( Теперь даже в RoI "непотыкаешь" :-(
Для ясности... мы оба понимаем, что никакие return и ack/nack от "чудес по сети" ну никак не спасают. Так? Если мы говорим о "девятке" то - по спеке - при обнаружении "чудес", нужно asap:
По возможность пульнуть (куда только можно) channel.flow
Кидать соответствующее исключение, и закрывать (переоткрывать) соответствующие каналы, соединения.
Это означает (ко всему прочему), что надо быть готовыми и к тому, что ни ack, ни nack получен не будет. Я это к тому, что если сама реализация клиента не умеет в обработку "такого", то обрабатывать "такое" придется самим. При реализации outbox, или ещё где-то.
"Стандартный" клиент (я для этого RoI и пытался найти) - если я правильно помню - должен на каждый канал держать отдельный "буфер". Он нужен для того, чтобы при "переоткрытии" канала не начинать "жизнь с нуля". Можно - наверное - раскопать клиента apache qpid... они, емнип, были весьма щепетильны в следовании RoI.
А так - если следовать принципу success is silent, and failure is noisy - достаточно же при получении basic.return помечать соответствующее сообщение в outbox как "нужно отправить". Нет?
Очень странное впечатление от статьи.
С одной стороны, какая-то часть утверждений верная... при этом, другая часть - вызывает недоумение :-(
Это так, в том смысле, что - условного - basic.publish-ok - по протоколу AMQP - действительно нет.
Это, мягко говоря, не так. Basic.publish тут не уникален - полно в протоколе методов без "возвратной части". Даже в классе basic он такой не один. Даже в множестве клиентских команд.
А вот это вот - вообще не так. Механизмы контроля "этого" были ещё в "восьмерке", остались в "девятке"... и только в "десятке" эта часть была кардинально "пересмотрена" в сторону симметричности обмена. Ну так вся "десятка" такая.
А зачем гадать?! Если вы таки читали спецификации, про "причины такого дизайна" есть прям в самом её начале. Пункт 2.2.3, если уж совсем точно. Можно почитать.
"Другая часть" - в смысле "гарантированной доставки" - действительно есть. И транзакции есть - но они, как вы сами и говорите, про "другое". А вот "подтверждений" - в части basic.publish - действительно нет. By design.
"Ребята из RabbitMQ" решили "подзабить" на спеку, в этой части... о чём им говорили, ещё когда они на "восьмерке" сидели.
Смотрим, что нам говорит "спека":
При обнаружении "сетевых проблем" клиент должен "закрывать" канал или соединение целиком;
Брокер, в случае невозможности обслуживать канал, переводит его в inactive через channel.flow;
В basic.publish есть два "флага", которые отвечают за обработку negative case'ов "доставки" на стороне брокера. Можно глянуть спеку, но суть в том, что при наличие этих флагов, брокер должен "вернуть" basic.return для каждого такого сообщения, в случае наступления этих самых negative case'ов.
Там, конечно, в некоторых местах MAY, а не MUST, но та ситуация, что описывается в доке RMQ как "танцы" с их basic.ack/basic.nack вполне может выглядеть как:
Для всех basic.publish с флагами mandatory/immediate - сформировать соответствующих basic.return и направить его в канал соответствующего клиента;
Направить в этот канал cannel.flow c acticve = 0.
без каких-либо противоречий "спеке". Другое дело, что "внутре у неё неонка" - в смысле, есть различные "особенности реализации", скажем так.
И то, что "ребята из RabbitMQ"- емнип, только с выходом "десятки" - часть её "сессионной" механики попытались адаптировать к - это лучше, конечно, чем ничего. До этого-то эта часть "спеки" ими вообще игнорировалась. При том, что сам по себе basic.return был уже реализован, емнип. Но отсылали они его (да и отсылают) только в тех случаях, когда им это "удобно", насколько я помню. Но, имхо, лучше бы уж целиком на "десятку" - в свое время - пересели... гарантий доставки-то, этот их "костылик" всё равно ведь не дает. О чем они сами и говорят. И да - это особенность RMQ, но не AMQP.
С VMными - всё понятно. Они более менее вписываются по "фазам" + их зависимости разрешаются через maven. Но даже тут есть нюансы...
У kotlin с использованием "плюшек" под maven уже есть "проблемы". Тот же ksp - насколько я в курсе - пока в maven нормально "вписать" не удалось. Оф. рекомендация, емнип, запускайте через exec.
Остальное всё - если я правильно понимаю - это всё на уровне "давайте запускать kotlinc через ant-task из maven". Можно, конечно - но в чём смысл-то?! Тут же нет ни управления зависимостями, ни всего остального... это всё вне maven получается. Нет?
Честно говоря - если брать gradle, то там большинство "таких" плагинов тож строятся по этому принципу (в смысле, "давайте запускать ..."). Но в gradle хоть в принципе возможно затащить "всё это" в билд скрипт, и реально "рулить" этим. Как тож самое (пусть хоть гипотетически) проворачивать в maven - лично мне - вообще не понятно :-(
https://github.com/apache/maven-build-cache-extension/issues/270 - это тот самый "небольшой баг".
Мы, когда пытались "это" использовать, знатно об это "постукались". Плюс, была ещё странность со сменой кофнигов... которая "то работает, то не работает".
Ну мы тож пытались это под себя "допилить". В конце концов, "плюнули и забыли" (tm)
К сожалению, про "самое сладенькое" (Maven Artifact Resolver) в статье только пара строчек. А меж тем - если мне не изменяет мой склероз - избавление от Aether и настраиваемый resolver - декларировались одними из главных целей, которые "четверка" должна будет достичь.
Сделано там, действительно, не мало. Но пока, следить за подвижками в этом плане можно только по
фотографиисходникам.Надеюсь "апачи" таки разродятся статьей на этот счёт, и мы таки узнаем много нового и интересного.
Не только для сборки? Или не только для Java проектов?
Если таки второе, то интересно - как?! Через antrun какой-нибудь? Так-то оно гвоздиками всё прибито к.
Я дико извиняюсь... но, вы сами-то пробовали "этим" пользоваться? Особенно - в CI/CD окружении? Судя по всему - нет. Иначе бы знали о "небольшом баге", который висит чуть ли не со старта на этой "балалайке". Грубо говоря - если вы сидите на "тройке" - что есть этот "кэш" у вас, что его нет.
FYI. Нет нормального кэширования в maven. By desing нет. Даже в "четверке" - это не предусмотрено. А попытки "прикрутить сбоку" - это (хоть так верти, хоть эдак) именно что "костыли со стразами".
Мы - например - и "своё" пытались делать, и эту "балалайку" прикручивали... не работает оно так, как надо. И - в общем случае - не может работать чисто из-за "некоторых особенностей" самого "движка".
:-) "Нечитаемая хрень" - это многоэтажные match'и от которых в принципе не уйти, как только union types начинают "оккупировать наш код".
@Dhwtj уже привел пример... он не слишком удачный, конечно - там вообще нет union... и это всё - более-менее - "съедобно", но "идея" - я думаю - должна быть ясна :-)
Зачем вообще нужен union-type? Очевидно, чтобы выражать "штуки" типа
Условно, ну хочется мне чтоб
и "вот это вот всё", что в принципе не выражается через sum-type.
И тут же вылезает "проблемка"... union-type - by design - некомпозициональный тип. Это - само по себе - значит не мало, но - ко всему прочему - означает так же, что "номинативно" работать с элементами такого типа - неудобно. А со структурной типизацией у jvm возможности, прямо скажем, весьма ограничены. Соответственно, мы гарантированно проваливаемся в match там, где - казалось бы - это и не нужно.
Да, можно сослаться, например, на какой-нибудь reflectiveSelectable... и прочее. Но все эти "штуки" - жрут как не в себя и - соответственно - "на наш путь" :-)
Ну и пошло-поехало... Мыж умные. Мыж не просто так, выбрали union (а не sum, например). У нас причина есть :-) И с огромной долей вероятности, элементами нашего супер-пупер-AA-union-type будут оказываться не менее гига-мега-AA-union-type и т.д. Ну это просто действительно удобно - обобщать через union.
Но вот разбираться с результатом этого обобщения (полученного, например, в результате композиции) в отсутствии структурной типизации - мягко говоря, тяжко.
А чтоб "не было тяжко" - приходится делать над собой усилие и бдеть :-) И других вариантов, к сожалению, просто нет.
При этом, то, что предлагают ребята из Kotlin - лично для меня - не выглядит сколь-нибудь страшно. Просто по той причине, что все элементы, в предлагаемом типе (кроме одного... которого может и не быть) - по определению: имеют эквивалентную семантику (это ошибки), не могут образовывать собственной иерархии (они не open, не могут реализовывать интерфейсы и не могут быть параметризированы) и не имеют пересечений с остальными типами (у них отдельный top type). Т.е., грубо говоря, самая "страшная" часть этого union - гарантированно "плоская". А "как есть таких слонов" (tm) - мы прекрасно знаем.
Да - остается опасность "обобщающего элемента" (в слайдах про "это" ничего нет). Но, я думаю, что там парни "не глупее нас" - самое простое, имхо, тупо запретить включать сам Error в множество.
P.S. На всякий... честно - мне лень тут приводить листинги чтоб "на пальцах" продемонстрировать вам, то, о чём написано выше. Имхо, всё что выше - это и так жуткий оффтопик. Но, судя по всему, scala для вас "родное", а значит - вы и сами должны прекрасно себе представлять все эти "прелести".
Если коротко - каноничный AA-union-type штука сильно уж - скажем так - "обобщающая". Во всех смыслах этого слова.
И очень уж много нужно контроля (на уровне "усилий мозга"), чтобы пользоваться этим "правильно". А чуть "отвлекся" - и всё... "получилась какая-то нечитаемая хрень" :-(
И ещё больше усилий нужно для возврата в "контекст". Читать "это", порой, совсем "грустненько"... хотя и сам "это вчера только написал" :-)
слайды по докладу
throw никак не меняется. Соответственно, не имеет смысла для подтипов Error. Т.е. "ошибка" возвращается штатно - через return.
Нет. Но если очень хочется, через !! можно кинуть KotlinException, который "обернет" Error.
Все "ошибки" - это подтипы Error. Error - это новый отдельный top type. Т.е. и он, и его подтипы не приводятся к Any?. Соответственно, на "или ошибка" можно проверять, банально, через is Error.
"Прикол" в том, что тут как раз нет Result'а. Ни в терминах rust'а, ни в терминах kotlin'а.
Result - это "эвфемизм" более общего Either. А Either - by design - скажем так, "хромает" в плане композиции "левой" своей части. Т.е. "левая" часть композиции выводится в что-то осмысленное только при прям очень сильных ограничениях, накладываемых на. Чего - по понятным причинам - делать вообще не хочется.
Result - соответственно - "хромает" на "правую" свою часть.
То, что предлагается ребятами из kotlin - позволяет с одной стороны - избежать такого рода "хромоты". А с другой - не скатится в AA-union-hell.
Насколько - в реальности - окажутся сильными ограничения, накладываемые на подтипы Error - это будем делать посмотреть (с). Дизайн пока не финализирован. Но то, что есть сейчас не выглядит сколь-нибудь "страшным".
Я к тому, что иметь - условный -
на уровне контроллера (или даже его пакета) - не выглядит, имхо, чем-то прям "ужос-ужос". Error и upper-bound - понятно, "в реальности" заменяются на какие-то более осмысленные type aliase'ы.
А вот как раз "городьба" отдельной иерархии для передачи "типизации в ошибках" в контроллер - при условии наличия rich errors - будет выглядеть "немножко странно". Нет?
Сугубое имхо...
Выглядит оно уже сейчас сильно "вкусно". Ребята из arrow - уже облизываются :-) Наконец-то нормальная "человечья" error composition "из коробки".
Озвученные "сомнения" - в разрезе Kotlin - выглядят, мягко говоря, неубедительно.
Яб таки дождался финализации. Возможно, оно действительно "негативненько" скажется на interop'е со стороны Java. Возможно (ну а вдруг), придумают как таки обойтись - в этой части - без "костыликов". Но это пока единственное, что хоть сколько-то "пугает".
?! Как раз - наоборот. В кои-то веки, "обшибки" - по честному - часть сигнатуры ф-ции. С нормальной композицией, выводом и т.п.
Если не понятно - User | NetworkError из примера - это полноценный тип. Эдакий AA union type, с единственным ограничением: AA это исключительно для подтипов Error.
Оно - может быть - и не универсально. Зато - сильно утилитарно получается. Как в Haskell - может быть и хочется, но не получится по очевидным причинам. А как в Scala - ну мы видели, к чему "оно" приводит :-)
Да ладно?! Даже самый очевидный (но не единственный) вариант обработки - через when выглядит очень органично.
Тут у нас отдельный высший тип для ошибок - Error. Т.е. они ("обшибки") - by design - не пересекаются с подтипами Any? - куда уж явней "объект ошибки" получать?
Единственное преимущество Throwable - это наличие stacktrace. Но и тут - если оно нам таки надо - всегда можно "раскрутиться" через !! оператор.
В каком месте?!
Это просто "корявости" перевода. В оригинале речь не про "отчитываться" и не про "например" :-)
В смысле, "становится"?! Velocity в явном виде декларируется в Scrum как метрика, и отдельно расписано почему она важна, зачем её отслеживать и т.п.
Если в смысле, что высокая Velocity становится целью для команды, и потом (достаточно быстро, на самом деле) приходит понимание того, как проще её набивать/удерживать - то полностью соглашусь с. Но, имхо, менеджмент в этом вопросе немножко вторичен. В том смысле, что если даже он - грубо говоря - играет по правилам, то лучше от этого не становится.
Я поинт автора (в этом плане) воспринял как, не то, что менеджмент "не такой какой-то", а в том, что он вообще есть. Собственно, и мой личный опыт в Scrum показывает что, это действительно работает только когда так и есть.
"Сложность" - это слишком упрощенно же. По идее, в estimate входит (должно входить) всё, что влияет на delivery и, при этом, находится в зоне ответственности команды.
Вот честно. Вообще не понял к чему это всё было сказано :-(
Мы точно о Scrum? В контексте статьи - план это бэклог спринта. Каким образом у вас в него пролезет "нечто" без estimate?!
К "точности оценки" привязано другое. Самой методологией, причем. Собственно, поэтому - так или иначе - и "система поощрений" будет привязана (в том числе) и к этой "точности".
Бла-бла-бла. Просто вспомните про первоначальный тезис:
А с тем что "оценки" имеют ценность для PO и SM - никто не спорит.
Тезис там в "они [оценки] ... на самом деле не ускоряют выполнение работы или не делают её лучше". Ну и плюс перевод дурацкий.
Другими словами, мы вынуждены работать не с тем, что действительно важно, а с тем, что можем "осмысленно" оценить. Т.е. - по факту - то, о чём пишет автор - верно. С чем спорим-то?!
Он не про это же пишет. Он пишет про то, что на основе этих ("удобных", "осмысленных" и т.п.) оценок, в конечном счёте, производится планирование работы. О том, что - в реальности - если строго следовать этой методологии у вас - буквально - выбор из двух зол. Вы либо не работаете над тем, чем действительно нужно/важно, либо ваши планы - с огромной долей вероятности - накрываются медным тазом. И винить в том, что исполнитель выбирает первое, а не второе нужно не исполнителя, а методологию.
Ну т.е. цель - "релиз в сентябре"? :-) То, что это хорошо для бизнеса, сомнений не вызывает. А вот для исполнителя - уже далеко не факт. И чем этот исполнитель более опытен - тем более "не факт". Собственно, об этом автор и пишет.
Дожили...
Еще раз, по-русски этот термин (problem), во всех перечисленных вами контекстах, звучит как - та-дам - задача.
Это ни разу не "оно". Вы бы хоть с упомянутым вами же ГОСТ сверились бы.
Термин проблема - в отечественной практике - означает совсем другое. "Так получилось" (с)
Рискну предположить, что вы за и "образец" ИП взяли какое-то описание/определение т.н. engineering problem. Если так, то всё становится на свои места.
Ну-ну :-)