Re[14]: Haskell нужен! (в Standard Chartered Bank)
От: Klapaucius  
Дата: 04.02.15 12:03
Оценка:
Здравствуйте, Mamut, Вы писали:

K>>Ок, с конкретикой: нельзя будет сконструировать заказ с состоянием "Отправленный" и "Непроверенный как написано в 4-м пункте" одновременно, так что в случае забывания 4-го пункта будет ошибка компиляции. Ну, как нельзя забыть проверить значение типа Maybe Foo на пустоту и вызвать функцию bar заданную на Foo, потому что она на Maybe Foo не определена.


M>Легко предание, да не верится вообще нифига.


Во что не верится-то? В каком месте тут вера нужна?

M>Сколько условий «Непроверенный как написано в энном пункте» предлагаешь впихивать в тип?


Столько сколько нужно.

M>Каким образом это поможет мне не забыть вписать в тип ошибку «Непроверенный как написано в энном пункте»? <- вот это и есть логическая ошибка, от которой все сказки про типы не спасут


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

M>Каким образом это мне поможет на этапе компиляции, если вся информация о заказе идет в рантайме (включая такие прекрасные вещи, как конфигурации десятка параметров из базы данных)?


В чем вы тут проблему увидели?

M>Ты перенес все эти четыре десятка условий в типы, молодца. Как ты собираешься проверять, что ты эти условия описал в типах логически правильно?


См. выше.

M>Это я еще умолчал о разных веселых сайд-эффектах:

M>- отсылать e-mail'ы разных типов при разных действиях
M>- в зависимости от конфигураций и того, что выбрал пользователь, отсылать бумажные письма
M>- дергать payments/dunning/bookkeeping за разные части в разные моменты
M>- вызывать risk check и производить разные действия в зависимости от результата. Не на всех этапах, естественно
M>- обрабатывать случаи нестандартной интеграции (в основном, для очень больших магазинов)
M>и еще вагон и маленькая тележка

И? Тем больше пользы будет от стат. проверки типов.
'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[18]: Haskell нужен! (в Standard Chartered Bank)
От: AlexRK  
Дата: 04.02.15 12:03
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Спасибо. Это все равно возвращает к изначальным:

M>- кто и как будет проверять, правильно ли реализована логика в этих типах

По идее, проверять будет компилятор. Проверить можно только "где-то в середине" — там, где одна функция ожидает одно, а другая ей дает другое.

Возьмем это.

- Есть заказ.
- Пока заказ не помечен, как отправленный, можно уменьшать и увеличивать сумму заказа
- Величина, на которую можно увеличить сумму заказа, зависит от:
  - Страны, в которой сделан заказ по конфигурируемому умолчанию
    - для Германии:
      - уменьшить можно на max(10% от оригинальной суммы заказа, 10 евро)
      - увеличить можно на max(10% от оригинальной суммы заказа, 10 евро)
  - Магазина, в котором сделан заказ
    - если конфигурации для магазина нет, берутся умолчания для страны магазина
  - если заказ помечен, как pre-paid, увеличивать сумму нельзя, уменьшать можно
    - уже есть тикет, который позволяет увеличивать сумму заказа для pre-paid заказов в зависимости от конфигурации магазина
M>


Рассматриваю для простоты только первые две строчки.

Псевдокод:

  type Order is
    ID: Integer;
    Sum: Money;
    Sent: Boolean;
  end;

  type SentOrder is Order where Sent = true;
  type UnsentOrder is Order where Sent = false;

  function SendOrder(order: UnsentOrder): SentOrder is
    ...
  end;

  function ChangeOrderSum(order: UnsentOrder, newSum: Money): UnsentOrder is    // эту функцию нельзя вызвать для заказа, у которого Sent равно true!
    ...
  end;

  function PrintOrderDetails(order: SentOrder) is
    ...
  end;

  var order := CreateOrder(1, 12000.00);
  order := ChangeOrderSum(order, 2800.23);
  order := SendOrder(order);
//  order := ChangeOrderSum(order, 5555.00);    // error
  PrintOrderDetails(order);    // OK


Таким образом, тут мы "закодировали в типах" требование "Пока заказ не помечен, как отправленный, можно уменьшать и увеличивать сумму заказа". Компилятор запрещает нарушать его.

Могли ли мы ошибиться при реализации этого требования? Могли. Но все равно существует вероятность обнаружения ошибки компилятором, есть шанс, что где-то какие-то типы "не срастутся". В данном случае, чтобы компилятор ничего не заподозрил, нам пришлось бы ошибиться сразу в двух сигнатурах функций (в SendOrder+ChangeOrderSum, или в ChangeOrderSum+PrintOrderDetails). Но, конечно, какая-то "отладка типов" тоже нужна, да.
Re[16]: Haskell нужен! (в Standard Chartered Bank)
От: Klapaucius  
Дата: 04.02.15 12:35
Оценка:
Здравствуйте, Mamut, Вы писали:

M>>>Сколько условий «Непроверенный как написано в энном пункте» предлагаешь впихивать в тип?

K>>Для начала в конструктор значения этого типа.
K>>Проектируя тип "Заказ", Вы просто описываете вашу спецификацию как конструкторы. Если в спецификации противоречия, вы даже можете их на этом этапе обнаружить. А потом тайпчекер уже заботится о том, что вы все проверяете по спецификации.

M>Можно больше конкретики? Я согласен на примеры кода.


Вы же свой пример описали словами, а не в коде. Чем вас точно такой же ответ не устраивает? Я и сам согласен на пример кода.
Напишите нетипизированную программу, приведите тестовые данные, тогда можно будет написать типизированную версию в качестве ответа.

M>Это не ответ на мой вопрос


Т.е. ваш вопрос был о том, что будет если вы "забудете про 4-й пункт" на этапе спецификации?
Речь шла о том, чем лучше логика "закодированная в типах", чем не закодированная. Это показано. Теперь вдруг оказалось, вы спрашиваете, чем поможет система типов если вы что-то не "закодируете". Может и ни чем не помочь, но на практике помогает.
Представим, что ни программист особых усилий не прилагает чтоб что-то там кодировать, ни система типов особо не блещет возможностями.
Тем не менее, как подметил Пирс, даже если попытаться доказать что-то довольно простое в программе с ошибкой часто что-нибудь "не срастется", типы не совпадают. Это и называется "легковесной верификацией". Даже если тип совсем простая теорема, которая учитывает очень малую часть реальной спецификации терма, этот тип населяющего, попытавшись доказать теорему мы зачастую что-нибудь да найдем если код с ошибкой. Т.е. даже если программист ленится использовать типы — что-то тайпчек все равно отловит, хотя и меньше, чем если бы программист постарался переложить больше работы на тайпчек. Чтоб испортить этот бесплатный праздник нужно уже какие-то усилия по обходу/затиранию типов прилагать, строково-ориентированным программированием, всякими кастами, реинтерпретацией памяти, рефлексией и другими веселыми вещами. Т.е. программист уже должен быть не просто ленивым, а активно злонамеренным.
'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[7]: Haskell нужен! (в Standard Chartered Bank)
От: a_g_99 США http://www.hooli.xyz/
Дата: 04.02.15 13:04
Оценка: :)
Здравствуйте, jazzer, Вы писали:

J>Смотря для чего. Чтоб за один день въехать и накропать что-то как-то работающее — безусловно. Чтоб писать хороший код, надо становиться профессионалом, что гораздо дольше, и в этом смысле двухнедельный выигрыш при въезжании в язык с нуля роли не играет, имхо. Гораздо важнее изучить и прочувствовать идиомы, перестроить образ мыслей — это все занимает время (у некоторых бесконечное — они так и продолжают писать в высокоуровневых языках как на бейсике).

Что есть "хороший код" по вашему? И почему pyhton-исты производят говнокод а хаскеллисты хороший? Из ваших слов получается потому что хаскелл сложнее out-of-box, он имеет идиомы "которые надо прочуствовать". Т.е in fact ЯП задает тон разработки инженеру, а если инженер уклоняется от этого пути, начинается борьба с ЯП.
Я же уверен что все должно быть наоборот. Настоящий профессионал-инженер использует ЯП только как tools чтобы решить задачу. Сам формирует лучшие идиомы для решения, реализует нужные абстракции и тд. И принцип KISS в такой парадигме будет просто идеален. Поэтому такие простые на первый взгляд языки как javascript и python завоевывют мир, а хаскелл продолжает болтаться где-то в подбрюшье банков категории Б

J>Ну вот утверждается, что в ФП все это проще и в каком-то виде из коробки.

Это не соответствует реальности. Хаскеллу 25 лет, его популярность где-то ~ 0. Если бы это было так просто из коробки все бы пилили на хаскелле, а не на пайтоне или плюсах.

J>Вот утверждают, что все ровно наооброт. Что ФП-код гораздо более ясный и прозрачный и гораздо лучше работает в стиле "собрать из крипичиков, и чтоб не макароны получились".

Так не надо собирать из кирпичиков. В этом-то и главная беда хаскеллов. У него кирпичики имеют геометрическую форму амплитуэдра. И когда из этих кирпичиков надо построить что-то простое и обычное, получается полный трэш. Хороший инженер всегда должен использовать принцип play-doh вместо этого.

J>В смысле набора программеров? Ну да, хаскеллистов меньше, чем джавистов, тут спору нет.

В смысле, что для поддержки проекта или системы нужно в первую очередь хорошо представлять граф связей модулей в проекте. А так как в ФП и хаскеле он крайне не очевиден, то разрастающиеся системы становятся адом. Новичку сильно проще увеличить количество говнокода дописав что-то свое вместо использования уже написанного.
Re[15]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 04.02.15 13:08
Оценка:
M>>Легко предание, да не верится вообще нифига.
K>Во что не верится-то? В каком месте тут вера нужна?

Во все то, что ты тут так радостно описываешь. Заметь: ты ограничиваешь ся строго заявлениями уровня «все будет хорошо»

M>>Сколько условий «Непроверенный как написано в энном пункте» предлагаешь впихивать в тип?

K>Столько сколько нужно.

/o\

В итоге у тебя будет развесистый тип, включающий в себя стопятьсот условий. Прекрасно. Чем это лучше любого другого решения? Как ты будешь проверять, что эти стопятьсот условий у тебя реализованы правильно?

M>>Каким образом это поможет мне не забыть вписать в тип ошибку «Непроверенный как написано в энном пункте»? <- вот это и есть логическая ошибка, от которой все сказки про типы не спасут


K>Во первых мы уже улучшили ситуацию переходом, от того как мне не забыть проверить что-то в 1024-х местах


Откуда взялись 1024 мест, известно только тебе

K>к тому, как мне не забыть включить что-то в тип (что то же самое, что не забыть включить в спецификацию, что вы естественно тоже можете забыть).

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

Опять сказки про белого бычка.

M>>Ты перенес все эти четыре десятка условий в типы, молодца. Как ты собираешься проверять, что ты эти условия описал в типах логически правильно?

K>См. выше.

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

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


M>>Это я еще умолчал о разных веселых сайд-эффектах:

M>>- отсылать e-mail'ы разных типов при разных действиях
M>>- в зависимости от конфигураций и того, что выбрал пользователь, отсылать бумажные письма
M>>- дергать payments/dunning/bookkeeping за разные части в разные моменты
M>>- вызывать risk check и производить разные действия в зависимости от результата. Не на всех этапах, естественно
M>>- обрабатывать случаи нестандартной интеграции (в основном, для очень больших магазинов)
M>>и еще вагон и маленькая тележка

K>И? Тем больше пользы будет от стат. проверки типов.


Каким образом? Каким образом стат. проверка типов поможет отослать правильное письмо, сделать правильный risk check и т.п., да еще в нужные моменты времени?


dmitriid.comGitHubLinkedIn
Re[17]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 04.02.15 13:08
Оценка:
M>>Можно больше конкретики? Я согласен на примеры кода.
K>Вы же свой пример описали словами, а не в коде. Чем вас точно такой же ответ не устраивает? Я и сам согласен на пример кода.
K>Напишите нетипизированную программу, приведите тестовые данные, тогда можно будет написать типизированную версию в качестве ответа.

Я привел простейший пример тут: http://rsdn.ru/forum/philosophy/5943418
Автор: Mamut
Дата: 04.02.15


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

update_order(Order, Amount) ->
  Estore         = order_api:get_estore(Order),
  Country        = estore_api:get_country(Estore),
  OriginalAmount = order_api:get_original_amount(Order),
  AmountDiff     = OriginalAmount - Amount,

  case AmountDiff > 0 andalso order_api:is_prepaid(Order) of
    true -> {error, amount};
    false -> 
        %% config сам определяет, что брать — конфиг магазина или страны,
        %% если 
        MaxIncreasePct = config:get_max_config_increase_pct(Estore),
        MaxIncrease    = config:get_max_config_increase(Estore),
        MaxDecreasePct = config:get_max_config_decrease_pct(Estore),
        MaxDecrease    = config:get_max_config_decrease(Estore),

        case (AmountDiff < 0 andalso abs(AmountDiff) > max(....)).... of
           true -> {error, amount};
           false -> 
             UpdatedOrder = order_api:set_amount(Order, Amount),
             order_ops:write(UpdatedOrder)
        end
  end.


Я так предполагаю, ответом на это будет «а, ну тут просто, просто пиши инициализаторы, все будет зашибись, верь»

M>>Это не ответ на мой вопрос

K>Т.е. ваш вопрос был о том, что будет если вы "забудете про 4-й пункт" на этапе спецификации?

Вопрос был. Как ты не смог его понять — это выше моего понимания.

K>Речь шла о том, чем лучше логика "закодированная в типах", чем не закодированная. Это показано.


Да не показано нигде. Идут сплошные сказки и рассказы про то, как все будет зашибись, и я в это должен верить Как магическая система типов все сома отловит, как невозможно будет написать неправильный код и т.п.

Извини, не верю. Учитывая, что ты даже не смог ни разу ответить на простой вопрос «как это все будет проверяться и дебажиться».

K>Теперь вдруг оказалось, вы спрашиваете, чем поможет система типов если вы что-то не "закодируете". Может и ни чем не помочь, но на практике помогает.


Пока что вся «практика» упирается в рассказы и сказки о том, как все хорошо. Внятно на мои вопросы пока ответило ровно два человека — genre
Автор: genre
Дата: 04.02.15
и Evgeny.Panasyuk
Автор: Evgeny.Panasyuk
Дата: 03.02.15
. lazy-cjow-rhrrпопытался
Автор: lazy-cjow-rhrr
Дата: 03.02.15
, но его ответ даже близко не был связан с моими вопросами


[много текста про то, как магический тайпчек все отловит, невзирая на леность программиста, просто поскипано]


dmitriid.comGitHubLinkedIn
Re[19]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 04.02.15 13:14
Оценка:
ARK>Рассматриваю для простоты только первые две строчки.

Действительно. Дальше же работа с конфигурацией идет, там уже все не так весело


ARK>Псевдокод:


ARK>
ARK>  type SentOrder is Order where Sent = true;
ARK>  type UnsentOrder is Order where Sent = false;

ARK>  function ChangeOrderSum(order: UnsentOrder, newSum: Money): UnsentOrder is    // эту функцию нельзя вызвать для заказа, у которого Sent равно true!
ARK>    ...
ARK>  end;


Чем это принципиально отличается от API с такими же условиями?


ARK>  var order := CreateOrder(1, 12000.00);
ARK>  order := ChangeOrderSum(order, 2800.23);
ARK>  order := SendOrder(order);
ARK>//  order := ChangeOrderSum(order, 5555.00);    // error
ARK>  PrintOrderDetails(order);    // OK

ARK>


Да-да. Error. Потому что в реальности все так и происходит, ага-ага

В реальности происходит так:
Order = read_order_from_db(OrderId),
change_order_amount(Order, Amount).


Ой. Внезапно на этапе компиляции мы даже не знаем, будет заказ отправелнным или неотправленным. Какая печаль!


ARK>Таким образом, тут мы "закодировали в типах" требование "Пока заказ не помечен, как отправленный, можно уменьшать и увеличивать сумму заказа". Компилятор запрещает нарушать его.


Вау. Ага. Так как мне поможет компилятор в тех двух строчках кода, что я привел?

ARK>Могли ли мы ошибиться при реализации этого требования? Могли.


Я об этом (среди прочего) и говорю уже пятнадцать сообщения подряд Нет, «магический тайпчекер все проверит»

ARK>Но все равно существует вероятность обнаружения ошибки компилятором, есть шанс, что где-то какие-то типы "не срастутся".


«Есть вероятность», «есть шанс»... Это не аргументы — это вера.

ARK>В данном случае, чтобы компилятор ничего не заподозрил, нам пришлось бы ошибиться сразу в двух сигнатурах функций (в SendOrder+ChangeOrderSum, или в ChangeOrderSum+PrintOrderDetails). Но, конечно, какая-то "отладка типов" тоже нужна, да.


Какая? Я уже пятнадцать раз спросил, как, реализовав логику на типах, люди собираются ее проверять?


dmitriid.comGitHubLinkedIn
Отредактировано 04.02.2015 13:40 Mamut [ищите в других сетях] . Предыдущая версия .
Re[20]: Haskell нужен! (в Standard Chartered Bank)
От: AlexRK  
Дата: 04.02.15 13:56
Оценка:
Здравствуйте, Mamut, Вы писали:

ARK>>Рассматриваю для простоты только первые две строчки.

M>Действительно. Дальше же работа с конфигурацией идет, там уже все не так весело

Я просто для иллюстрации. Понятно, что сложность/возможность задать проверки с помощью типов напрямую зависят от возможностей конкретного ЯП и геморройности предметной области.

M>Чем это принципиально отличается от API с такими же условиями?


Если в широком смысле — то ничем, дополнительные ограничения на типы это просто полезные возможности ЯП для создания более грамотного API.

Мой пример тривиален, но можно ведь придумать типы и поинтереснее, например "where Sent=true and (Sum>100 or Sum<200)".

Или вас интересует не принцип "выноса логики в типы", а конкретные возможности какого-то конкретного ЯП?

M>В реальности происходит так:

M>
M>Order = read_order_from_db(OrderId),
M>checnge_order_amount(Order, Amount).
M>


M>Ой. Внезапно на этапе компиляции мы даже не знаем, будет заказ отправелнным или неотправленным. Какая печаль!


Внезапно в этом месте будет ошибка компиляции — ибо checnge_order_amount хочет не тот тип, который выдает read_order_from_db. Какая печаль.
Вот вам компилятор и отловил баг.

ARK>>Таким образом, тут мы "закодировали в типах" требование "Пока заказ не помечен, как отправленный, можно уменьшать и увеличивать сумму заказа". Компилятор запрещает нарушать его.

M>Вау. Ага. Так как мне поможет компилятор в тех двух строчках кода, что я привел?

Написал выше.

ARK>>Могли ли мы ошибиться при реализации этого требования? Могли.

M>Я об этом (среди прочего) и говорю уже пятнадцать сообщения подряд Нет, «магический тайпчекер все проверит»

Несоответствия обязательно проверит.

ARK>>Но все равно существует вероятность обнаружения ошибки компилятором, есть шанс, что где-то какие-то типы "не срастутся".

M>«Есть вероятность», «есть шанс»... Это не аргументы — это вера.

Вы же понимаете отличие статической типизации от динамической? Или там тоже вера?
Вот и здесь то же самое — что-то _дополнительно_ проверяется статически.

M>Какая? Я уже пятнадцать раз спросил, как, реализовав логику на типах, люди собираются ее проверять?


Если будут несоответствия в типах — вылетит ошибка компиляции. Но даже если все типы соответствуют друг другу, это не гарантирует корректность программы, надо тестировать, как обычно.
Но некоторая часть ошибок будет выловлена компилятором.
Re[21]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 04.02.15 14:12
Оценка:
M>>Чем это принципиально отличается от API с такими же условиями?

ARK>Если в широком смысле — то ничем, дополнительные ограничения на типы это просто полезные возможности ЯП для создания более грамотного API.

ARK>Мой пример тривиален, но можно ведь придумать типы и поинтереснее, например "where Sent=true and (Sum>100 or Sum<200)".

В языке с паттерн-матчингом это тривиально реализуется в API

ARK>Или вас интересует не принцип "выноса логики в типы", а конкретные возможности какого-то конкретного ЯП?


Меня интересует именно принцип. Тут мне рассказывают, насколько он мегакрут, а на практике никто не может даже внтяно объяснить, чем он так мегакрут.

M>>В реальности происходит так:

M>>
M>>Order = read_order_from_db(OrderId),
M>>checnge_order_amount(Order, Amount).
M>>


M>>Ой. Внезапно на этапе компиляции мы даже не знаем, будет заказ отправелнным или неотправленным. Какая печаль!


ARK>Внезапно в этом месте будет ошибка компиляции — ибо checnge_order_amount хочет не тот тип, который выдает read_order_from_db. Какая печаль.

ARK>Вот вам компилятор и отловил баг.

Ээээ. С какого перепуга? Или у тебя заказы не в базе данных лежат?

M>>Вау. Ага. Так как мне поможет компилятор в тех двух строчках кода, что я привел?

ARK>Написал выше.

Ерунду написал

ARK>>>Могли ли мы ошибиться при реализации этого требования? Могли.

M>>Я об этом (среди прочего) и говорю уже пятнадцать сообщения подряд Нет, «магический тайпчекер все проверит»
ARK>Несоответствия обязательно проверит.

Несоответсвия чего чему?


M>>Какая? Я уже пятнадцать раз спросил, как, реализовав логику на типах, люди собираются ее проверять?

ARK>Если будут несоответствия в типах — вылетит ошибка компиляции. Но даже если все типы соответствуют друг другу, это не гарантирует корректность программы, надо тестировать, как обычно.

Именно

ARK>Но некоторая часть ошибок будет выловлена компилятором.


Могу только повторить: ошибки типизации — это очень маленькая часть от общего количества ошибок.


dmitriid.comGitHubLinkedIn
Re[22]: Haskell нужен! (в Standard Chartered Bank)
От: AlexRK  
Дата: 04.02.15 14:32
Оценка:
Здравствуйте, Mamut, Вы писали:

ARK>>Или вас интересует не принцип "выноса логики в типы", а конкретные возможности какого-то конкретного ЯП?

M>Меня интересует именно принцип. Тут мне рассказывают, насколько он мегакрут, а на практике никто не может даже внтяно объяснить, чем он так мегакрут.

Я как раз принцип и пытаюсь объяснить, но что-то пока безуспешно.

M>>>В реальности происходит так:

M>>>
M>>>Order = read_order_from_db(OrderId),
M>>>checnge_order_amount(Order, Amount).
M>>>


M>>>Ой. Внезапно на этапе компиляции мы даже не знаем, будет заказ отправелнным или неотправленным. Какая печаль!


ARK>>Внезапно в этом месте будет ошибка компиляции — ибо checnge_order_amount хочет не тот тип, который выдает read_order_from_db. Какая печаль.

ARK>>Вот вам компилятор и отловил баг.

M>Ээээ. С какого перепуга? Или у тебя заказы не в базе данных лежат?


А вот с такого. read_order_from_db возвращает заказ — Order. А какой он, отосланный или нет — мы при компиляции действительно не знаем. Значит, передавать этот Order без проверки в change_order_amount не имеем права — это и есть ошибка в логике.
Согласно спецификации, change_order_amount обязан оперировать не с любыми Order, а только с их подмножеством, у которых Sent=true. Мы записываем в сигнатуру change_order_amount тип "UnsentOrder" (вместо любого Order) — и получаем в каких-то местах ошибку компиляции (вот в этих двух строчках в частности).
И это правильно, потому что этот код некорректен. Чтобы он стал корректным, добавляем:
  Order = read_order_from_db(OrderId);
  if (Order.Sent = true)
    change_order_amount(Order, Amount);

и внезапно код теперь компилируется.

M>>>Вау. Ага. Так как мне поможет компилятор в тех двух строчках кода, что я привел?

ARK>>Написал выше.
M>Ерунду написал

Да нет, просто вы не понимаете, о чем речь (ну или спорите из принципа).

ARK>>>>Могли ли мы ошибиться при реализации этого требования? Могли.

M>>>Я об этом (среди прочего) и говорю уже пятнадцать сообщения подряд Нет, «магический тайпчекер все проверит»
ARK>>Несоответствия обязательно проверит.

M>Несоответсвия чего чему?


Одних типов другим типам (как в примере с заказами).

ARK>>Но некоторая часть ошибок будет выловлена компилятором.

M>Могу только повторить: ошибки типизации — это очень маленькая часть от общего количества ошибок.

У нас тут речь как раз о том, чтобы закладывать в программу больше семантики на уровне типов, тогда и дополнительных проверок будет больше.
Но сколько это "больше" в процентном отношении — зависит от кучи факторов. Может оказаться и вовсе невыгодно работать со сложным языком, хоть и позволяющим больше проверок.
Re[22]: Haskell нужен! (в Standard Chartered Bank)
От: genre Россия  
Дата: 04.02.15 14:36
Оценка:
Здравствуйте, Mamut, Вы писали:

M>>>В реальности происходит так:

M>>>
M>>>Order = read_order_from_db(OrderId),
M>>>checnge_order_amount(Order, Amount).
M>>>


M>Ээээ. С какого перепуга? Или у тебя заказы не в базе данных лежат?


Если псевдокодом, то предполагается как-то так:
Order[New] order = read_order_from_db(OrderId)
Order[Processed] processedOrder= order.processSomehow()

при этом имея метод: 
void checnge_order_amount(Order[Processed] order, Amount amount) 

ну и: 
checnge_order_amount(order, Amount) << ошибка
checnge_order_amount(processedOrder, Amount) << ок
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[23]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 04.02.15 14:57
Оценка:
M>>Ээээ. С какого перепуга? Или у тебя заказы не в базе данных лежат?

ARK>А вот с такого. read_order_from_db возвращает заказ — Order. А какой он, отосланный или нет — мы при компиляции действительно не знаем. Значит, передавать этот Order без проверки в change_order_amount не имеем права — это и есть ошибка в логике.


Это не ошибка в логике. Мы передаем черный ящик в API, который знает, как с этим ящиком работать. Где ошибка логики — неизвестно
Ошибка логики может быть дальше — где собственно реализуется изменение суммы заказа. Ну так она, как мы выяснили, может быть и в типе и в API


ARK>Согласно спецификации, change_order_amount обязан оперировать не с любыми Order, а только с их подмножеством, у которых Sent=true. Мы записываем в сигнатуру change_order_amount тип "UnsentOrder" (вместо любого Order) — и получаем в каких-то местах ошибку компиляции (вот в этих двух строчках в частности).

ARK>И это правильно, потому что этот код некорректен. Чтобы он стал корректным, добавляем:
ARK>
ARK>  Order = read_order_from_db(OrderId);
ARK>  if (Order.Sent = true)
ARK>    change_order_amount(Order, Amount);
ARK>

ARK>и внезапно код теперь компилируется.

Во-первых, стоп. Выше ничего не изменилось. Тип Order как был, так и остался Order. Или оборачивание его в if(Order.Sent = true) автоматом превратило его в SentOrder?

Во-вторых, ровно ноль разницы с API, которое имеет под капотом if Order.Sent/if Order.NotSent Более того, даже объемы тестов будут одинаковые. Причем при росте количества условий сам тип будет становиться мешаниной, а вызовы чего бы то ни было с этим типом будут ничем не отличаться от вызвовов без типов

ARK>Да нет, просто вы не понимаете, о чем речь (ну или спорите из принципа).


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

ARK>>>Но некоторая часть ошибок будет выловлена компилятором.

M>>Могу только повторить: ошибки типизации — это очень маленькая часть от общего количества ошибок.

ARK>У нас тут речь как раз о том, чтобы закладывать в программу больше семантики на уровне типов, тогда и дополнительных проверок будет больше.


Пока что я не убежден, от слова совсем. Разницы с API пока что ноль. Плюс проверки размазываются по всему коду: в одном месте что-то форсируется на уровне типов, в другом месте надо написать сто if'ов, чтобы впихнуть полученный объект в вызов.

ARK>Но сколько это "больше" в процентном отношении — зависит от кучи факторов. Может оказаться и вовсе невыгодно работать со сложным языком, хоть и позволяющим больше проверок.


Во-во


dmitriid.comGitHubLinkedIn
Re[23]: Haskell нужен! (в Standard Chartered Bank)
От: Mamut Швеция http://dmitriid.com
Дата: 04.02.15 15:00
Оценка:
M>>Ээээ. С какого перепуга? Или у тебя заказы не в базе данных лежат?

G>Если псевдокодом, то предполагается как-то так:

G>
G>Order[New] order = read_order_from_db(OrderId)
G>Order[Processed] processedOrder= order.processSomehow()

G>при этом имея метод: 
G>void checnge_order_amount(Order[Processed] order, Amount amount) 

G>ну и: 
G>checnge_order_amount(order, Amount) << ошибка
G>checnge_order_amount(processedOrder, Amount) << ок

G>


Вот откуда берется это детское восприятие, что все эти вызовы идут последовательно?

1 января. Создали заказ.
5 января. Отослали часть заказа или весь заказ.
13 января. Изменили заказ (добавили новые товары или скидку или оформили return) с изменением суммы заказа.

13 января заказ уже имеет статус «обработан и отослан». Нет никакого read -> process -> change_amount. Есть read -> change_amount.


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

M>Вот откуда берется это детское восприятие, что все эти вызовы идут последовательно?


M>1 января. Создали заказ.

M>5 января. Отослали часть заказа или весь заказ.
M>13 января. Изменили заказ (добавили новые товары или скидку или оформили return) с изменением суммы заказа.

M>13 января заказ уже имеет статус «обработан и отослан». Нет никакого read -> process -> change_amount. Есть read -> change_amount.


Я тебе идею иллюстрирую, а ты к конкретной строчке докопался .
Ну пускай у тебя read_order_from_db(OrderId) возвращает Order
  • уже в правильном состоянии.
    ... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
  • Re[25]: Haskell нужен! (в Standard Chartered Bank)
    От: Mamut Швеция http://dmitriid.com
    Дата: 04.02.15 15:23
    Оценка:
    M>>13 января заказ уже имеет статус «обработан и отослан». Нет никакого read -> process -> change_amount. Есть read -> change_amount.

    G>Я тебе идею иллюстрирую, а ты к конкретной строчке докопался .


    Я понимаю, что идея. Только уже сколько раз эти идеи мало соотносятся с реальностью И твой пример, и первый пример AlexRK строго зависит от того, чтобы все изменения шли друг за другом, чтобы компилятор видел, что происходит в каждой точке с этим объектом

    G>Ну пускай у тебя read_order_from_db(OrderId) возвращает Order[*] уже в правильном состоянии.


    То есть ты предлагешь стандартное

    Order = read_order_from_db(OrderId),
    update_order_amount(Order, Amount)


    то есть на этапе компиляции уже никакой проверки не будет? Зачем тогда весь сыр-бор?


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

    M>Я понимаю, что идея. Только уже сколько раз эти идеи мало соотносятся с реальностью И твой пример, и первый пример AlexRK строго зависит от того, чтобы все изменения шли друг за другом, чтобы компилятор видел, что происходит в каждой точке с этим объектом


    Нет, с чего ты взял? Тебе ничего не мешает инстанцировать сразу объект в нужном состоянии.

    M>То есть ты предлагешь стандартное


    M>
    M>Order = read_order_from_db(OrderId),
    M>update_order_amount(Order, Amount)
    M>


    M>то есть на этапе компиляции уже никакой проверки не будет? Зачем тогда весь сыр-бор?


    Нет.

    public Order[*] read_order_from_db(OrderId id); // читаем уже правильный ордер, либо Order[New] либо Order[Processed]
    ... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
    Re[27]: Haskell нужен! (в Standard Chartered Bank)
    От: genre Россия  
    Дата: 04.02.15 15:31
    Оценка:
    Здравствуйте, genre, Вы писали:

    G>
    G>public Order[*] read_order_from_db(OrderId id); // читаем уже правильный ордер, либо Order[New] либо Order[Processed]
    G>


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

    Но все тоже возможно.
    ... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
    Re[27]: Haskell нужен! (в Standard Chartered Bank)
    От: Mamut Швеция http://dmitriid.com
    Дата: 04.02.15 15:33
    Оценка:
    M>>то есть на этапе компиляции уже никакой проверки не будет? Зачем тогда весь сыр-бор?

    G>Нет.


    G>
    G>public Order[*] read_order_from_db(OrderId id); // читаем уже правильный ордер, либо Order[New] либо Order[Processed]
    G>


    И? Дальше что?


    public Order[*] read_order_from_db(OrderId id);
    
    // каким образом тут компилятор проверит, что за Order сюда идет?
    // заказ же из базы читается
    change_order_amount(Order, Amount)


    dmitriid.comGitHubLinkedIn
    Re[22]: Haskell нужен! (в Standard Chartered Bank)
    От: D. Mon Великобритания http://thedeemon.livejournal.com
    Дата: 04.02.15 15:38
    Оценка:
    Здравствуйте, Mamut, Вы писали:

    M>Ну вот вам простейший пример простейшей логики:


    M>
    M>- Есть заказ.
    M>- Пока заказ не помечен, как отправленный, можно уменьшать и увеличивать сумму заказа
      ...
    M>  - если заказ помечен, как pre-paid, увеличивать сумму нельзя, уменьшать можно
    M>


    M>Просто же. Как можно больше логики можно на типах запрограммировать же.


    Да, просто, можно:
    data NewOrder -- основные качественные состояния заказа
    data PrepaidOrder 
    data SentOrder
    
    type Money = Int -- для простоты тут такие заглушки, реально Money описывает неотрицательные суммы в нужной валюте
    type OrderInfo = [String] 
    
    data Order state = Ordr Money OrderInfo -- тип заказа параметризован состоянием
    
    class CanChangeAmount a  -- в каких состояниях можно менять сумму
    instance CanChangeAmount NewOrder
    instance CanChangeAmount PrepaidOrder
    
    --уменьшить сумму. работает лишь для нужных состояний, сохраняет состояние
    decreaseAmount :: (CanChangeAmount state) => Order state -> Money -> Order state
    decreaseAmount (Ordr amt info) delta = Ordr (amt - delta) info
    
    --увеличить сумму. работает лишь для того состояния, где это разрешено
    increaseAmount :: Order NewOrder -> Money -> Order NewOrder
    increaseAmount (Ordr amt info) delta = Ordr (amt + delta) info


    В итоге компилятор хотя бы эту логику проверит статически и не даст менять сумму когда не положено. Остальные правила проще описать данными.
    Re[23]: Haskell нужен! (в Standard Chartered Bank)
    От: Mamut Швеция http://dmitriid.com
    Дата: 04.02.15 15:40
    Оценка:
    DM>В итоге компилятор хотя бы эту логику проверит статически

    Каким образом, если заказ читается из базы данных? Подробнее в этом обсуждении: http://rsdn.ru/forum/philosophy/5943812
    Автор: AlexRK
    Дата: 04.02.15


    DM>и не даст менять сумму когда не положено. Остальные правила проще описать данными.


    Что значит «проще описать данными»?


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