Здравствуйте, jazzer, Вы писали: J>Можно вернуть вариант (хм... непереводимая игра слов ), в котором будет зашит статический тип ордера.
Конкретнее? J>Это если мы хотим все свойства проверить в самом LoadOrder (что маловероятно — все-таки нам что-то делать с ордером хочется каждый раз разное — нет смысла пихать все это в LoadOrder).
Нет, мы не хотим все свойства проверять в самом LoadOrder. Например, проверку на fraud нужно сделать один раз — слишком дорого каждый раз обращаться к сервисам.
J>После этого, если это правильный вариант (т.е. со статическим диспатчем, как в Boost.Variant), у нас появляется возможность переложить контроль на компилятор.
Не понимаю. Тип, тип какой? J>Другой вариант — это передавать continuation в LoadOrder — тогда он будет называться LoadAndProcessOrder(int orderId, function whatToDo).
Это понятно — но дальше-то что? вот эта WhatToDo — она какого типа?
J>А можно вернуть "просто ордер", как в моем примере по ссылке — но ты с ним ничего реального не сможешь сделать. Вернее, сможешь, но только после проверки необходимых свойств — и попытки вызвать код для ордера, у которого необходимые свойства не проверены, просто не скомпилируются.
Пока непонятно. Получается, мы не можем персисить ордер за пределы текущего процесса — а это очень плохо, т.к. типичные workflow ордеров катастрофически медленные. Требование держать всё в памяти — невыполнимо.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[53]: Haskell нужен! (в Standard Chartered Bank)
так и быть, отвечу здесь, хотя не уверен, что в коня корм...
S>Здравствуйте, jazzer, Вы писали: J>>Можно вернуть вариант (хм... непереводимая игра слов ), в котором будет зашит статический тип ордера. S>Конкретнее?
boost::variant. J>>Это если мы хотим все свойства проверить в самом LoadOrder (что маловероятно — все-таки нам что-то делать с ордером хочется каждый раз разное — нет смысла пихать все это в LoadOrder). S>Нет, мы не хотим все свойства проверять в самом LoadOrder. Например, проверку на fraud нужно сделать один раз — слишком дорого каждый раз обращаться к сервисам.
Ну так можно результат проверки "заперсистить" и потом смотреть на него.
J>>После этого, если это правильный вариант (т.е. со статическим диспатчем, как в Boost.Variant), у нас появляется возможность переложить контроль на компилятор. S>Не понимаю. Тип, тип какой?
тип чего? варианта? ордер со всеми возможными свойствами (но это комбинаторный взрыв типов, как раз поэтому я за это не агитирую). J>>Другой вариант — это передавать continuation в LoadOrder — тогда он будет называться LoadAndProcessOrder(int orderId, function whatToDo). S>Это понятно — но дальше-то что? вот эта WhatToDo — она какого типа?
функциональный объект, в которм operator() перегружен по типам ордера (т.е. по типам свойств).
Для примера посмотри тут http://www.boost.org/doc/libs/1_57_0/doc/html/variant/tutorial.html#variant.tutorial.basic (найди на странице times_two_visitor и times_two_generic)
J>>А можно вернуть "просто ордер", как в моем примере по ссылке — но ты с ним ничего реального не сможешь сделать. Вернее, сможешь, но только после проверки необходимых свойств — и попытки вызвать код для ордера, у которого необходимые свойства не проверены, просто не скомпилируются. S>Пока непонятно. Получается, мы не можем персисить ордер за пределы текущего процесса — а это очень плохо, т.к. типичные workflow ордеров катастрофически медленные. Требование держать всё в памяти — невыполнимо.
по ссылке все же (я понимаю, что лень, но все-таки). Там есть свойства, которые сидят прямо в ордере, и замечательно "персистятся".
Так что сделал check_fraud, записал результат order.fraud_checked=true, order.fraud_check_result=true, заперсистил. У тебя уже новый тип, как после долгой проверки, так и после загрузки "заперсисченного" ордера из БД, непосредственно из order.fraud_check_result.
Здравствуйте, Sinclair, Вы писали:
S>Упрощу вопрос: вот у нас есть метод LoadOrder(int orderId). Какой тип он возвращает?
Это просто, как уроки не учить.
loadOrder :: (ЭтоНесущественныеДеталиРеализации k) => Int -> SomeSing k
Возвращает тип `SomeSing`
data SomeSing (kproxy :: KProxy k) where
SomeSing :: Sing (a :: k) -> SomeSing ('KProxy :: KProxy k)
Тут не обращайте внимание на `kproxy`, ключевой момент что на входе конструктора `SomeSing` есть `a`, а между `data` и `where` нету — это экзистенциальный тип.
SomeSing конструируется при загрузке, там же все что надо и проверяется в рантайме. Мы все данные об `Order` оформляем как синглетон `SOrderDescription` типа `OrderDescription`, который и передаем в конструктор `SomeSing`. После этого мы можем элиминировать `SomeSing` паттерн-матчингом и получить синглетон, а значит и описывающий `Order` тип:
case order of SomeSing order' -> procOrder order'
Где тип у ф-и
procOrder :: Sing (order :: OrderDescription) -> Result
все, дальше у нас все статически типизировано описанием `Order` полученным в рантайме. Т.е. рантайм проверка одна, дальше ее результат переносится типами.
Чтоб понятно было, как это работает продемонстрирую минимальный полный работающий пример. Вот в этой ветке http://rsdn.ru/forum/decl/5378899.1
шла дискуссия о том, что нельзя сделать в принципе в статтипизированном языке, а в бестиповом — можно. И там ответ написан на языке с завтипами.
Это же можно делать и на языке без завтипов:
{-# LANGUAGE PolyKinds, DataKinds #-}
{-# LANGUAGE TypeFamilies, GADTs #-}
{-# LANGUAGE NPlusKPatterns #-}
{-# LANGUAGE TypeOperators #-}import Data.Singletons
import Data.Singletons.Prelude
data Nat = Z | S Nat -- Nat это и тип и кайнд (тип типов)
integerToNat 0 = Z
integerToNat (n + 1) = S (integerToNat n)
-- Все что ниже генерируется автоматически
-- genSingletons [''Nat]
-- но приведено для образовательных целей
-- это синглетон для Natdata instance Sing (n :: Nat) where
SZ :: Sing Z
SS :: Sing n -> Sing (S n)
-- механизм трансляции между значениями типа Nat и значениями синглетон-типаinstance SingKind ('KProxy :: KProxy Nat) where
type DemoteRep ('KProxy :: KProxy Nat) = Nat
fromSing SZ = Z
fromSing (SS n) = S (fromSing n)
toSing Z = SomeSing SZ
toSing (S n) = case toSing n :: SomeSing ('KProxy :: KProxy Nat) of
SomeSing k -> SomeSing (SS k)
-- конец автоматически генерируемого бойлерплейта
-- функция создает вложенные списки (если n > 0)
create :: Sing (n :: Nat) -> MyType n
create SZ = 0
create (SS x) = [create x]
-- разных типов в зависимости
-- от передаваемого ей значения.type family MyType (a :: Nat)
type instance MyType Z = Int
type instance MyType (S x) = [MyType x]
-- функция сериализует в строку эти списки (разных типов)
-- тут от передаваемого значения зависит тип другого принимаемого значения
useValue :: Sing (n :: Nat) -> MyType n -> String
useValue SZ v = show v
useValue (SS x) v = "[" ++ (concat $ map (useValue x) v) ++ "]"
main = do
ns <- getLine -- пользователь вводит число с клавиатурыlet n = integerToNat . read $ ns -- парсим его
-- создаем из этого числа синглетон, дальше внутри лямбды
-- это число будет типом.
putStrLn $ withSomeSing n (\n -> useValue n (create n))
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[54]: Haskell нужен! (в Standard Chartered Bank)
?
Да, прочитал. J>Внимательно? А то ты задаешь вопросы, на которые есть ответы прямо там, причем в примерах кода.
Возможно, там неявно есть какие-то ответы. Но явно ничего про load_order нету — везде обыгрывается попытка изменения суммы ордера.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[53]: Haskell нужен! (в Standard Chartered Bank)
J>>Можно вернуть вариант (хм... непереводимая игра слов ), в котором будет зашит статический тип ордера. S>Конкретнее?
Я тут чем дальше читаю все эти ветки про типы и прочая, тем больше убеждаюсь в том, что все это — какие-то игры разума и сугубо теоретические конструкции, которые никто из присутсвующих никогда не использует в реальной жизни.
jazzer. В частности — из-за того, что нужно иметь пару PhD, чтобы смочь это описать в реальной жизни.
В итоге так и получается — пока PhD два года пишет внутренне непротиворечивую структуру того же Order'а на типах, в Виллабаджо уже взлетели, продались за 16 миллиардов, вышли на IPO и раз в неделю меняют требования к заказам по требованиям левой задней пятки заказчиков.
Это сравнительно новый подход (вызванный во многом выходом на арену языков с зависимыми типами и вообще языков с более мощной типизацией и метапрограммированием) — так что в судествующих языках просто-напросто еще нет инфраструктуры (библиотек, макросов, расширений типа LINQ и т.п.).
В моем варианте ничего сложного нет абсолютно (правда, я к простому варианту реализации пришел совсем не сразу, но то, что есть, меня устраивает).
M>В частности — из-за того самого комбинаторного взрыва типов, о котором говорит
Комбинаторный взрыв будет, только если мы захотим описать скопом все возможные состояния всех возможных свойств и запихнуть это дело в вариант. И только. Если использовать это как у меня (т.е. проерять те свойства, которые нас интересуют) — никакого взрыва не будет.
M>В частности — из-за того, что нужно иметь пару PhD, чтобы смочь это описать в реальной жизни.
Сорри, где ты видишь у меня что-то, для чего требуется пара PhD?
? S>Да, прочитал. J>>Внимательно? А то ты задаешь вопросы, на которые есть ответы прямо там, причем в примерах кода. S>Возможно, там неявно есть какие-то ответы. Но явно ничего про load_order нету — везде обыгрывается попытка изменения суммы ордера.
Я ответил рядом. Если очень нужно, напишу примеры кода, но, имхо, и так все понятно.
Здравствуйте, jazzer, Вы писали: J>Я ответил рядом. Если очень нужно, напишу примеры кода, но, имхо, и так все понятно.
Я пока увидел много рассуждений о том, как и что синтаксически развести. Есть риск того, что мне "и так всё понятно" неверно.
Давайте приведём хотя бы один пример тела функции load_order. Сигнатуры — это здорово, но писать-то надо не только их.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[57]: Haskell нужен! (в Standard Chartered Bank)
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, jazzer, Вы писали: J>>Я ответил рядом. Если очень нужно, напишу примеры кода, но, имхо, и так все понятно. S>Я пока увидел много рассуждений о том, как и что синтаксически развести. Есть риск того, что мне "и так всё понятно" неверно. S>Давайте приведём хотя бы один пример тела функции load_order. Сигнатуры — это здорово, но писать-то надо не только их.
да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств.
Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.
Чтоб не писать еще раз, цитирую (судя по всему, ты этот ответ просто не читал):
J>>А можно вернуть "просто ордер", как в моем примере по ссылке — но ты с ним ничего реального не сможешь сделать. Вернее, сможешь, но только после проверки необходимых свойств — и попытки вызвать код для ордера, у которого необходимые свойства не проверены, просто не скомпилируются.
S>Пока непонятно. Получается, мы не можем персисить ордер за пределы текущего процесса — а это очень плохо, т.к. типичные workflow ордеров катастрофически медленные. Требование держать всё в памяти — невыполнимо.
по ссылке все же (я понимаю, что лень, но все-таки). Там есть свойства, которые сидят прямо в ордере, и замечательно "персистятся".
Так что сделал check_fraud, записал результат order.fraud_checked=true, order.fraud_check_result=true, заперсистил. У тебя уже новый тип, как после долгой проверки, так и после загрузки "заперсисченного" ордера из БД, непосредственно из order.fraud_check_result.
Как именно проверяются свойства — см. PROP_IF в моем большом посте. И пример (там же) тела функции try_to_change_amount, которая пытается вызвать функцию increase_amount, но та требует наличия свойств HasRisk:false и Shipped:false и не дает себя звать без предварительной проверки оных.
Если что-то непонятно — спрашивай.
ЗЫ Серьезно, складывается впечатление, что ты читаешь мои ответы через раз и по диагонали. Очень неприятно.
J>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств. J>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.
Я же говорю: теоретики.
Заказ в базе лежит в одном из нцати состояний (то есть имеет нцать свойств). Если его возвращать из load_order «безо всяких свойств», то откуда эти свойства возмутся, когда внезапно требуются «те или иные свойства»?
Здравствуйте, Mamut, Вы писали:
J>>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств. J>>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.
M>Я же говорю: теоретики.
M>Заказ в базе лежит в одном из нцати состояний (то есть имеет нцать свойств). Если его возвращать из load_order «безо всяких свойств», то откуда эти свойства возмутся, когда внезапно требуются «те или иные свойства»?
Вот ты бы сначала задал вопросы, получил ответы, а потом уже раздавал бы оценки
Откуда свойства берутся, показано в моей статье на примере increase_amount и try_to_change_amount (ты-то ее прочитал, даже оценку поставил же).
Считай, что Order в статье был извлечен из БД как есть.
J>Вот ты бы сначала задал вопросы, получил ответы, а потом уже раздавал бы оценки
Я задавал множество вопросов. Ответы если и получал, то неполные и после множество долгих рассказов и сказок.
J>Откуда свойства берутся, показано в моей статье на примере increase_amount и try_to_change_amount (ты-то ее прочитал, даже оценку поставил же).
Оценку я поставил, потому что да — наконец-то появился нормальный внятный и полноценный ответ на мои простейшие вопросы. И да, подход показался интересным.
J>Считай, что Order в статье был извлечен из БД как есть. J>Если есть вопросы — задавай, а не зубоскаль.
Вон — Sinclair задает внятные вопросы. Твою реакцию на них тебе процитировать, или сам найдешь?
Здравствуйте, Mamut, Вы писали:
J>>Вот ты бы сначала задал вопросы, получил ответы, а потом уже раздавал бы оценки
M>Я задавал множество вопросов. Ответы если и получал, то неполные и после множество долгих рассказов и сказок.
Никаких сказок, я всю дорогу описывал подход, который в результате детально описал в статье.
J>>Откуда свойства берутся, показано в моей статье на примере increase_amount и try_to_change_amount (ты-то ее прочитал, даже оценку поставил же).
M>Оценку я поставил, потому что да — наконец-то появился нормальный внятный и полноценный ответ на мои простейшие вопросы. И да, подход показался интересным.
Тогда непонятно твое ёрничанье. В моем подходе есть ответы и на твои вопросы, и на вопросы Синклера. Я их уже несколько раз разъяснил. Если что-то непонятно — задай конкретные вопросы.
J>>Считай, что Order в статье был извлечен из БД как есть. J>>Если есть вопросы — задавай, а не зубоскаль.
M>Вон — Sinclair задает внятные вопросы. Твою реакцию на них тебе процитировать, или сам найдешь?
И что конкретно тебе непонятно в моих ответах ему? Мою реакцию на его посты оставь между нами, плиз, я ее вполне внятно объяснил.
Здравствуйте, jazzer, Вы писали:
J>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств. J>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.
И, простите, чем это отличается от нашего нынешнего кода, где изо всех типов есть только самый обычный ордер, безо всяких "свойств"?
У меня весь workflow состоит из вот таких шагов:
— поднять ордер из базы
— попытаться двинуться на шаг вперёд
— сохранить ордер в базу (не нужно, если transition failed).
J>Как именно проверяются свойства — см. PROP_IF в моем большом посте. И пример (там же) тела функции try_to_change_amount, которая пытается вызвать функцию increase_amount, но та требует наличия свойств HasRisk:false и Shipped:false и не дает себя звать без предварительной проверки оных.
J>Если что-то непонятно — спрашивай.
Непонятно, в чём бенефит статической типизации. Я могу описать функцию increase_amount вот так:
Вся разница — в том, что теперь в try_increase_amount мне придётся провести корректное приведение типов:
public void Order try_increase_amount(Order order, Money newAmount)
{
var o = checkNoRisk(order); // выбросим исключение, если есть риск, вернём Order<Risk.None, S> иначеvar o2 = checkNotShipped(o); // выбросим исключение, если зашиплен, вернём Order<Risk.None, Shipped.None> иначеreturn increase_amount(order// не компилируетсяo// не компилируется
o2, newAmount); // ура, компилируется!!!
}
Но мы наблюдаем те же яйца, вид сбоку. Всё, try_increase_amount — это наш верхний уровень, мы не можем ничего доказать за её пределами.
У нас совершенно нормально иметь workflow, в котором мы пытаемся увеличить amount у уже отправленного ордера. Потому что компилятор видит ровно один шаг этого workflow.
В итоге, мы написали кучу кода, напрягли компилятор, но никак не улучшили поведение системы. У нас уже и так increase_amount была вполне локальна — все бизнес-правила выписаны в начале, шансы напортачить в них минимальны. Ну вынесли мы требования к флагам из тела в сигнатуру — так ведь нам это никак не помогло!
J>ЗЫ Серьезно, складывается впечатление, что ты читаешь мои ответы через раз и по диагонали. Очень неприятно.
А у меня наоборот — складывается впечатление, что ты отвечаешь не на те вопросы, которые я задаю.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[59]: Haskell нужен! (в Standard Chartered Bank)
Здравствуйте, Sinclair, Вы писали:
J>>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств. J>>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства. S>И, простите, чем это отличается от нашего нынешнего кода, где изо всех типов есть только самый обычный ордер, безо всяких "свойств"?
Тем что теперь компилятор будет заставлять пользователя проверять свойства. И соответственно он не сможет проигнорировать тот факт, что тут две логические ветки (свойство верно или неверно), и не сможет порушить предусловия вызываемой функции.
S>Непонятно, в чём бенефит статической типизации. Я могу описать функцию increase_amount вот так: S>
Что проверяют твои Assert'ы — нарушение предусловия или вполне легальный вариант данных? Это важно.
Обычно assert означает нарушение предусловия, и лучшей реакцией будет склейка ласт ASAP — так как программа попала в неизвестное состояние, непонятно каким способом.
EP>Нарушение предусловия это баг в программе, программа находится в том состоянии в котором не должна была по логике вещей, и как она туда попала неизвестно, лучше всего пристрелить такую программу как взбесившуюся собаку. Почитай наконец что такое precondition.
EP>Предусловия можно проверять, и как-то энфорсить, но отнюдь не обязательно. Например assert'ы энфорсящие предусловия часто используют только в отладочных режимах.
EP>Система типов позволяет энфорсить некоторые предусловия в compile-time, но не все. Я даже больше скажу, далеко не все предусловия можно проверить в runtime — попытка такой проверки попросту может поломать инварианты, и сделать невозможным достижение постусловий.
S>Вся разница — в том, что теперь в try_increase_amount мне придётся провести корректное приведение типов: S>
...
S> var o = checkNoRisk(order); // выбросим исключение, если есть риск, вернём Order<Risk.None, S> иначе
S> var o2 = checkNotShipped(o); // выбросим исключение, если зашиплен, вернём Order<Risk.None, Shipped.None> иначе
S>...
Необязательно исключение, это могут быть две разные ветки.
S>Но мы наблюдаем те же яйца, вид сбоку. Всё, try_increase_amount — это наш верхний уровень, мы не можем ничего доказать за её пределами. S>У нас совершенно нормально иметь workflow, в котором мы пытаемся увеличить amount у уже отправленного ордера. Потому что компилятор видит ровно один шаг этого workflow. S>В итоге, мы написали кучу кода, напрягли компилятор, но никак не улучшили поведение системы. У нас уже и так increase_amount была вполне локальна — все бизнес-правила выписаны в начале, шансы напортачить в них минимальны. Ну вынесли мы требования к флагам из тела в сигнатуру — так ведь нам это никак не помогло!
это действительно не имеет смысла. Я уже многократно об этом говорил:
EP>Если же для запуска функции нет необходимости делать проверки, change_amount сама всё что нужно проверяет внутри, и более того её запуск с невыполненными условиями не является ошибкой, а вполне штатным режимом — то пытаться переписать все эти внутренние проверки на типы, не вижу смысла, это никак не отразится на местах вызова этой функции.
это действительно не имеет смысла. Я уже многократно об этом говорил: EP>
EP>>Если же для запуска функции нет необходимости делать проверки, change_amount сама всё что нужно проверяет внутри, и более того её запуск с невыполненными условиями не является ошибкой, а вполне штатным режимом — то пытаться переписать все эти внутренние проверки на типы, не вижу смысла, это никак не отразится на местах вызова этой функции.
Я про это тоже говорил. Ты придумал себе какие-то предусловия и активно пытаешься это энфорсить. В моем случае условия никуда не исчезли — это все те же условия. Только ВНЕЗАПНО для них «не имеет смысла»
Вообще, экстраполируя твое заявление, получается, что хваленые типы вообще не имеют смысла почти никогда. Ведь если программисты вменяемые, то будет банальный вызов функции, в который передастся бизнес-объект, над которым будут произведены какие-то действия, а обратно вернется уже измененный объект.
Где «это» тогда имеет смысл? То, что в process_order/increase_order_amount/archive_order мы передаем именно Order, а не GoodsItem? Ну это у нас и так отлавливают интеграционные тесты, и таких случаев возникает хорошо если два в год.
Здравствуйте, Mamut, Вы писали:
M>Я про это тоже говорил. Ты придумал себе какие-то предусловия и активно пытаешься это энфорсить. В моем случае условия никуда не исчезли — это все те же условия. Только ВНЕЗАПНО для них «не имеет смысла»
Повторюсь:
EP>Состояние ранее являвшееся ошибкой, стало вполне штатным режимом работы, и это повлияло на возможные варианты реализации этого в коде. И что тебя удивляет?
M>Вообще, экстраполируя твое заявление, получается, что хваленые типы вообще не имеют смысла почти никогда.
Что за мода такая пошла на экстраполяцию из каких-то частных случаев?
M>Ведь если программисты вменяемые, то будет банальный вызов функции, в который передастся бизнес-объект, над которым будут произведены какие-то действия, а обратно вернется уже измененный объект.
То есть предусловий нет? О чём и речь
M>Где «это» тогда имеет смысл?
Для тех задач где есть какие-то предусловия.
Re[59]: Haskell нужен! (в Standard Chartered Bank)
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, jazzer, Вы писали:
J>>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств. J>>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства. S>И, простите, чем это отличается от нашего нынешнего кода, где изо всех типов есть только самый обычный ордер, безо всяких "свойств"?
Отличается тем, что проверки допустимых операций (вызов недопустимой операции — ошибка программиста) происходят во время компиляции.
Таким образом, недопустимый код просто не компилируется и в продакшен не попадает физически.
S>Непонятно, в чём бенефит статической типизации. Я могу описать функцию increase_amount вот так: S>
// выбросим исключение!
А у тебя проверки допустимых операций (вызов недопустимой операции — ошибка программиста) происходят во время выполнения (в продакшене, да).
Твой аргумент, по сути — нафига нам статическая типизация, если мы можем устроить динамическую и бросаться исключениями в случае ошибки программиста?
И ничего, что исключения имеют обыкновение вылетать в продакшене в момент обработки сделаки на миллион.
S>В итоге, мы написали кучу кода, напрягли компилятор, но никак не улучшили поведение системы.
А типы, вообще-то, не о том, чтоб улучшить поведение системы.
Корректно работающая система будет работать корректно независимо от того, написана она на на статической типизации или на рукопашном ассемблере по живой памяти.
Они о том, чтоб исключить ошибки при ее написании.
Чтоб программист в принципе не мог сложить апельсины с градусами, а не откладывал выяснение этого факта до вылета исключения в продакшене.
Твой же подход не исключает ошибки, а наоборот, провоцирует: "Пишите, что хотите! Если напишете фигню — вылетит исключение!"
А юзеру покажем баннер: "Извините, у нас тут произошло исключение, похоже, программер накосячил и позвал не ту функцию, мы его высечем, баг пофиксим, зарелизим, через недельку приходите и попробуйте снова".
S>У нас уже и так increase_amount была вполне локальна — все бизнес-правила выписаны в начале, шансы напортачить в них минимальны. Ну вынесли мы требования к флагам из тела в сигнатуру — так ведь нам это никак не помогло!
Помогло, так как теперь компилятор следит, чтоб ты не вызвал то, что нельзя. А не ждал исключения в продакшене.
J>>ЗЫ Серьезно, складывается впечатление, что ты читаешь мои ответы через раз и по диагонали. Очень неприятно. S>А у меня наоборот — складывается впечатление, что ты отвечаешь не на те вопросы, которые я задаю.
Ну вот сейчас ты задал нормальный вопрос. Правда, он о том, зачем вообще нужна статическая типизация, если можно все проверять в динамике и бросаться исключениями, но тем не менее это нормальный вопрос.
M>>Вообще, экстраполируя твое заявление, получается, что хваленые типы вообще не имеют смысла почти никогда. EP>Что за мода такая пошла на экстраполяцию из каких-то частных случаев?
Какие к черту частные случаи? Ты говоришь про общий случай уже ниже:
EP>То есть предусловий нет? О чём и речь
M>>Где «это» тогда имеет смысл? EP>Для тех задач где есть какие-то предусловия.
Чем это принципиально отличается от одной точки входа, после которой идут условия? Простейший вопрос:
Было: copy-paste кода в 5 мест в виде if(X)->if(Y)->if(Z)->вызов_функции(). Раскорячились, написали «предусловия» на типах