Комментарии 105
Очередь всё равно содержит мьютекс или что-то подобное внутри себя. Чтоб встать в очередь, двум одновременно подошедшим с разных сторон клиентам надо синхронизироваться между собой.
При помощи мьютексов, локов на уровне базы, или других примитивов синхронизации мы добиваемся того, что пять пользователей будут обслужены последовательно, чтобы гарантировать наличие товара для каждого (последнего) из них.
Достаточно синхронизировать резервацию товара на балансе, привет передают все банки.
Банк может себе позволить заблокировать чужие деньги, а подавляющее большинство API поскромнее — нет.
Кроме того, ссылаться на банковское ПО, как на пример для подражания, — за гранью.
Почему вы привязались к одному примеру и причём здесь банковское ПО (они подобным алгоритмом занимались за много веков до рождения Бэббиджа)? Резервирование помимо указанных в задаче магазинов и складов используется отелями, логистикой, пассажирскими компаниями...
API магазина блокирует не чужие деньги, а свой товар на своем складе.
Весь io я делаю асинхронный. Из-за этого весь сервисный слой тоже асинхронный. Но модель домена синхронная по природе, да и время на его вызовы почти не тратится.
Должны ли быть асинхроннонными апи? Если это statefull то да, если нет то нет.
Но привязывать асинхронность к предметной области мне пока в голову не приходило
Но модель домена синхронная по природе,
Не любого домена.
да и время на его вызовы почти не тратится.
Повезло, чё. Но так будет не всегда.
Должны ли быть асинхроннонными апи? Если это statefull то да, если нет то нет.
API никому ничего не должны. В некоторых задачах stateful API должны быть синхронными (баланс бабла на счету), в некоторых — stateless запросто может быть асинхронным.
Связи между синхронностью и согласованностью практически нет.
Синхронность это больше про управление ресурсами. Особенно, если у нас одновременное количество запросов превышает количество ресурсов железа или программа вообще не умеет дать больше одного протока на запрос.
А согласованность это когда все части программы имеют одинаковые данные.
Если думаете что "раз операция критична и требует согласованности, то она должна быть "синхронной" в смысле блокирующей", то нет. Такое только если асинхронность реализована ненадежным образом. Например, микросервисом через ненадежную сеть
Если вы ждёте как закипает чайник и ничего не можете сделать пока он не закипит, то вы подобны однопоточным PHP, python или JS. В мире c# например это совсем не так.
В мире C# зачем-то умные люди портировали же из мира джавы Akka (которую в мир джавы другие умные люди принесли из эрланга, почти не испортив по дороге).
Если бы было все так хорошо, как вы рассказываете, этих портов бы не было, наверное.
У меня вопрос - "асинхронный" - это как понимают программисты или лингвисты?

Я буквально во втором абзаце прошу не путать асинхронное с параллельным.
Хорошие программисты понимают «асинхронность» как отсутствие согласования, синхронизации; один процесс может выполняться вне зависимости от состояния второго. Как кто-то из больших мастеров говорил: «Одна кофемашина, две очереди — это асинхронность, две кофемашины, одна очередь — это параллелизм».
Одна кофемашина, две очереди — это асинхронность
Это конкуренция, а не асинхронность.
Асинхронность, это скорее всего, когда к одной или нескольким кофе машинам создается виртуальная очередь, но люди в ней не стоят, а продолжают заниматься своими делами и подходят за кофе только тогда, когда им приходит уведомление, что наступила их очередь.
Да, в оригинале это concurrency.
Я имел в виду, что асинхронность без параллелизма превращается в конкурентность (могут люди заниматься своими делами, или нет — это неважно). Но сформулировал я отвратительно, пардон.
Мне кажется, что вы немного запутались и ничего ни в кого не превращается.
Асинхронность, это ручное переключение между внутренними потоками среды выполнения (задачами, нитями т.д.), тогда как параллелизм, это принудительное переключение между потоками ядра ОС (похоже на кооперативную и вытесняющая многозадачность).
И асинхронность не зависит от количества параллельных процессов, так как такие таски обычно выполняются в рамках одного потока ОС, чтобы экономить на переключении контекста и не требовалось использовать элементы межпотоковой синхронизации (хотя кажется С# ловил баги из-за await при переключении между своими внутренними потоками).
Мне не кажется, что ручное это переключение, или принудительное, — имеет значение.
Если на пальцах, то параллелизм — это то, что позволяет преобразовать CSV в памяти в набор «строк» (объектов) — в N раз быстрее, чем последовательная обработка одной строки за другой (где N — количество ядер).
Конкурентность (забудем про контексты и переключения) — ничего не убыстрит.
Асинхронность вообще никаких гарантий не даёт, и конкурентность, и параллелизм — асинхронны. Руками можно ничего не переключать, этим может быть занят компилятор, или среда выполнения.
Параллелизм и асинхронность, это теплое и мягкое и никак не связаны между собой.
Асинхронность, это вообще не про скорость, а про прерывание выполнения текущей задачи в конкретном месте, пока не будут доступны данные для обработки и увеличение скорости, это всего лишь следствие из-за более грамотного переключении контекста процессора между множеством задач из-за конкурентого доступа к более медленному ресурсу (у одной асинхронной задачи увеличения скорости не будет).
Один обработчик не блокирует другой обработчик. Вместе они могут использовать один системный поток и не использовать ресурсоемкое создание и переключение системных потоков.
Один обработчик не блокирует другой обработчик. Вместе они могут использовать один системный поток
Если несколько обработчиков используют один системный поток, тогда они друг друга именно блокируют (ведь выполняться может только один из них). Но переключаться между обработчиками в рамках одного системного потока можно очень быстро без переключения контекста.
Нет. Ресурсы и контексты тут вообще ни при чем.
Синхронное выполнение: одна живая очередь, один кабинет, один врач. Перед кабинетом — медсестра, которая разрешает каждому пациенту войти, когда выходит предыдущий.
Асинхронное выполнение: одна очередь, талончики, неважно, сколько врачей. Пациент получает талончик, идёт пить кофе, ему звонят на пейджер, когда пора идти.
Асинхронное конкурентное выполнение: —››—, врач один
Асинхронное параллельное выполнение: —››—, врачей много
Поэтому я и сказал выше, что параллельность увеличивает скорость, за счет использования всех врачей, а конкурентность — нет. А асинхронность — это просто антоним синхронности, там очередь живая, а тут талончики и вызовы.
Аналогии всегда лживы. Если вы пишите про асинхронность, как про антоним синхронности, то забудьте про программирование, так как в нем всегда есть и ресурсы и контекст.
В вашем примере "Асинхронное параллельное выполнение: —››—, врачей много" требуется синхронизация доступа при многопоточном выполнении (ведь врачей много), тогда как отсутствие межпоточной синхронизации, это одна из самых важных особенность асинхронного программирования.
Асинхронное выполнение (в программировании), это именно про сокращение ресурсов и уменьшение трудоемкости при написании кода (программисту легче и понятней писать алгоритм, а стейт машину за него сделает компилятор автоматически).
отсутствие межпоточной синхронизации
Почти сорок лет назад умные люди придумали эрланг, который я постоянно упоминаю. В нем нет межпоточной синхронизации, потому что она не нужна при полной изоляции потоков, иммутабельных состояниях и асинхронных сообщениях в качестве единственно возможного способа передачи информации.
Как и в любой другой реализации акторной модели, ссылки на которые я уже тоже триста раз приводил.
Я выбрал эрланг (эликсир) именно потому, что программисту легче и понятнее породить сто тысяч независимых параллельных процессов (потенциально на нескольких разных машинах в кластере) — и просто прозрачно обмениваться сообщениями между ними, как будто каждый из них — запущен в вакууме.
Врачу не требуется знать, чем там занят его коллега, чтобы вырезать аппендикс. Если ему потребуется помощь — он позовет коллегу сообщением, и коллега придет, но скальпель все равно будет в результате один.
И вы опять пишите какие-то аналогии (ведь речь шла об асинхронности для пациентов).
В Эрланге нет синхронизации, но подумайте, каким образом реализуются очереди сообщений между приложениями? Это просто другой уровень абстракции, когда синхронизацию доступа убрали из языка, но она никуда не делась, а просто переместилась на уровень ОС.
Это точно такой же способ упрощения разработки программ, как и асинхронное программирование. Но ни синхронизация, ни управление ресурсами от этого никуда не деваются, просто в случае Эрланга синхронизация отдается на откуп ОС, а в при асинхронном программировании, формируется стейт машина внутри программы.
синхронизация отдается на откуп ОС
Ээээ… Синхронизация чего именно?
И не ОС, а VM, но это несущественно.
каким образом реализуются очереди сообщений между приложениями?
Мне не нужно об этом думать, я это знаю. Когда виртуальная машина решает, что пора процессу №12345 дать немного процессорного времени, она заглядывает в его message box (это просто связный список сообщений в куче), и если он не пуст, достаёт оттуда одно и передаёт управление соответствующей функции процесса, вместе с состоянием (которое тоже просто лежит в куче). Там нет ни единой блокировки, потому что всё иммутабельно.
такой же способ упрощения разработки программ, как и асинхронное программирование
Любой, кто пробовал и то, и другое, — никогда в жизни так не скажет, готов поставить свой любимый кий против бамбуковой удочки.
Еще раз: чтобы запрограммировать процесс в эрланге, вам вообще ничего не нужно знать об окружающем мире. Во-о-бще ничего. И он будет работать одинаково в одиночестве, в окружении других сотен тысяч процессов, на другой ноде в кластере, и так далее.
Ээээ… Синхронизация чего именно?
Очереди сообщений у виртуальных машин. Вы же не думаете, что программа на Эрланг и в самом деле отправляет их в вакуум?
Думаю. Доставка сообщения в общем случае не гарантируется.
Гарантия доставки сообщения тут не причем.
Тогда я не понимаю, что именно вы имеете в виду под «синхронизацией очереди сообщений».
Ваша аналогия про "отправку сообщения в вакуум" очень правильная в части упрощения понимая требований к синхронизации процессов при разработке программы на Эрланге. Она помогает программисту абстрагироваться от этой проблемы и сосредоточиться на прикладном алгоритме.
Но ведь в реальности эти сообщения отправляются не в космос или вакуум, а в какие-то структуры данных, только о них заботится не прикладной программист, а среда выполнения (виртуальная машина), которая в свою очередь крутится на реальном железе (на нескольких процессорах и/или потоках). А раз их несколько, то синхронизация требуется почти в любом случае, просто она реализуется не в прикладном приложении, а на уровень или два выше.
Джо Армстронг защитил целую диссертацию по Concurrency Oriented Programming.
Синхронизация не требуется, если конкурентность перекладывается полностью на VM, процессы изолированы, а все без исключения данные — иммутабельны.
As soon as two processes share any common resource, for example, memory or a pointer to memory, or a mutex etc the possibility exists that a sodware error in one of the processes will corrupt the shared resource. Since eliminating all such sodware errors for large sodware systems is an unsolved problem I think that the only realistic way to build large reliable systems is by partitioning the system into independent parallel processes, and by providing mechanisms for monitoring and restarting these processes.
Этот конкретно пассаж — про fault tolerance, но я не могу же сюда весь диссер скопировать.
но я не могу же сюда весь диссер скопировать.
И очень хорошо, так как я вам пишу про другое. "Если конкурентность перекладывается полностью на VM", то в языке действительно не нужна синхронизация, вот только это означает, что синхронизация отдается на откуп ВМ или ОС, но она никуда не девается, просто проблема синхронизации будет решаться на другом уровне.
А я вам пишу, что не нужна никакая контекстная синхронизация, когда потоки выполнения абсолютно независимы, ну, чтобы было понятнее, как не нужна синхронизация между терминалом и браузером.
У терминала отобрали управление, браузеру передали. Всё.
Я понял, что вы хотите обсуждать свои абстрактные аналогии вместо обыденной физической реальности.
Давайте тогда прекратим переливать из пустого в порожнее, если до вас никак не дойдет, что какой бы гениальной диссертацией не была, у нее всегда будут ограничения и допущения и самое главное из них, это реализация в железе, которой обязательно требуется синхронизация доступа в том или ином виде.
Можно и без среды выполнения
Компилятор Rust преобразует это в конечный автомат (state machine), который реализует трейт Future.
В Rust асинхронность — это статически компилируемый механизм представления задач с точками приостановки. И это бесплатная абстракция в отличие от языков со средой выполнения
преобразует это в конечный автомат (state machine)
Это может не только Rust. "Среда выполнения" - это и есть либо конечный автомат, либо нити, легкие потоки и пр. Конкретная реализация асинхронного выполнения зависит от языка и реализации системных библиотек.
Вы пробовали на расте писать что-то по-настоящему параллельное?
В теории это прекрасный язык. На практике мало-мальский сложный высококонкурентный проект на нем не удавалось пока написать никому, включая авторов языка.
Servo от авторов языка на раст. Gecko (Firefox) использует его наработки и пишет модули на раст. Blink (Chrome,...) частично использует Раст.
Но я не продавать его вам пришёл.
Лично я пишу на нём мелочи
Я написал что можно и без среды выполнения
Авторы языка так и не смогли дописать Servo до релиза, про то и речь.
У него есть замечательная ниша, которая ограничивается (надеюсь, пока) модулями, плагинами, кусочками кода. Вон я вчера ссылку выложил на действительно крутую утилиту для работы с CSV-файлами.
Но для настоящего продакшена он пока непригоден, не знаю уж, как так вышло. И на го я что-то пока серьезного параллельного кода не видел. Я лично делаю из этого (возможно ложный) вывод, что без среды исполнения мы пока не справляемся. Буду рад ошибиться, но на данный момент — что есть, то и есть.
Всё-таки, что такое серьезный параллельный код?
Воображаемый движок хабра, в котором каждая статья с активностью за последний месяц живет в своём активном процессе.
В продолжении этого текста я покажу свою реализацию FSM, которую мы в продакшене используем для того, чтобы держать горячий кэш актуальных курсов валют (там примерно 40К процессов в реальном времени получают данные от внешнего источника, высчитывают курсы с учетом некоторых эвристик и отдают их по мере надобности).
WhatsApp — тоже прекрасный пример.
Хорошие программисты понимают «асинхронность» как отсутствие согласования, синхронизации
Никакой связи
В программировании асинхронность означает, что код может продолжать выполняться, не ожидая завершения долгих операций. Но мы всё равно не можем выполнять что-то пока файл не прочтется. Значит асинхронность в программировании это что-то про освобождение ресурсов. Парадокс, увод термина от первоначального значения.
10,000 синхронных потоков, ожидающих I/O = 10,000 заблокированных потоков операционной системы с медленным переключением контекста. Асинхронность помогает тратить меньше системных ресурсов и быстрее переключать задачи.
В однопоточных приложениях асинхронность даёт возможность в ожидании ответа начать обработку другого события, без неё такой возможности совсем нет. И вот отсюда ноги растут причины сдвига значения термина.
Пример
Гипотетический веб-сервер, обрабатывающий 10,000 одновременных запросов, где каждый запрос включает ожидание базы данных (50 мс):
Синхронная многопоточная модель:
10,000 блокирующих потоков
~20 ГБ памяти на стеки потоков
Высокие затраты на переключение контекста
Серверу может потребоваться 128+ ГБ RAM
Тем не менее, он может обработать их
Асинхронная модель:
8-16 потоков (для использования всех ядер CPU)
~32 МБ памяти на стеки потоков
Минимальное переключение контекста
Серверу может хватить 4-8 ГБ RAM
В современном программировании "асинхронность" фактически означает:
Способ организации конкурентного выполнения, при котором операции могут приостанавливаться и возобновляться без блокирования системных потоков
Произошло серьезное смещение значения термина:
1. Исходное значение: "не зависит от временной синхронизации" (не требует ожидания)
2. Текущее значение: "освобождает системные ресурсы во время ожидания", "организует собственную легковесную очередь ожидания"
Меня самого бесят эти съезжающие термины. Например, ООП это совсем не тот ООП который был изначально
Пожалуйста, я по-человечески прошу, прекратите копипастить сюда бессмысленную херню, которую генерирует ваш Т9.
Я тоже вас прошу не пользоваться абстрактными терминами в искаженном смысле. С ООП то же самое было.
Могу только своими словами писать, но будет не менее абстрактно и трудно понять где же правда.
Абстрактные (или обобщенные если хотите) термины вроде ООП, асинхронности имеют плохое свойство: нет математически точного определения. Либо практика не соответствует когда то данному определению.
И если говорить ООП или асинхронность мы ступаем на очень тонкий лёд. Особенно, если пользуемся разными языками для реализации. Проще говоря, абстракции сильно текут.
10,000 блокирующих потоков ~20 ГБ памяти на стеки потоков
Это виртуальная память. Реальной памяти будет занято от 40 МБ.
Высокие затраты на переключение контекста
Ну а stackless-сопрограммы хранят данные сильно фрагментированно в куче вместо стека, что куда плачевнее для IO-bound задач.
Пробовали?
Я нет, только в теории
В высококонкурентных средах IO-bound задачи элиминируются всеми силами. В кластере любая IO-bound задача — узкое место, причем не только для производительности, но и для горизонтального масштабирования.
Воткнуть еще 10 нод в кластер — прозрачный и безболезненный процесс только в отсутствии (или внятной, строгой изоляции) IO-bound задач.
Вы описали проблему стекового управления памятью в многопоточной среде, но это не имеет никакого отношения к синхронности/асинхронности.
«асинхронность» (сепульки) как отсутствие согласования, синхронизации (сепулькария) ; один процесс может выполняться вне зависимости от состояния второго
Мне вот эти конкретно слова не нравятся. 4 ошибки в 3 словах
Могу принять в таком виде
«асинхронность» как отсутствие взаимного ожидания (мы не согласовываем ни потоки ни события ни обработчики); один обработчик событий (чтобы не путать с thread, системным потоком) может выполняться вне зависимости от ожидания (состояние не причём) второго.
Как свойство асинхроннонность позволяет по возможности использовать для обработки один и тот же поток в ожидании длительных операций по обработке нескольких событий.
Если хотите использовать общие слова то будьте точны.
Хоть триста абстракций вокруг одного ядра построй — две задачи одновременно оно выполнять не научится.
Я б тут отметил следующее.
Обычно когда говорят о проблемах синхронизации, имеют ввиду что-нибудь сложносоставного объекта, многопоточный доступ к которому надо обкладывать мьютексами, чтобы все видели объект в консистентном состоянии, а не в каком-нибудь промежуточном.
Однако есть и другой, более интересный аспект. Представьте себе, у вас есть некоторый внешний сложносоставной объект, и сведения о его состоянии (и его изменений ) вы получаете по частям. Понятно, что промежуточные состояния могут меняться в разной последовательности, и даже в разной последовательности для разных наблюдателей.
Но когда всё устаканится, хотелось бы, чтобы конечное состояние вашего представления об объекте было бы консистетно с состоянием самого объекта и зависило только от него, а не от того, каким путём вы туда придете.
Это вот и называется eventually consistent.
Заметим, что от многопоточности это не зависит никак от слова совсем. А зависит только лишь от корректности переходов вашего представления о состоянии объекта и от того, что поступившие нотификации об изменении разных частей этого состояния правильно "складываются" между собой, вне зависимости от порядка поступления.
Анализировать такой код сложно, и на проблемы можно нарваться даже и в однопоточной реализации. Тут асинхронность внешняя, а не внутренняя. Обусловленная не реализацией, а самим устройством вещей.
И это - более интересный случай, чем те, которые решаются мьютексами. И в большей степени привлекающий сложные ошибки, которые трудно анализировать и устранить.
зависело только от него, а не от того, каким путём вы туда придете
Тут надо отметить, что в общем случае эта задача решения не имеет.
это — более интересный случай, чем те, которые решаются мьютексами
Тут бы с мьютексами разобраться для начала. Пока мне кажется, что такая задача решается лучше всего стейтмашиной, которая умеет прикладывать диффы и хранить те, которые пока не были приложены. Но надо еще подумать, спасибо.
P. S: Объект, который изменяется в разных местах разными способами — это плохо. Простота и бессвязность — вот наш девиз.
Ну я не тексты все-таки редактирую, одновременно и асинхронно с нескольких рабочих мест, а скорее ищу принтеры в сети по DNS-SD.
Но там тоже своих забот хватает. Например, IPv6-е адреса могут прийти совершенно независимо и асинхронно от IPv4-х. Информация о доступе через IPP приезжает на другом паровозе, чем информация о доступе через IPPS. А хотелось бы более-менее консистентно свести всё это вместе (с разумной долей вероятности), прежде чем пользователю показывать.
Диффы — это не обязательно про тексты же. Если есть MAC — то IPv6 и IPv4 подружить должно быть несложно, и это ровно история про независимые диффы.
Я имею ввиду, не обязательно так сложно, как с полноценными диффами.
Свести можно по UUID, он предусмотрен. Сложный вопрос, в какой момент перестать больше ждать и решить, что вот он, окончательный результат. И что делать при наличии противоречий (хоть это и маловероятно).
«Окончательный результат» — штука, которой в реальном мире не предусмотрено (кроме апокалипсиса, конечно).
Поэтому в идеальном случае — обновляем то, что видит пользователь в режиме реального времени, в неидеальном (как обычно) — таймаут, который может быть подкручен эвристиками, наподобие: у каждого IPv4 в нашей сети есть парный IPv6 и наоборот.
При наличии противоречий придется просто выбрать что-то одно, чуда не произойдет: показывать оба значения красным, ни одного, или опять на эвристиках: если все остальные в той же подсети, что один из двух противоречивых — то вот он, правильный.
Из известных мне более-менее распространенных языков, AST на уровне стандартной библиотеки (что означает — в прямом доступе компилятора) реализован в лиспах, эрланге и эликсире (а также наполовину в julia и на 200% в brainfuck).
Python, Scala, Rust. Есть и в OpenJDK, но в виде непубличного API.
Питон и раст? Серьёзно? И там и там оно сбоку шиферными гвоздями приколочено.
Непубличное API — лучше, чем ничего, но мало помогает разработчику, потому что завтра всё поломается.
Про скалу не знаю, скажу честно. Посмотрю, спасибо. Скала может, её правильные ребята проектировали.
И там и там оно сбоку шиферными гвоздями приколочено.
В чём это выражается?
Питон:
The abstract syntax itself might change with each Python release; this module helps to find out programmatically what the current grammar looks like. — https://docs.python.org/3/library/ast.html
то есть это вообще ни о чем, read-only feature.
Раст:
syn и quote — сторонние крейты, а ast — всё еще read-only beast
И то и другое — было добавлено значительно позже появления языка и procedural macros официально считаются плагинами к компилятору.
то есть это вообще ни о чем, read-only feature
Или я чего-то не понимаю в вашем посыле, или не read-only https://docs.python.org/3/library/ast.html#ast.NodeTransformer
The abstract syntax itself might change with each Python release
Это буквально означает, что вы не можете использовать AST-трансформации в продакшене.
AST и API работы с ним не меняются только у тех языков, которые не развиваются.
Развиваться в течение заметного времени требуется только тем языкам, которые изначально были спроектированы как говно.
Это не касается С++, там это просто уже острая неизлечимая форма шизофрении.
Что касается AST, то если ему нужен специальный API — это уже родовая травма. И если оно изменилось хоть на йоту после фиксации в версии 1.0.0
— это значит, что нет никакого AST, есть фантазии на тему.
На LISPы гляньте. Вот там AST рабочее.
Буква A в AST как бы намекает, что дерево не должно меняться вместе с языком.
Было бы здорово, но совершенные абстракции недостижимы. Хочешь добавить новый оператор или изменить существующий, придётся менять AST.
Так что там с LISPами? Как они-то справляются?
Имеют очень примитивный синтаксис из, образно говоря, пары очень примитивных операторов. Если попытаться написать другой язык с применением такого же подхода, получим ещё один LISP. А это не нужно по двум причинам: Во-первых, LISP'ы уже есть. Во-вторых, на них пишут полтора программиста, уже не образно говоря. Прикладники хотят Java и Go с кучей операторов, которые не simple, но easy.
Как вы мило переобулись из «да везде этот AST есть» в «да никому этот AST не нужен» — буквально за 23 часа.
Прикладники хотят Java и Go с кучей операторов […]
Если прикладников запереть за колючей проволокой, и показывать им только артхаус, они и Дэвида Гриффита полюбят.
да везде этот AST есть
да никому этот AST не нужен
Не утверждал ни того, ни другого. Похоже, вы видите в моих комментариях желание опровергать вашу позицию или даже нападать лично на вас - это не так.
Вовсе нет. Кроме того, мне импонирует, когда мою позицию доказательно опровергают, а напасть лично на меня из интернета — невозможно.
Я вижу в ваших комментариях нежелание понимать, что такое AST и зачем он в принципе может быть нужен. Вы как бы потакаете толпам формошлёпов, которым LLM пишет их круды, а понимание, что там за экраном — непозволительная трата времени. Я полагаю это довольно вредным влиянием, и вот именно с этим, по мере сил, стараюсь бороться.
Есть мир наших идеалистических мечтаний о том, как должно быть, и объективная реальность. Я считаю, что успех приближения одного к другому в готовности к компромиссам. Хороший пример - это ситуация со Скалой: Одерского просят хотя бы чуть-чуть сменить вектор развития языка в сторону удобства его промышленного применения, но он продолжает все силы вкладывать в развитие возможностей, имеющих преимущественно академическую ценность, а язык тем временем стремительно теряет популярность. Так же с AST, можно сказать, что в том же Python механизмы метапрограммирования недостаточно хороши и не надо их применять, то ли дело в Lisp'ах, а можно рассказать огромному питонячьему сообществу, как круто иметь возможность менять код в компайлтайме, как полезно создавать выразительные DSL, и что для их рантайма даже есть свой Lisp.
можно рассказать огромному питонячьему сообществу, как круто иметь возможность менять код в компайлтайме
Чтобы завтра всё поломалось? Вы думаете, питонисты скучают по увлекательному переходу со второй версии на третью?
Наличие Hylang как раз и говорит о том, что в питоне что-то не так.
Кроме того, моё оригинальное утверждение (все еще верное), гласило буквально следующее: правильный подход к AST продемонстрировали (кроме LISP’ов) — эрланг и эликсир. Это было не оценочное суждение, это была констатация факта, с которой вы зачем-то начали спорить.
Насчет промышленного применения Скалы — я считаю, что Akka написали именно на Скале не случайно, как и хадуп со спарком. Что может быть промышленнее? Круды в банке? Для них уже есть сама джава.
Хороший язык (повторюсь) — не испытывает нужды куда-то там развиваться. За почти сорок лет «развития» эрланга — в язык добавили мапы. Всё остальное «развитие» — связано с ростом производительности, починкой очень неочевидных ошибок в крайне нестандартных случаях и улучшением документации. И тем не менее BEAM — остаётся лучшей (и единственной) средой для создания отказоустойчивых систем. Эликсир со своими ноутбуками — за год уделал юпитер-ноутбуки не только удобством и кластеризацией из коробки, но и нативной компиляцией в CUDA. Но, как и скала, как и LISPы, — лучшее всегда остаётся нишевым, потому что миллионы мух не могут ошибаться, а широта использования языка до сих определяется только количеством денег, вложенных в раскрутку.
Повторюсь, я с вами не спорю. Просто задаю вопросы, чтобы лучше понять ваш посыл и узнать что-то новое для себя.
А я тоже не спорю (ну, зависит от определения слова «спорить», конечно).
Я отвечаю на ваши комментарии, потому что мне есть, что добавить. Стремление оказаться правым — целесообразно в современных европейских парламентах и в постели, в интернетах — оно совершенно неуместно, и я уж подавно таковую цель не преследую.
Если после добавления в язык нового оператора меняется AST для старых (а тем более API для AST), то это значит, что был с самого начала плохо продуман метаязык.
Лисп, конечно, недосягаем в том отношении, что является метаязыком сам для себя. Но это необязательно, если просто хорошо подумать. Как мне помнится, например, разработку Ады начинали с метаязыка.
Эрланг был начат с метаязыка, эликсир на основе опыта эрланга — тоже. И там, и там — AST фактически, как в лиспах, я однажды целую библиотеку на нем написал.
При этом в расте quote! — попытка повторить идею эликсирной quote/2, но 1:1
это оказалось нереально, потому что ни контекст не протащить, ни компилятор не помощник.
Но в Глобусе проблему колбасы и очереди решили введением нового уровня абстракций и мультипликацией в нем уменьшенных копий обработчиков: каждому вручают персональную кассу в виде штрихкод сканера. Покупатель становится сам себе кассир, так сказать personal Jesus
Звучит очень умно, но практического смысла я не увидел. Стандартные подходы вполне устраивают.
мы не можем гарантировать eventual consistency нашей модели, если она асинхронна. В воздухе явственно запахло CAP-теоремой, но на практике всё гораздо проще.
CAP теорема исключительно про strong consistency. Eventual consistency применяется исключительно в асинхронных моделях, и работает она там замечательно. Другое дело, что есть области где она неприменима и необходима именно строгая согласованность. Ну оно и нормально, нечего микроскопом гвозди забивать, или через молоток микробов разглядвать, каждому инструменту свое дело.
Глянул в статье про эту самую Linearizability, принципиальной разницы с strong consistency не вижу. В том примере, с Алисой и Бобом, строгая согласованность означает, что пока изменения в данных не попадут с мастера в реплики, читать эти реплики будет нельзя, таким образом и Алиса и Боб в итоге прочитают одно и тоже.
Консистентность к линеаризации не имеет никакого отношения.
на хабре есть русский перевод
Да, я ровно это и написал.
На этот счёт, помню, став менеджером, осознал, что менеджерская деятельность крайне похожа на асинхронный код (раскидал задачи, позадавал вопросы в чатах, понаписывал письма, и ждёшь, пока одна из этих корутин вовлечет тебя на 5 минут эксклюзивно, после чего снова на await сядет), а исполнительская - на синхронный, ибо берёшь задачу, и делаешь, надо что-то уточнить, - задал вопрос и ждёшь, чтобы дальше продолжить.
Мы тогда ещё шутили с коллегами, что, следует ли их этого, что с NodeJs программистов выходят отличные менеджеры?
Вопрос интересный про параллелизм и асинхронность. Просто надо учитывать некоторые вещи а именно, доступ к ресурсу. А доступ к нему , например к БД идет через тот же Ethernet,, это не параллельный доступ к сети а случайный. Задержка будет зависеть от количества пакетов на линии. Далее сам ceрвер данных , транзакционая модель которого тоже не дает параллельного доступа к ресурсу ( очередь, по аналогии со статьей причем жесткая и если несколько покупателей пытаются вырвать ресурс-колбасу из рук продавца-сервера, то тот сам решает кому ее отдать, так разруливая дедлок. Нас спасает только размазывание запросов во времени. Про параллелизм здесь можно забыть от слова совсем. В жизни параллелизм вполне возможен. Я ставлю чайник и пока он закипает делаю 50 отжиманий, к примеру.
потому что мы все привыкли к одноядерным процессорам
продолжают моделировать полностью асинхронный мир — синхронными шаблонами
Если вы не можете вернуть ответ API до того, как прочитали данные из базы, значит ваш алгоритм - синхронный. Сначала надо прочитать, потом отправить. Окружающий мир асинхронный (хотя на самом деле параллельный), низкий уровень асинхронный, а ваш алгоритм синхронный. Синхронность определяется логической зависимостью по данным, а не аппаратным уровнем. Получение результата это точка сихронизации, нельзя начать следующий шаг, не завершив предыдущий.
Мне не надо ждать, пока вон та тетка оплатит свою колбасу, я уже отпихнул её, схватил свою, и несусь к кассе.
Ага, или к двери, не оплатив. Как гарантировать, что взятый со склада но неоплаченный товар не выйдет за пределы магазина, не исчезнет в никуда, а вернется обратно на склад? И даже в этом примере вам понадобились мьютексы в виде отпихивания.
Разница между примером с Inventory и этим только в том, что здесь этих Inventory несколько в виде рядов товара на прилавке, которые подготовлены заранее. Это можно сделать и в "обычном" подходе. Вместо одной записи Inventory в базе будет 10, и время ожидания будет N×t/10.
Покупатели сначала выстраиваются в очередь к нашему супер-раздавателю колбас, а потом — к кассиру.
В "обычном" подходе всё то же самое, про резервирвание уже написали. То, что вы называете N×t, это ожидание резервирования. В вашей очереди к раздавателю последний будет точно так же ждать всех предыдущих то же самое время N×t.
В "обычном" подходе мьютексы на весь процесс покупки обычно не делают, один платит через PayPal, другой банковской картой, зачем им друг друга ждать?
и просто забить на потерявшиеся колбасы (этот вариант неприменим, если магазин ...)
Он неприменим в любом магазине, потому что бизнес этого не хочет. Если ваша программа позволяет терять товары, а другая не позволяет, магазин купит другую программу, и ему неважно, что покупатели будут ждать чуть дольше, у них 10000 человек в одну секунду обычно товар не покупают. Если ваш бизнес согласен с потерей товара, это исключение, а не правило.
Очень приятная статья! Но почему-то у меня всё везде однопоточное. С другой стороны, наши языки влияют на мышление — если чего-то не знаешь, то этим и не пользуешься, и не видишь самих возможностей использования.
Мы тут в срачах про нужность ВО для программиста проходили — человек с ВО и большим кругозором просто видит больше возможностей, там где ПТУшник пройдёт мимо, у «инженера» может что-то щёлкнуть, а может и нет.
В общем, надо Эрланг учить и применять... Вопрос — с чего начать?
если чего-то не знаешь, то этим и не пользуешься, и не видишь самих возможностей использования
Я ради именно вот такого отзыва этот текст (и все смежные) и писал. Спасибо!
с чего начать?
Я завтра очень подробно и — насколько смогу — внятно — отвечу. На свежую голову.
В современных реалиях лучше подходить к снаряду со стороны эликсира: у него [незначительно] лучше инструментарий, есть прямо киллер-фича в виде макросов и экспонированного AST (у эрланга оно тоже есть, но на мой взгляд хуже подходит для погружения), ну и синтаксис чуть посовременнее. При этом они полностью интероперабельны, из эликсира можно звать эрланг и vice versa. Когда понадобится, вызвать библиотеку на эрланге будет очень просто.
Язык (эрланг) создавался почти сорок лет назад, поэтому он как emacs — в стандартной библиотеке есть всё, что может потребоваться на низком уровне.
Я обычно не рекомендую читать книги, поэтому если есть на уме какая-то задачка, то я бы прочитал Getting Started и открывал REPL :)
Но если такой задачки нет — лучшая беллетристика на тему — книга «Elixir in Action» Саши Юрича (она переведена на русский). Саша — практикующий профессионал, принесший в экосистему кучу пользы (и немного помогший мне с библиотекой, описанной в приложении к тексту выше).
Я не рекомендую заходить со стороны веба (Phoenix Framework), потому что если вникнуть в идеологию эрланга/эликсира — фреймворк потом можно освоить за день, а наоборот — это все равно, что сначала научиться жать на газ, а только потом — крутить руль. И я повидал ребят, по нескольку лет работавших с финиксом и впадающих в ступор при виде «низкоуровневого» запуска процессов.
Если у вас есть неоформившиеся мысли на тему проекта, на котором можно потренироваться — задавайте столько вопросов, сколько появится.
Приятного путешествия!
Этот мир — асинхронный, и что вы ему сделаете