Re[4]: Никогда не недооценивайте силу по умолчанию
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 03.10.22 14:37
Оценка: +1 :)
Здравствуйте, Sinclair, Вы писали:

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

N>>На чём будет написан рантайм Go, реализующий шедулер горутин, и почему этот язык не является нормальным?
S>Ну это же классический вопрос, с не менее классическим ответом.

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

S>Вопрос как раз в том, как мы ограничиваем возможности программиста выстрелить себе в ногу.


Да. Но когда я писал
Автор: netch80
Дата: 10.04.12
то же самое, реакции были неоднозначными

S>Так что не очень важно, на каком языке будет написан рантайм. Он не будет нормальным ровно потому, что в нём вынужденно будет реализована возможность выстрелить себе в ногу, и её предотвращение будет ответственностью программиста, а не компилятора.


Разница будет в том, насколько легко контролировать запреты.
Если язык в принципе не может запортить структуры памяти, как в Java без спец. модулей для этого, а использование таких модулей легко ловится — это одно. Или как когда все опасные места ограждены словом unsafe, и на него можно тупо выгрепать.
Если нет — это другое. Для C++ такое ловить сложнее на порядки.
The God is real, unless declared integer.
Re[7]: Никогда не недооценивайте силу по умолчанию
От: maxkar  
Дата: 16.10.22 12:28
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Может, конечно, это мои личные загоны, но на парсинг второго текста мне нужно на порядок меньше времени.

Согласен. Второй текст гораздо проще читается.

M>>Вот так мог бы выглядеть современный язык программирования.

SD>Это единственное утверждение, с которым я позволю себе не согласиться.
SD>Если же оставаться в рамках существующих ограничений (программа — текст, читаем сверху вниз, слева направо), то я бы скорее видел это как машину состояний в примерно таком стиле:
Спасибо, интересное наблюдение. Особенно с учетом того, что я специально хотел отойти от явного описания конечных автоматов . Но дело здесь скорее в том, что я пытаюсь решать немного другую задачу. В первую очередь — избежать комбинаторного взрыва состояний там, где он возможен. В рамках вашего примера это
process(Request) ->
    ask_for_payment_data(Request),
    ask_for_inventory_data(Request),
    multi_receive
        {Payment, Inventory} -> intersect(Payment, Inventory).

Смотрите. Есть "пассивные" состояния, в которых мы ждем какого-либо внешнего события (например, initial в вашем примере). Вот описание этих состояний и переходов между ними прекрасно ложится в машину состояний. Или, например, в UML State Diagram. А еще есть "активные" состояния (вроде process), где система что-то делает внутри. Переход в другие состояния обычно "внутренний", не на основании пользовательских запросов. Вот как раз process в этом сценарии показателен. Его можно представить в виде конечного автомата. Это будет суперпозиция состояний для payment и inventory. Т.е. (payment_complete, inventory_in_progress). В этом случае будут классические реации на "внешние" события типа "payment complete". Но количество явных состояний растет очень быстро. А еще ведь можно добавить внешние запросы. Например, пришел cancel и по завершению текущей активности нужно бы его обработать. И тогда у нас уже состояние вроде (payment_in_progress, inventory_failed, cancel_requested). Когда payment будет завершен, его нужно будет откатить. Получается, что для описания активностей (activity diagram в UML) нужны немного другие средства, чем для описания высокоуровневых состояний.

Далее. У вас есть multi_receive. Проблема в том, что это может быть несколько непрактично. Например, у платежа (в рамках payment) может быть много разных состояний. Например, new -> authenticating -> (optional) SCA -> successful/failed. Состояния предоставляются API. Для какой-нибудь службы поддержки видимость состояния SCA (strong customer authentication) может быть полезена. Отсюда у меня в примере и берется wait. Т.е. вместо receive(Payment) получается wait payment to { p => hasDecision(p) }. Мы ждем не первого события, а некоторого состояния, в котором находится платеж (наблюдение состояний реализуется через те же события). Чем еще хорошо ожидание — оно развязывает нас от потенциальных гонок с событиями. Вдруг соответствующее событие уже произошло? В случае wait семантика не меняется. Да и на REST это ложится — можно запросить состояние в момент wait и проверить предикат.

В целом, в рамках process мы делаем примерно одно и то же. Ну, с точностью до терминов. А так — я согласен, что на уровне выше машина состояний как раз была бы полезна. У меня как раз не было продумано, что вообще делать со внешними запросами к системе. Я фокусировался на activity. С состояниями вроде бы получается хорошо. В "пассивных" состояниях у нас описыавется логика реакции на запросы ("события"). В активных — описывается логика поведения но не запросов (внешние запросы слишком усложняют обработку). Зато запросы можно сохранять (HTTP 202 Accepted) до того момента, когда система перейдет в "пассивное" состояние и там уже запрос можно будет обработать. Переходы и события между состояниями не обязательно описывать текстом. Вот как раз state diagram отлично подходит. Стрелочки с guard — раз реакция на внешние события. И для определенных состояний может уже задаваться логика поведения. Это может быть "последовательный" код. Может быть — activity diagram (но в сложных случаях код все-таки оказывается проще для восприятия). Может даже вложенная state diagram.

SD>Возможно, пример не самый удачный, но зато чуть менее concept art, и, пожалуй, при некотором упорстве реализуемый на Haskell'е.

Пример хороший. Он вполне позволяет иллюстрировать те проблемы, которые я пытался решить.

SD>Но проблемы все те же, о которых вы написали — отсутствие условной транзакционности (более того, я вообще не представляю, как делать транзакционность в распределенных системах, даже на уровне банальных key-value storage — до сих пор прикидываю, есть ли какой-то способ надежно положить bidirectional map в этот самый key-value storage).

А вот в key-value транзакционность может внезапно оказаться сложнее, чем для реальных задач. Практически вся интеграция имеет понятие aggregation root. Например, в рамках платежной подсистемы у нас на верхнем уровне будет payment, к нему все будет привязано. В рамках checkout — будет order, включающий cсылки на payment. В этом случае в рамках системы можно денормализованно хранить весь объект. На границах между системами у нас будет двухфазный коммит и/или do+undo. Каждая интеграция со внешней системой в случае завершения операции будет возвращать handler вида:
trait TxHandler {
  def onCommit(): Unit;
  def onRollback(): Unit;
}

В условиях идемпотентности вызова и commit/rollback (rollback может быть undo) мы можем сохранять все состояния вычисления (в том числе TxHandlers) и повторять/вызывать их при необходимости. Поэтому у нас будет Atomic (мы либо все докатим, либо откатим). И будет eventual consistency. Совершенно не будет изоляции (видны все эти переходы). И будет durability.

Что же касается bidi map — да, сложно. В том числе — из-за семантики. Что делать в случае дублирования? С другой стороны, это можно реализовать в тех же механизмах, что и rest. Допустим, у нас прямое отображение считается главным. Тогда reverse map — подчиненное. Когда мы пишем "key->value", нужно предусмотреть механизмы для "надежной доставки". Например, "reverse updated". Тогда будет двухфазный коммит. Мы записали "key:value,revDelivered:false,v:3". Затем обновили "value:key,keyVersion:3" и записали "key:value,revDelivered:false,v:3". Для конкретных хранилищ может быть что-нибудь попроще. Например, в mongo можно подписаться на changesteram и отслеживать изменения "пачками".

SD>На самом деле, думаю, если ваши наблюдения оформить в виде чуть более упорядоченного текста, получился бы неплохой блог-пост (если, конечно, такие вещи вас интересуют).

Спасибо! Блоги немного интересуют. Но кто их читать будет? Поэтому я уж лучше здесь, в рамках темы.
Re[3]: Никогда не недооценивайте силу по умолчанию
От: ути-пути Россия  
Дата: 17.10.22 19:49
Оценка:
Здравствуйте, netch80, Вы писали:

N>Во всём коде функции используется kptm2, в одном месте kmpt2. Удачи в поиске. Особенно в конце спринта с рычащим тимлидом.


Тебе, скорее всего, это еще IDE подсветит. Или, позже, транслятор ругнется предупреждением. А если имя не такое страшное, как ты привел, то, скорее всего, длинное, и набирать ты его не будешь, а выберешь из комплита.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[8]: Никогда не недооценивайте силу по умолчанию
От: SkyDance Земля  
Дата: 18.10.22 03:19
Оценка:
M>Спасибо, интересное наблюдение. Особенно с учетом того, что я специально хотел отойти от явного описания конечных автоматов . Но дело здесь скорее в том, что я пытаюсь решать немного другую задачу. В первую очередь — избежать комбинаторного взрыва состояний там, где он возможен.

Ага, теперь понимаю. Да, тоже с этой проблемой мучаюсь. Теоретически можно разделить state и attributes (те, которые есть во всех состояниях). Тем самым потенциально можно слепить три состояния "жду ответа от payments и inventory", "жду ответа от payments", "жду ответа от inventory" в одно состояние "жду ответов" (и атрибутами задать, что именно за ответов жду).

M>Далее. У вас есть multi_receive. Проблема в том, что это может быть несколько непрактично. Например, у платежа (в рамках payment) может быть много разных состояний. Например, new -> authenticating -> (optional) SCA -> successful/failed.


В таком варианте это уже два конечных автомата (что, кстати, великолепно ложиться на систему акторов). Хорошо если взаимодействие между ними тривиально. Но если нет, то взрывной рост сложности, увы, гарантирован.

M> В "пассивных" состояниях у нас описыавется логика реакции на запросы ("события"). В активных — описывается логика поведения но не запросов (внешние запросы слишком усложняют обработку).


Хм, я перечитал примеры исходного кода в вашем предыдущем сообщении, но не смог разглядеть это за синтаксисом. Более того, некоторые вещи в синтаксисе мне кажутся слишком узкоспециализированными. Скажем, вот это:
notify paymentSvc of paymentRequest


Почему не "send paymentRequest to PaymentSvc"?

Аналогично, следует ли разделять запрос и ответ семантически? Т.е. что должен делать paymentSvc, тоже писать "notify <???> of paymentReply"? Или как-то иначе, типа "reply paymentReply to <???>"

M>А вот в key-value транзакционность может внезапно оказаться сложнее, чем для реальных задач. Практически вся интеграция имеет понятие aggregation root. Например, в рамках платежной подсистемы у нас на верхнем уровне будет payment, к нему все будет привязано. В рамках checkout — будет order, включающий cсылки на payment. В этом случае в рамках системы можно денормализованно хранить весь объект. На границах между системами у нас будет двухфазный коммит и/или do+undo.


Ага, это сравнительно свежий подход к задаче консенсуса (всякие вариации Merkle tree и blockchain, заодно решающая проблему верификации и восстановления). Но производительность такого решения может сделать бессмысленным само применение key-value, ибо реляционная БД окажется быстрее.

M> Например, в mongo можно подписаться на changesteram и отслеживать изменения "пачками".


Тоже классика распределенного консенсуса. Те самые learners, которые не участвуют в кворуме (не голосуют за принятие решения и не обеспечивают durability), но в курсе текущего состояния. Беда в том, что consistency таких решений уж очень eventual, и с гарантиями совсем беда. Я по этим граблям уже не первый год хожу
В конечном итоге мне очень-очень-очень хочется продвинуть идею заменить эти bidi maps на что-нибудь с лУчшими гарантиями. Что — пока не знаю. Хорошо бы выслушать кого-нибудь, кто уже аналогичную проблему решил.
Re[4]: Никогда не недооценивайте силу по умолчанию
От: Privalov  
Дата: 18.10.22 12:04
Оценка:
Здравствуйте, ути-пути, Вы писали:

УП>Тебе, скорее всего, это еще IDE подсветит. Или, позже, транслятор ругнется предупреждением. А если имя не такое страшное, как ты привел, то, скорее всего, длинное, и набирать ты его не будешь, а выберешь из комплита.


Конкретно в Фортране я не видел подсветок. Использовал Programmer's WorkBench, Fortran Power Station 1.0 и 4.0. Это объяснимо: в Фортране зарезервированных слов нет. И компилятор Фортрана когда-то распознавал только 6 символов имени. Потом это починили, но математики как писали FR3M2, так и пишут. При этом они потом такие имена свободно читают.
Re[5]: Никогда не недооценивайте силу по умолчанию
От: ути-пути Россия  
Дата: 18.10.22 12:17
Оценка:
Здравствуйте, Privalov, Вы писали:

P>Конкретно в Фортране я не видел подсветок. Использовал Programmer's WorkBench, Fortran Power Station 1.0 и 4.0. Это объяснимо: в Фортране зарезервированных слов нет. И компилятор Фортрана когда-то распознавал только 6 символов имени. Потом это починили, но математики как писали FR3M2, так и пишут. При этом они потом такие имена свободно читают.


Ну вот тут обещают подсветку, не знаю, насколько продвинутую, но тем не менее.
Но имхо, ужасное фортрановское имя было лишь примером, вряд ли тут многие на нем пишут.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[6]: Никогда не недооценивайте силу по умолчанию
От: Privalov  
Дата: 18.10.22 12:32
Оценка:
Здравствуйте, ути-пути, Вы писали:

УП>Ну вот тут обещают подсветку, не знаю, насколько продвинутую, но тем не менее.


Значит, анализируют так же, как компилятор. Я помню, раньше материала "как не надо писать на Фортране" было намного больше, чем "как надо писать на Фортране". Ну и может быть в последних стандартах что-то доработали. Я имел дело с версиями Фортрана до 90. Я даже свободный формат читаю с трудом.

УП>Но имхо, ужасное фортрановское имя было лишь примером, вряд ли тут многие на нем пишут.


Математики такие имена используют постоянно. И в них есть смысл. Но да, мне, как и netch80, приходилось возиться с путаницей в именах. Компилятор не предупреждал. Молча создавал переменную по умолчанию. Я когда про implicit none узнал, чуть танцы солнцепоклонников прямо перед компом не устроил. Правда, команда математиков (они все были дядьки заметно старше меня) на него забили. Видимо, привыкли к первой букве.
Re[9]: Никогда не недооценивайте силу по умолчанию
От: maxkar  
Дата: 23.10.22 10:26
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>
SD>notify paymentSvc of paymentRequest
SD>

SD>Почему не "send paymentRequest to PaymentSvc"?
А это потому, что я рассказываю модель вычисления с середины а не с самого начала . Если же начинать с самого начала, получается следующее.

Давайте для начала рассмотрим одну систему (сервис). У нее есть какое-то внутреннее состояние и есть какие-то представления этого состояния, который сервис может отдавать наружу. Запросы к сервису могут возвращать состояние, а могут приводить к изменению состояния. Дополнительное условие — любой "модифицирующий" запрос потенциально изменяет какое-то обозримое состояние. Не бывает так, чтобы что-то внутри изменилось, но наружу это ни в каком виде видно не было. Какой смысл тогда в запросах? При этом необязательно, чтобы всё состояние было доступно клиентам. Например, какие-то изменения могут быть доступны только через "системные" API (которые при этом используются в тестах). В целом всё API описывается доступными состояниями и правилами переходов: что может происходить "самопроизвольно" (по правилам системы) и что происходит по запросам клиентов.

Теперь добавим другие системы и посмотрим на характер запросов между ними. Можно сказать, что это либо запросы на чтение состояний (ничего не изменяющие), либо на изменение состояний. Других запросов вроде бы не нужно — зачем что-то посылать, если в результате ничего не изменится? Запросы у нас имеют не стандартную операционную форму "пожалуйста, сделай XXX" а "я считаю, что представление состояния сущности XXX должно быть YYY". Например, не "отменить платеж" (между системами заказа (order) и платежа (payments)) а "я считаю, что состояние платежа 123 должно быть Cancelled". Обмен идет в рамках состояний (целевых в запросе на изменение, "актуальных" в ответах). Такой протокол неплохо ложится на современные реалии с потенциальными гонками между клиентами. "Операционная" семантика все равно предполагает какие-то исходные ("операция применима") и целевые ("результат применения") состояния. Так зачем описывать переход, если можно описать цель? И предоставить другой системе разбираться о том, как же достичь целевого состояния в рамках её модели.

Выше мы свели всю модель к задаче достижения консенсуса в распределенной среде. У каждого актора (системы) есть свое представление о том, какой должна быть "сущность". Акторы общаются друг с другом с целью получения консенсуса. Обычно для каждого типа сущностей имеется "главная" (authoritative), которая является абсолютной истиной и дргуие пытаются согласовать свое внутреннее состояние с тем, что может или не может быть в этом главном хранилище. Например, "главное" состояние платежей будет в payments, которая обеспечивает интеграцию с провайдерами. Да, я свел все к сложной задаче. И я считаю, что нет смысла решать простую задачу, если можно решать сложную. Даже если 90% случаев — простые, остаются 10% сложных случаев. В большой корпоративной среде 10% систем/коммуникаций явно будет больше 0. Т.е. сложную задачу придется решать. И будет лучше, если команды тренированы и уже знают, с какой стороны подходить к проблеме.

В рамках данной модели "payments" — главное состояние. Клиент "просит" получить целевое состояние. (Да, глагол можно выбрать и получше).

SD>Аналогично, следует ли разделять запрос и ответ семантически? Т.е. что должен делать paymentSvc, тоже писать "notify <???> of paymentReply"? Или как-то иначе, типа "reply paymentReply to <???>"

Да, стоит. Есть ассиметрия в модели (главное/авторитетное состояние и остальные). И есть практическое соображение об отсутствии циклических зависимостей между сервисами. Если orders знает о payments, то payments не должен ничего знать об orders. Поэтому и каналы "асимметричны". Запросы у нас получаются точка-точка (point-to-point). А вот ответы — это publish-subscribe. В этом случае все клиенты payments могут подписаться на изменения в состоянии платежа и соответствующим образом корректировать свои дальнейшие ожидания.

В нашем примере обмен будет следующим:
Синхронный ответ вообще говоря не обязателен. Т.е. payments может ответить "да, принято" и потом order будет ждать нового состояния и сравнивать с тем, что он хотел. Но с практической точки зрения удобно определять конфликты сразу в момент запроса. Особенно — при создании новых сущностей. Есть и сценарии, когда нужно ответить синхронно потому, что где-то далеко ждет браузер. Например, проведение платежа может потребовать авторизации на сайте платежной системы. В этом случае payments может ответить "да, принято, текущее состояние платежа ID=123 — требуется авторизация на <URL>". При этом в ответах payments все равно сообщает текущее состояние с некоторой дополнительной информацией. Синхронный обмен здесь скорее для удобства чем для функциональности.

Отсюда и наблюдаемая асимметрия в примерах. Notify — это запрос к авторитетной системе. Он может привести к (синхронному) конфликту. Или (в зависимости от API) к асинхронному выполнению (http 202 — accepted). А вот await — это предикативное ожидание какого-либо состояния в авторитетной системе. Т.е. прослушивание событий из темы (topic) и реакция на них.
Re: Никогда не недооценивайте силу по умолчанию
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.22 08:30
Оценка:
Здравствуйте, Caracrist, Вы писали:

C>Из моего опыта по умолчанию должно быть примерно так:


C>

C>
Какой язык мне посоветуете?
C>

Для этого не нужен какой то особый язык, т.к. достаточно конвенции.
Re[4]: Никогда не недооценивайте силу по умолчанию
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.22 08:44
Оценка: 13 (2)
Здравствуйте, netch80, Вы писали:

N>2. Для "сложных и развесистых структур данных" можно почитать книжку Окасаки "Чисто функциональные структуры данных" или посмотреть реализации, например, в Erlang.

N>Там этот вопрос решён для большинства типовых структур (деревья, хэш-таблицы и всё такое). Обновление меняет корневые ссылки и сохраняет основное тело структуры.

Раз уж зашла речь про Окасаки, то стоит таки глянуть предисловие от самого Окасаки. Там сказано, что для многих задач он сам не в курсе эффективных решений на функциональщине, а для большинства задач сходится только ассимптотика, а не реальный перформанс.
Re[10]: Никогда не недооценивайте силу по умолчанию
От: Sharov Россия  
Дата: 31.10.22 13:43
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Отсюда и наблюдаемая асимметрия в примерах. Notify — это запрос к авторитетной системе. Он может привести к (синхронному) конфликту. Или (в зависимости от API) к асинхронному выполнению (http 202 — accepted). А вот await — это предикативное ожидание какого-либо состояния в авторитетной системе. Т.е. прослушивание событий из темы (topic) и реакция на них.


Вы предлагаете повесить на все это дело типизацию? Т.к. не ясно чем сущ. языки и библиотеки не подходят?
Если да, то почему с исп. соотв. языка будет проще, чем с соотв. языком+библиотекой?
Кодом людям нужно помогать!
Re[6]: Никогда не недооценивайте силу по умолчанию
От: _FRED_ Черногория
Дата: 31.10.22 16:34
Оценка:
Здравствуйте, netch80, Вы писали:

N>(Property стиля C# решают это же косвенно, но ломая ABI для тех модулей, что уже скомпилировались с прямым доступом. Или там это обошли?)


Что именно нужно было обходить? Добавить, например, set-тер к свойству можно, не требуя перекомпиляции использующего кода.

N>Кстати, я таки думаю, что самый правильный стиль из этих таки не в C++ private/protected/public, а в Java/C#, где умолчанием является package-internal.


В C# умолчанием является private для членов типа, а "package-internal" для типов уровня сборки.
Help will always be given at Hogwarts to those who ask for it.
Re[11]: Никогда не недооценивайте силу по умолчанию
От: maxkar  
Дата: 02.11.22 09:50
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Вы предлагаете повесить на все это дело типизацию?

Со временем — и типизацию. С ней как раз проблем особых нет и в существующей инфраструктуре.

S>Т.к. не ясно чем сущ. языки и библиотеки не подходят?

Конкретно данный вызов Notify:

S>Если да, то почему с исп. соотв. языка будет проще, чем с соотв. языком+библиотекой?

Здесь ценность даже не столько в языке, сколько в рантайме. Фокусы, подобные выше, легко изначально закладывать в среду выполнения но очень сложно добавлять потом в виде "библиотек". Ну и прочие вещи вроде ожидания с предикатом (wait в примерах здесь
Автор: maxkar
Дата: 19.09.22
). Я не собираюсь держать в памяти одного процесса кучу ожиданий с предикатами — это ненадежно.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.