Re[52]: Haskell нужен! (в Standard Chartered Bank)
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.02.15 10:05
Оценка: +1
Здравствуйте, 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)
От: jazzer Россия Skype: enerjazzer
Дата: 24.02.15 10:41
Оценка:
Здравствуйте, Sinclair, Вы писали:

Сорри, ты правда прочитал мой пост
Автор: jazzer
Дата: 10.02.15
? Внимательно? А то ты задаешь вопросы, на которые есть ответы прямо там, причем в примерах кода.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[53]: Haskell нужен! (в Standard Chartered Bank)
От: jazzer Россия Skype: enerjazzer
Дата: 24.02.15 15:03
Оценка:
Здравствуйте, Sinclair, Вы писали:

так и быть, отвечу здесь, хотя не уверен, что в коня корм...

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 ордеров катастрофически медленные. Требование держать всё в памяти — невыполнимо.

Посмотри на мой код
Автор: jazzer
Дата: 10.02.15
по ссылке все же (я понимаю, что лень, но все-таки). Там есть свойства, которые сидят прямо в ордере, и замечательно "персистятся".
Так что сделал check_fraud, записал результат order.fraud_checked=true, order.fraud_check_result=true, заперсистил. У тебя уже новый тип, как после долгой проверки, так и после загрузки "заперсисченного" ордера из БД, непосредственно из order.fraud_check_result.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[51]: Haskell нужен! (в Standard Chartered Bank)
От: Klapaucius  
Дата: 25.02.15 10:12
Оценка:
Здравствуйте, 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
Автор: D. Mon
Дата: 30.11.13
шла дискуссия о том, что нельзя сделать в принципе в статтипизированном языке, а в бестиповом — можно. И там ответ написан на языке с завтипами.
Это же можно делать и на языке без завтипов:

{-# 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]
-- но приведено для образовательных целей

-- это синглетон для Nat
data 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)
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.02.15 18:37
Оценка: +1
Здравствуйте, jazzer, Вы писали:

J>Сорри, ты правда прочитал мой пост
Автор: jazzer
Дата: 10.02.15
?

Да, прочитал.
J>Внимательно? А то ты задаешь вопросы, на которые есть ответы прямо там, причем в примерах кода.
Возможно, там неявно есть какие-то ответы. Но явно ничего про load_order нету — везде обыгрывается попытка изменения суммы ордера.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[53]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 26.02.15 05:28
Оценка:
J>>Можно вернуть вариант (хм... непереводимая игра слов ), в котором будет зашит статический тип ордера.
S>Конкретнее?

Я тут чем дальше читаю все эти ветки про типы и прочая, тем больше убеждаюсь в том, что все это — какие-то игры разума и сугубо теоретические конструкции, которые никто из присутсвующих никогда не использует в реальной жизни.

Что здесь
Автор: Klapaucius
Дата: 17.02.15
, что здесь
Автор: Evgeny.Panasyuk
Дата: 22.02.15
, что здесь
Автор: jazzer
Дата: 10.02.15
и т.п.

В частности — из-за того самого комбинаторного взрыва типов, о котором говорит
Автор: jazzer
Дата: 24.02.15
jazzer. В частности — из-за того, что нужно иметь пару PhD, чтобы смочь это описать в реальной жизни.

В итоге так и получается — пока PhD два года пишет внутренне непротиворечивую структуру того же Order'а на типах, в Виллабаджо уже взлетели, продались за 16 миллиардов, вышли на IPO и раз в неделю меняют требования к заказам по требованиям левой задней пятки заказчиков.


dmitriid.comGitHubLinkedIn
Re[54]: Haskell нужен! (в Standard Chartered Bank)
От: jazzer Россия Skype: enerjazzer
Дата: 26.02.15 06:21
Оценка: +2
Здравствуйте, Mamut, Вы писали:

M>Что здесь
Автор: Klapaucius
Дата: 17.02.15
, что здесь
Автор: Evgeny.Panasyuk
Дата: 22.02.15
, что здесь
Автор: jazzer
Дата: 10.02.15
и т.п.


Это сравнительно новый подход (вызванный во многом выходом на арену языков с зависимыми типами и вообще языков с более мощной типизацией и метапрограммированием) — так что в судествующих языках просто-напросто еще нет инфраструктуры (библиотек, макросов, расширений типа LINQ и т.п.).
В моем варианте ничего сложного нет абсолютно (правда, я к простому варианту реализации пришел совсем не сразу, но то, что есть, меня устраивает).

M>В частности — из-за того самого комбинаторного взрыва типов, о котором говорит
Автор: jazzer
Дата: 24.02.15
jazzer.


Комбинаторный взрыв будет, только если мы захотим описать скопом все возможные состояния всех возможных свойств и запихнуть это дело в вариант. И только. Если использовать это как у меня (т.е. проерять те свойства, которые нас интересуют) — никакого взрыва не будет.

M>В частности — из-за того, что нужно иметь пару PhD, чтобы смочь это описать в реальной жизни.


Сорри, где ты видишь у меня что-то, для чего требуется пара PhD?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[55]: Haskell нужен! (в Standard Chartered Bank)
От: jazzer Россия Skype: enerjazzer
Дата: 26.02.15 06:23
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, jazzer, Вы писали:


J>>Сорри, ты правда прочитал мой пост
Автор: jazzer
Дата: 10.02.15
?

S>Да, прочитал.
J>>Внимательно? А то ты задаешь вопросы, на которые есть ответы прямо там, причем в примерах кода.
S>Возможно, там неявно есть какие-то ответы. Но явно ничего про load_order нету — везде обыгрывается попытка изменения суммы ордера.

Я ответил рядом. Если очень нужно, напишу примеры кода, но, имхо, и так все понятно.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[56]: Haskell нужен! (в Standard Chartered Bank)
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.02.15 08:48
Оценка: +1
Здравствуйте, jazzer, Вы писали:
J>Я ответил рядом. Если очень нужно, напишу примеры кода, но, имхо, и так все понятно.
Я пока увидел много рассуждений о том, как и что синтаксически развести. Есть риск того, что мне "и так всё понятно" неверно.
Давайте приведём хотя бы один пример тела функции load_order. Сигнатуры — это здорово, но писать-то надо не только их.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[57]: Haskell нужен! (в Standard Chartered Bank)
От: jazzer Россия Skype: enerjazzer
Дата: 26.02.15 09:07
Оценка: -1 :)
Здравствуйте, 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 и не дает себя звать без предварительной проверки оных.
Если что-то непонятно — спрашивай.


ЗЫ Серьезно, складывается впечатление, что ты читаешь мои ответы через раз и по диагонали. Очень неприятно.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[58]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 26.02.15 09:29
Оценка:
J>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств.
J>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.

Я же говорю: теоретики.

Заказ в базе лежит в одном из нцати состояний (то есть имеет нцать свойств). Если его возвращать из load_order «безо всяких свойств», то откуда эти свойства возмутся, когда внезапно требуются «те или иные свойства»?


dmitriid.comGitHubLinkedIn
Re[59]: Haskell нужен! (в Standard Chartered Bank)
От: jazzer Россия Skype: enerjazzer
Дата: 26.02.15 09:37
Оценка: +1
Здравствуйте, Mamut, Вы писали:

J>>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств.

J>>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.

M>Я же говорю: теоретики.


M>Заказ в базе лежит в одном из нцати состояний (то есть имеет нцать свойств). Если его возвращать из load_order «безо всяких свойств», то откуда эти свойства возмутся, когда внезапно требуются «те или иные свойства»?


Вот ты бы сначала задал вопросы, получил ответы, а потом уже раздавал бы оценки

Откуда свойства берутся, показано в моей статье на примере increase_amount и try_to_change_amount (ты-то ее прочитал, даже оценку поставил же).
Считай, что Order в статье был извлечен из БД как есть.

Если есть вопросы — задавай, а не зубоскаль.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[60]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 26.02.15 10:04
Оценка:
J>Вот ты бы сначала задал вопросы, получил ответы, а потом уже раздавал бы оценки

Я задавал множество вопросов. Ответы если и получал, то неполные и после множество долгих рассказов и сказок.

J>Откуда свойства берутся, показано в моей статье на примере increase_amount и try_to_change_amount (ты-то ее прочитал, даже оценку поставил же).


Оценку я поставил, потому что да — наконец-то появился нормальный внятный и полноценный ответ на мои простейшие вопросы. И да, подход показался интересным.

J>Считай, что Order в статье был извлечен из БД как есть.

J>Если есть вопросы — задавай, а не зубоскаль.

Вон — Sinclair задает внятные вопросы. Твою реакцию на них тебе процитировать, или сам найдешь?


dmitriid.comGitHubLinkedIn
Re[61]: Haskell нужен! (в Standard Chartered Bank)
От: jazzer Россия Skype: enerjazzer
Дата: 26.02.15 10:23
Оценка:
Здравствуйте, Mamut, Вы писали:

J>>Вот ты бы сначала задал вопросы, получил ответы, а потом уже раздавал бы оценки


M>Я задавал множество вопросов. Ответы если и получал, то неполные и после множество долгих рассказов и сказок.


Никаких сказок, я всю дорогу описывал подход, который в результате детально описал в статье.

J>>Откуда свойства берутся, показано в моей статье на примере increase_amount и try_to_change_amount (ты-то ее прочитал, даже оценку поставил же).


M>Оценку я поставил, потому что да — наконец-то появился нормальный внятный и полноценный ответ на мои простейшие вопросы. И да, подход показался интересным.


Тогда непонятно твое ёрничанье. В моем подходе есть ответы и на твои вопросы, и на вопросы Синклера. Я их уже несколько раз разъяснил. Если что-то непонятно — задай конкретные вопросы.

J>>Считай, что Order в статье был извлечен из БД как есть.

J>>Если есть вопросы — задавай, а не зубоскаль.

M>Вон — Sinclair задает внятные вопросы. Твою реакцию на них тебе процитировать, или сам найдешь?


И что конкретно тебе непонятно в моих ответах ему? Мою реакцию на его посты оставь между нами, плиз, я ее вполне внятно объяснил.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[58]: Haskell нужен! (в Standard Chartered Bank)
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.02.15 14:20
Оценка: 72 (2) +2
Здравствуйте, 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 вот так:
public void Order increase_amount(Order order, Money newAmount)
{
  Contracts.Assert(order, (o)=> !o.HasRisk); // выбросим исключение!
  Contracts.Assert(order, (o)=> !o.Shipped); // выбросим исключение!

  Contracts.Assert(order, (o)=> newAmount <= 1.20 * o.CurrentAmount) // выбросим исключение!

  return order.Clone().SetAmount(newAmount);
}

А могу — вот так:
public void Order<Risk.None, Shipped.None> increase_amount(Order<Risk.None, Shipped.None> order, Money newAmount)
{
  Contracts.Assert(order, (o)=> newAmount <= 1.20 * o.CurrentAmount) // выбросим исключение!
  return order.Clone().SetAmount(newAmount);
}


Вся разница — в том, что теперь в 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)
От: Evgeny.Panasyuk Россия  
Дата: 26.02.15 14:43
Оценка:
Здравствуйте, Sinclair, Вы писали:

J>>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств.

J>>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.
S>И, простите, чем это отличается от нашего нынешнего кода, где изо всех типов есть только самый обычный ордер, безо всяких "свойств"?

Тем что теперь компилятор будет заставлять пользователя проверять свойства. И соответственно он не сможет проигнорировать тот факт, что тут две логические ветки (свойство верно или неверно), и не сможет порушить предусловия вызываемой функции.

S>Непонятно, в чём бенефит статической типизации. Я могу описать функцию increase_amount вот так:

S>
S>public void Order increase_amount(Order order, Money newAmount)
S>{
S>  Contracts.Assert(order, (o)=> !o.HasRisk); // выбросим исключение!
S>  Contracts.Assert(order, (o)=> !o.Shipped); // выбросим исключение!
S>  Contracts.Assert(order, (o)=> newAmount <= 1.20 * o.CurrentAmount) // выбросим исключение!
S>  return order.Clone().SetAmount(newAmount);
S>}
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 была вполне локальна — все бизнес-правила выписаны в начале, шансы напортачить в них минимальны. Ну вынесли мы требования к флагам из тела в сигнатуру — так ведь нам это никак не помогло!

Для задачи Mamut'а
Автор: Mamut
Дата: 05.02.15
это действительно не имеет смысла. Я уже многократно об этом говорил:

EP>Если же для запуска функции нет необходимости делать проверки, change_amount сама всё что нужно проверяет внутри, и более того её запуск с невыполненными условиями не является ошибкой, а вполне штатным режимом — то пытаться переписать все эти внутренние проверки на типы, не вижу смысла, это никак не отразится на местах вызова этой функции.

Отредактировано 26.02.2015 14:44 Evgeny.Panasyuk . Предыдущая версия .
Re[60]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 26.02.15 14:58
Оценка:
EP>Для задачи Mamut'а
Автор: Mamut
Дата: 05.02.15
это действительно не имеет смысла. Я уже многократно об этом говорил:

EP>

EP>>Если же для запуска функции нет необходимости делать проверки, change_amount сама всё что нужно проверяет внутри, и более того её запуск с невыполненными условиями не является ошибкой, а вполне штатным режимом — то пытаться переписать все эти внутренние проверки на типы, не вижу смысла, это никак не отразится на местах вызова этой функции.



Я про это тоже говорил. Ты придумал себе какие-то предусловия и активно пытаешься это энфорсить. В моем случае условия никуда не исчезли — это все те же условия. Только ВНЕЗАПНО для них «не имеет смысла»

Вообще, экстраполируя твое заявление, получается, что хваленые типы вообще не имеют смысла почти никогда. Ведь если программисты вменяемые, то будет банальный вызов функции, в который передастся бизнес-объект, над которым будут произведены какие-то действия, а обратно вернется уже измененный объект.

В итоге получается что?


Order = load_order(Id),
UpdatedOrder = process_order(Order).  
  %% внутри process_order «это действительно не имеет смысла» ©

....

%% в другом месте

Order = load_order(Id),
UpdatedOrder = increase_order_amount(Order, Amount).
  %% внутри increase_order_amount «это действительно не имеет смысла» ©


%% в третьем месте

Order = load_order(Id),
UpdatedOrder = archive_order(Order).
  %% внутри archive_order «это действительно не имеет смысла» ©

%% и т.п.


Где «это» тогда имеет смысл? То, что в process_order/increase_order_amount/archive_order мы передаем именно Order, а не GoodsItem? Ну это у нас и так отлавливают интеграционные тесты, и таких случаев возникает хорошо если два в год.



dmitriid.comGitHubLinkedIn
Re[61]: Haskell нужен! (в Standard Chartered Bank)
От: Evgeny.Panasyuk Россия  
Дата: 26.02.15 15:16
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Я про это тоже говорил. Ты придумал себе какие-то предусловия и активно пытаешься это энфорсить. В моем случае условия никуда не исчезли — это все те же условия. Только ВНЕЗАПНО для них «не имеет смысла»


Повторюсь:

EP>Состояние ранее являвшееся ошибкой, стало вполне штатным режимом работы, и это повлияло на возможные варианты реализации этого в коде. И что тебя удивляет?


M>Вообще, экстраполируя твое заявление, получается, что хваленые типы вообще не имеют смысла почти никогда.


Что за мода такая пошла на экстраполяцию из каких-то частных случаев?

M>Ведь если программисты вменяемые, то будет банальный вызов функции, в который передастся бизнес-объект, над которым будут произведены какие-то действия, а обратно вернется уже измененный объект.


То есть предусловий нет? О чём и речь

M>Где «это» тогда имеет смысл?


Для тех задач где есть какие-то предусловия.
Re[59]: Haskell нужен! (в Standard Chartered Bank)
От: jazzer Россия Skype: enerjazzer
Дата: 26.02.15 15:40
Оценка: +2
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, jazzer, Вы писали:


J>>да нету никакого кода в load_order, самый обычный код, возвращающий самый обычный order безо всяких свойств.

J>>Все интересное начинается потом, когда ты собираешься что-то с ордером делать — а функции, которые ты собираешься звать, требуют те или иные установленные свойства.
S>И, простите, чем это отличается от нашего нынешнего кода, где изо всех типов есть только самый обычный ордер, безо всяких "свойств"?

Отличается тем, что проверки допустимых операций (вызов недопустимой операции — ошибка программиста) происходят во время компиляции.
Таким образом, недопустимый код просто не компилируется и в продакшен не попадает физически.

S>Непонятно, в чём бенефит статической типизации. Я могу описать функцию increase_amount вот так:

S>
// выбросим исключение!


А у тебя проверки допустимых операций (вызов недопустимой операции — ошибка программиста) происходят во время выполнения (в продакшене, да).

Твой аргумент, по сути — нафига нам статическая типизация, если мы можем устроить динамическую и бросаться исключениями в случае ошибки программиста?
И ничего, что исключения имеют обыкновение вылетать в продакшене в момент обработки сделаки на миллион.

S>В итоге, мы написали кучу кода, напрягли компилятор, но никак не улучшили поведение системы.


А типы, вообще-то, не о том, чтоб улучшить поведение системы.
Корректно работающая система будет работать корректно независимо от того, написана она на на статической типизации или на рукопашном ассемблере по живой памяти.

Они о том, чтоб исключить ошибки при ее написании.
Чтоб программист в принципе не мог сложить апельсины с градусами, а не откладывал выяснение этого факта до вылета исключения в продакшене.

Твой же подход не исключает ошибки, а наоборот, провоцирует: "Пишите, что хотите! Если напишете фигню — вылетит исключение!"
А юзеру покажем баннер: "Извините, у нас тут произошло исключение, похоже, программер накосячил и позвал не ту функцию, мы его высечем, баг пофиксим, зарелизим, через недельку приходите и попробуйте снова".

S>У нас уже и так increase_amount была вполне локальна — все бизнес-правила выписаны в начале, шансы напортачить в них минимальны. Ну вынесли мы требования к флагам из тела в сигнатуру — так ведь нам это никак не помогло!


Помогло, так как теперь компилятор следит, чтоб ты не вызвал то, что нельзя. А не ждал исключения в продакшене.

J>>ЗЫ Серьезно, складывается впечатление, что ты читаешь мои ответы через раз и по диагонали. Очень неприятно.

S>А у меня наоборот — складывается впечатление, что ты отвечаешь не на те вопросы, которые я задаю.

Ну вот сейчас ты задал нормальный вопрос. Правда, он о том, зачем вообще нужна статическая типизация, если можно все проверять в динамике и бросаться исключениями, но тем не менее это нормальный вопрос.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[62]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 26.02.15 16:11
Оценка:
M>>Вообще, экстраполируя твое заявление, получается, что хваленые типы вообще не имеют смысла почти никогда.
EP>Что за мода такая пошла на экстраполяцию из каких-то частных случаев?

Какие к черту частные случаи? Ты говоришь про общий случай уже ниже:

EP>То есть предусловий нет? О чём и речь


M>>Где «это» тогда имеет смысл?

EP>Для тех задач где есть какие-то предусловия.

Чем это принципиально отличается от одной точки входа, после которой идут условия? Простейший вопрос:

Было: copy-paste кода в 5 мест в виде if(X)->if(Y)->if(Z)->вызов_функции(). Раскорячились, написали «предусловия» на типах

Стало: перенесли if(X)->if(Y)->if(Z) в саму функцию. Теперь в пяти местах стоит только вызов_функции(). Все, «это действительно не имеет смысла»© и «То есть предусловий нет? О чём и речь»© ?



Я вот этого момента не понимаю.


dmitriid.comGitHubLinkedIn
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.