Привет. Есть исключительные ситуации, а есть ожидаемые. Под исключинтельными ситуациями я понимаю: закончилась память, закончился диск, разорвалось соединение и т.п. И это реализуют с помощью исключений. А есть вполне ожидаемые прерывания основного потока выполнения. Пользователь ввел невалидные данные. Пользователь не имеет прав на операцию. Обычно советуют исползовать either, maybe и т.п. для этого случая. На первый взгляд отличие исключения от either в том, что исключение дороже из-за стек трейса. В для either мы должны особым образом структурировать код. А результат один - всплывание ошибок. Только в одном случае это делает jvm, а в другом мы сами. Так может не мучаться и использовать исключения без стектрейсов? Что думаете? Есть какие-то практические примеры?
flow сделан для того, чтобы использовать exception вместо either/left
сам either чужероден для кложуры как по мне
слишком много упаковки и распаковки
в идеологии flow есть нормальные значения, а есть exception instances которые сингализируют об ошибке
насчет дороговизны трейсов, есть возможность использовать кастомный контейнер вместо ExceptionInfo, я сейчас думаю над этим. Хотел добавить поддержку "no stacktrace" в ex-info в core, но зареджектили, что вобщем-то ожидаемо
@kuzmin_m ^^
Если я правильно понял, то в flow используются идеи continuation-passing style (CPS). А исключения можно реализовать через континуации. И это равномощные вещи. Только исключения в jvm из коробки есть. Я пока не пробовал использовать исключения для ожидаемых ответвлений оснвного потока вычислений. Но пока единственная “проблема” - медленные исключения. Ну один случай из 10 будет чуть медленнее, если пользователь ошибется и введет что-то не то. И видимо из-за этого Миллер в в тикете отписался, что не видит проблем. Плюс, если кто-то добавил новый тип исключения, и забыли добавить его обработку, то тут стек-трейс полезен. Я где-то с полгода назад бенчмарки делал, и уже не помню, но исключения вроде раз в 100 или в 1000 были медленнее. Но это миллесекунды и наносекунды. Но зато нет накладных расходов в основном потоке исполнения. Я сравнивал исключения, better-cond, вложенные if и свою первую реализацию either. @fmnoise
не уверен насчет cps, там все навороченнее ментально чем во flow
ну там тоже коллбек передается
flow просто определяет инстанс исключения как fail, и дает 2 хелпера базирующихся на этом утверждении
так это не колбеки
просто классический threading macro
мы же об этом говорим?
и try/catch под капотом
да
посмотри код, его там очень мало
(defn check-login [req next]
(if (:user req)
next
{:error "Login required" :code 401}))
а next - не коллбэк?)
а
так это пример кода, от которого мы уходим)
он не вызывается
при помощи flow
первый пример лесенка if if if if
которая потом рефакторится в колбеки
я значит не так понял
и типа ой все равно плохо
давайте сделаем флоу
и на флоу все сразу хорошо)
я правильно понял, что это похоже на промисы в js?
не, это больше на монаду похоже
either
так и проимсы - монада
и fmap
ну можно сказать и промис
просто в промисе еще асинхронность и тп
а тут просто try/catch и if
там же тоже цепочка Promise(…).then().then().catch()
я не уверен можно ли в промисе вернуться на happy path
во flow можно
хм
в else можно вернуть значение
и дальше опять будет then например
а как по функциям разбить?
(->> (call load-settings)
(else (constantly default-settings))
(then...))
а там все аргументы функции
а зачем ex-info? как правило исключение не выбрасывается же, а просто передается
да, верно
ex-info умеет мапу хранить
и при создании еще трейс вычисляет
с произвольными данными
так свой тип можно сделать
и никто не мешает тебе сделать throw
(else #(throw %))
трасса да, затратная
в новой версии добавлю возможность скипать трассу
там посмотри открытое issue
там бенчмарк
без трассы сильно быстрее
Для понимания, я сам either делал https://github.com/darkleaf/either Но тогда не до конца разобрался зачем все это нужно. И сейчас кажется - что обычные исключения это подходящее решение. И как-то не убедительно для меня использование either-like подходов. Может быть я о каких-то случаях не знаю. Попробую свой either выпилить, может быть и найду такой случай.
свой тип да, сделал уже, щас думаю над апи чтобы выбирать надо тебе трасса или нет
на either красиво пайплайны пишутся
но он как яд
если они нужны
нужны
а можешь пример привести?
пайплайна
(defn remove-calendar-parent!
[{:keys [user db conn admin?] :as ctx} {:keys [spaceId]} _]
(->> (ensure-login user)
(then (fn [_] (find-space db spaceId)))
(then #(check-edit-ability % user admin?))
(then #(perform-remove-calendar-parent conn %))
(else respond-with-error)))
из боевого кода
ну это маленький
совсем
а если у тебя зависимости появляются?
все в одну мапу писать?
можно flet
или в 1 мапу
ага, flet нашел
мы часто в пайплайне датомик транзакцию собираем
а потом делаем transact
а чем это лучше try/catch?
или log/error
красивее
читаемее
особенно когда начинаются вложенные try/catch
так, а зачем тут вложенные try/catch?
flow спасет от вложенных т.к. умеет вернуть в нормальную ветку
на самом верху объявил try/catch и лови все что нужно
да даже тот же вложенный try/catch можно в специализированную фукнцию завернуть, что бы вычисление в номальную ветку возвращалось
та можно
но мне не нравится try/catch
семантически
тогда надо делать throw
а throw это сайд эффект
а так мы меняем throw на возврат инстанса исключения
т.е. мы перешли в область нравится/не нравится и измеримых факторов, кроме скорости не осталось это не наезд, просто констатация у всех есть вкусы и это нормально
и получается красиво
не, я не говорю что flow что-то решает кроме читабельности
не вводя при этом новых абстракций типа right/left
так throwable у тебя и есть left)
или я не так понял?
нет, throwable это throwable
он "типа left"
ну вот)
если ты сравниваешь с either
а в мойе реализации Object - типа Right
а Left - это left
ладно, понятно более-менее
но throwable был есть и будет
это не новая абстракция
а left/right это хаскель
давай дальше уже в личку
давай)
только ты не отвечаешь
а вот
гады 🙂 Зажали размышления
Такая любопытная тема, а они в личку
да мы в личке datomic обсуждали
про исключения тут все
Прощаю 😁
подтвеждаю
https://dev.clojure.org/jira/browse/CLJ-2423 Miller против 🤷
> Обычно советуют исползовать either, maybe и т.п. для этого случая. мне кажется формулировка “обычно советуют” - ложна. Кто, где, почему? > На первый взгляд отличие исключения от either в том, что исключение дороже из-за стек трейса. дороговизна понятие относильное, но основное отличие - контекст исключение может быть поймано где выше в асбтракции и что с ним делать? тот же go to - не просто же так считается плохим паттерном
По поводу стека - это оптимизация, и к вопросу слабо относится.
и почему в итого either vs exception?
других вариантов нет?
ок, какие?
вернуть ошибку
и чем это поможет?
ладно
какую проблему вы хотите решить
Есть некий сценарий. что-то получить на вход проверить сделать операцию проверить контекст сделать операцию проверить результат что-то сделать
если проверка не прошла дальше делать нет смысла
и нужно вернуться на верх
т.е. подходит и exception и either но вроде как exception для этого не стоит использовать а either - нужно код особым образом писать
Failjure
это either
мне нужно особым обрзом код писать
Где?
flow
(if (empty? s)
(f/fail "Please enter a value")
s))
if test left right
все библиотеки заставляют особым образом код структурировать, чтобы можно было ошибку на верх передать я не очень понимаю зачем, если есть исключения
которы именно это и делают
может быть я что-то не так понимаю?
Не
Нет right
видимо, там right - это все, что не left
т.е. любой объект по дефолту - right
Тогда if - это монады )
С такой логикой
https://github.com/adambard/failjure/blob/master/src/failjure/core.clj#L19-L25
но это вообще не важно
А что важно?
чем either и компания лучше исключений?
Контекст
что за контекст?
Исключения могут быть обработаны где выше
Без доступа к контексту
что за контекст то?
адмирал ясен ... просто 😃
может пример какой-то есть?
контекста
В общем обсуждение довольно деструктивное
Какие проблемы решаются - я не понимаю
Так что врядли могу что подсказать
Если исключения работают - используйте
зачем тогда все эти flow, failjure?
Да кто знает
Понаписуют
Потом ломай голову
Everyone here is repeating the phrase “exceptions are for exceptional circumstances”, but that really doesn’t give any understanding of why its bad to use them for unexceptional circumstances. I need more than that. Is the performance hit of throwing exceptions really that bad? Are there any benchmarks available?
Рич норм вброс про изер и мейби слелал на конже.
На предыдущем проекте у нас было вида 3 разных имплементаций монад. Всё такой ад, даже с тредин-макросами, которые сами заворачивают/разворачивают конвертики эти. Самый ништяк - эксепшены с ex-data, и потом диспатчить в трай по типу эксепшена (какой-нибудь :error/type в ex-info)
@misha а есть ссылка на вброс?
или пара ключевиков?
Rich Hickey Maybe
опять побуду капитаном 😉
т.е. пока исключения проигрывают только по производительности
Youtube rich hickey maybe not
но в том толке maybe расматривается в другом контексте
Там реально надо каждую функцию или лифтить или рефакторить, в итоге запутываешься в бюрократии на ровном месте, где надо и где не надо
Ну это всё равно цена
то, что если метод возвращает мейби, а потом всегда только занчение и нужно всех потребителей переписывать?
Типа того, послушай, там первых минут 10-15
ну в clojure можно Object сделать Righht
я так и делал
т.е. это проблема в хаскеле
вернее даже в реализации
Ну а примитивы как? Заворачивать?
Да тебе потом в любом случае на клиенте проверять «лефт или обж-экстендс-райт»
> Ну а примитивы как? Заворачивать?
что за примитивы?
в clojure строка - это java.lang.String
а это объект
для nil отдельную реализацию
С тз поддержки кодобазы - это был major gemor. И оно ж потом как рак метастазы во все уголки проекта пускает
7
false
https://github.com/darkleaf/either/blob/master/src/darkleaf/either.clj#L16-L37
числа тоже объекты
булевы тоже
или я путаю?
(defprotocol Foo
(foo [x]))
(extend-protocol Foo
Object
(foo [_] :obj))
(foo 1)
(foo false)
работает
@misha а к чему в итоге то пришли? исключения?
Не всё завернуто как минимум потому что тебе может из жавы примитив предыдущая ф-я вернуть после интеропа
это да
К тому, что времени выкашивать нет. Там до сих пор срач, где эксепшены, где одни монады, где катс, где мапы еррор/валью, где вектора лево/право
Если кому-то интересны continuations - то можем обсудить. Я собрал jvm от проекта loom, который добавляет поддержку файберов. Вот маленький пример
(ns yield
(:import
[java.lang ContinuationScope Continuation]))
(let [scope (ContinuationScope. "example")
f (fn []
(prn :fn-start)
(Continuation/yield scope)
(prn :fn-end))
cont (Continuation. scope (fn []
(prn 1)
(Continuation/yield scope)
(f)))]
(while (not (.isDone cont))
(.run cont)
(prn :control)))
;; 1
;; :control
;; :fn-start
;; :control
;; :fn-end
;; :control