Обновить
101
0.1
Роман Смирнов@Source

Head of Elixir at Ecom.tech

Отправить сообщение

Можно подумать, что у вас в программах вся нагрузка на процессор полезная xD
Стабильность и отказоустойчивость важнее экономии на спичках.

Ваши объяснения сводятся к утверждению «в Erlang-е сообщения полноценные, следовательно в других местах они ненастоящие».

Вовсе нет, такого вам никто не писал.


Они бы устроили, если бы где-то Кэй сказал, что начиная со Smalltalk-76 «сообщения уже не те»…

Я вам подскажу, в цитате выше есть слова "was" и "did", именно они в английском означают, что сейчас «сообщения уже не те».


Поэтому давайте так: вы уверены, что в Smalltalk-72 сообщения были полноценными именно в том смысле, который вы дружно вкладываете в этот термин?

Меня ещё и на свете то не было, когда Smalltalk-72 был в моде ))) Поэтому я тут полагаюсь только на слова Кэя. Зачем ему врать? Если там реально было так: "a message send was just a "notify" to the receiver that there was a message, plus a reference to the whole message. The receiver did the actual work of looking at it, interpreting it, etc.", то да — там были сообщения.


Эмулятор я видел, но им хрен разберёшься как пользоваться… В 70-е было модно что-ли программировать на символах, которые нельзя ввести в клавиатуры?

Ну если чисто технически к этому вопросу подходить. То как добиться уменьшения среднего значения любой величины? Найти максимальные отклонения от среднего в большую сторону и уменьшить их.


В контексте данного обсуждения, смотрим карту:
image


Видим, что минимум — 8, максимум — 50, среднее — 29. Во всех странах, где показатель равен 30 и выше, вводим какие-то меры, усложняющие наличие более 2 детей у одной женщины. Остальные страны вообще не трогаем. Итого: лет через 50 население планеты перестанет расти. Для пущего эффекта можно запретить переезд из стран с ограничением в страны без ограничения.


P.S. Я не призываю всё это воплощать, но если вопрос стоит: как остановить прирост населения Земли? То ответ будет в любом случае примерно такой, как я описал. Других гуманных и действенных способов в общем-то нет.

Ну спросите Кэя лично, если вам что-то непонятно в его формулировках. В чём проблема то? на Quora забанили? xD


Smalltalk-72, если судить по библиографии Хьюитта, просто не мог базироваться на модели акторов поскольку первое упоминание термина «Актор» относится к 73-му году.

Вот вы дотошный… Ok.
Smalltalk-72 базировался на том, что в 73 году переросло в модель акторов (вот Хьюитт — молодец, не стал всех путать термином "объект", а ввёл новый и более подходящий). Однако, сам Smalltalk пошёл дальше другой дорогой, и отказался от этих идей по performance-соображениям тех лет.

Игнорировать можно пустым методом (в том числе и #doesNotUnderstand:

Это не игнорирование, а ничегонеделание при обработке. Так тоже можно, но это совсем другой вариант.


Как часто мы хотим, чтобы получатель игнорировал полученные сообщения? Грубо говоря, это хорошо, если очень много посланных сообщений игнорируется?

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


iex(1)> pid = spawn fn -> 1 + 2 end
#PID<0.134.0>
iex(2)> Process.alive?(pid)
false
iex(3)> GenServer.cast(pid, :message)
:ok

Я просто сократил "a message send was just a "notify" to the receiver that there was a message, plus a reference to the whole message. The receiver did the actual work of looking at it, interpreting it, etc." до "настоящие сообщения".
Вы же сами пытаетесь выяснить, что мы подразумеваем под "настоящими сообщениями" и почему в Smalltalk они ненастоящие. Наши объяснения Вас не устроили, поэтому я нашёл Вам ответ лично от Кэя.
Да, в Smalltalk-72 были сообщения, которые мы тут обозвали настоящими. Но эту идею не смогли реализовать приемлемо по скорости работы. Поэтому от неё отказались и заменили сообщения на "замаскированный вызов метода". А термин "сообщение" исторически остался от первых реализаций, в которых они были.


P.S. Если Вас даже объяснение от Кэя не устроит, тогда уже не знаю, что тут ещё дополнить. Не хочется перепечатывать уже написанное. По-моему, мы коллективными усилиями разжевали ответ на ваши исходные вопросы дальше некуда.

Погодите, какая разница, что там описывает класс, если классы — это необязательная часть ООП. Это просто один из вариантов реализации.
Отсюда и все недопонимания. Если хотите руководствоваться википедией в этих вопросах, то советую читать английскую. В русской слишком много некорректных формулировок.


А классическое определение ООП, если что, выглядит так:


  1. Everything is an object.
  2. Objects communicate by sending and receiving messages (in terms of objects).
  3. Objects have their own memory (in terms of objects).

Про другие определения можно применять эпитеты "популярное", "распространённое" и т.д. Но классическое есть только одно.

Про такое я что-то не слышал… можно целиком его привести? А то непонятно, что такое "сущности из области определения".
Да и метод, вроде как, не может представлять взаимодействие между объектами, т.к. он принадлежит одному из объектов. Другими словами, метод максимум может быть частью интерфейса возможного воздействия на объект (однако, это однонаправленное понятие, в отличии от взаимодействия). Но и то публичные методы — необязательная часть, т.к. объект может и активную "жизненную позицию" занимать и сам откуда-нибудь брать себе работу (из очереди например), без внешнего обращения к нему.

А какое из определений ООП вы считаете классическим?


P.S. Мне кажется, с ответа на этот вопрос должна любая статья про ООП начинаться :-)

«настоящим» обьектам, представляющим данные

Почитайте ветку чуть выше. Я начал как раз с того, что настоящие объекты не должны представлять данные. И им не нужны вспомогательные объекты, им нужен интерфейс для получения нужных данных. А кто там выступит в роли провайдера этих данных, будет ли он один или несколько, не так уж важно.
По сути любая программа — это тупо набор конвейерных линий по обработке данных. Вы получаете пользовательский ввод, как-то его обрабатываете, в результате этой обработки что-то идёт на склад (в БД), а что-то отдаётся обратно пользователю.
А объекты — это рабочие на этих конвейерах, к ним приходят одни данные — уходят другие (обработанные) и отправляются к следующим объектам.

Тут про полиморфизм речь шла, а не про наследование.

Нет, не значит. Иметь состояние — это нормально и для актора и для объекта. А вот что именно будет его состоянием зависит исключительно от роли, которую он выполняет.
Другими словами, не "данные и методы работы с ними", а "методы, реализующие определенную ответственность, и данные, необходимые для выполнения этой ответственности".


Звучит как будто разница не велика, но по факту разница огромна.
Первый подход: у нас есть деревянный брусок, давайте опишем всё (методы), что можно с ним сделать.
Второй подход: нам надо сделать скалку, давайте подумаем что (какие данные) нам для этого нужно.

Удобно структуры и методы для их обработки располагать в одном модуле

Лишь до тех пор, пока вы не начали писать хоть что-то нетривиальное. Когда одна и та же структура в разных контекстах обладает совершенно разным поведением, пихать все методы в один файл становится преступлением. В лучшем случае вы такие структуры продублируете аля Bounded Context, в худшем — получите классический God Object аля Active Record.
Если бы вы изначально проектировали от поведения, а не от структур, таких проблем бы даже не возникло. Но в этом случае вам бы даже в страшном сне не приснилось структуры и методы для их обработки надо располагать в одном модуле.


Иерархичное расположение структур позволяет сократить описание как структур, так и методов.

Сокращение описания структур — так себе цель. Во-первых, если у вас в структуре 30+ полей, а вы ещё от неё и наследуете, и полей добавляете — это уже плохо пахнет. Лучше подумать над доменной моделью, чем экономить десяток строк.

Например, через протоколы вы можете достигать полиморфизма даже от типов, к реализации которых не имеете доступа.
Пример: https://medium.com/everydayhero-engineering/extensibility-in-elixir-using-protocols-2e8fb0a35c48


Ещё один вариант: Type Classes в Haskell.


Одной инкапсуляции мало — вы правы.
ООП = обмен сообщениями + инкапсуляция + максимально позднее связывание.
А вот полиморфизм вообще к ООП никакого отношения не имеет.

Так в этом и дело, что когда объекты общаются посредством отсылки сообщений (как это в определении ООП было заложено), то у каждого объекта есть своя «очередь сообщений».
Проблема что по факту это реализовано только в модели акторов. А ООЯП на классах и вызовах методов изначально свернули не туда. В результате знаменитые холивары на темы типа "принтер печатает строку" vs "строка печатается на принтере".


Это не так просто, а часто и невозможно.

Я бы сказал, что это просто непривычно.


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

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

Дело в том, что класс/объект никогда не должен строиться вокруг данных, он должен строиться вокруг поведения — одной определённой ответственности. Простейшее утверждение! При этом одно оно позволит избежать практически всех антипаттернов ООП. А большинство паттернов — просто следствия из него.


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

Эх, может соберусь когда-нибудь статью написать. В двух словах, основное дополнение — это метапрограммирование в стиле Lisp (манипуляции с AST). Многие другие плюсы (поддержка самого свежего Unicode, протоколы, примеси, etc.) являются как раз практическим применением этого метапрограммирования.

В Erlang (если я не прав, поправьте) обработка сообщения заключается в сопоставлении сообщения с образцом и выполнении соответствующего выражения.

Понимаете какая штука. С одной стороны, сказать так можно, но это слишком упрощенная трактовка. Как бы объяснить…


Представьте, что у вас несколько doesNotUnderstand и несколько способов отправить сообщение. В зависимости от способа отправки одно и то же сообщение попадёт в разные doesNotUnderstand. А внутри конкретного doesNotUnderstand оно — да — частенько будет сопоставляться с образцом (но не всегда). И опять же сопоставление с образцом и поиск метода по сигнатуре — это весьма разные вещи. Первое можно редуцировать до второго, но не наоборот.


Просто сойдемся на том, что Erlang ведь тоже еще не идеал?

В Erlang я пару субъективных минусов могу найти, а вот в Elixir до сих пор ни одного существенного минуса не нашёл. И даже сам в лёгком недоумении от этого. По-моему он объективно ближе всех к идеалу в современных реалиях. Не в 100% задач само собой. Я по бэкенду крупных веб-сервисов / мобильных приложений в основном специализируюсь, поэтому остальные сферы применения я просто не буду оценивать. Я мог бы рассказать чем кроме синтаксиса Elixir отличается от Erlang — но это будет слишком оффтопик.

Я надеюсь, за alankay1 на ycombinator скрывается именно ОН? :)

Да, это ОН. И на Quora тоже он. Я реально рекомендую Вам задать какие-то из ваших вопросы ему лично (мне кажется, у вас есть несколько, которые давно беспокоят). Он уже года 3 как достаточно активно отвечает на этих двух ресурсах на все вопросы, адресованные ему.
И именно по этим его сообщениям можно понять, что он не в восторге от текущих реализаций Smalltalk. Разумеется, он не может негодовать на эту тему открыто. Каким бы ни вырос твой ребёнок, он всё равно твой и ты его всё равно любишь. Но между строк сквозит некое сожаление и мощная ностальгия по первым реализациям.


Кстати, есть ещё достаточно интересное видео:
https://www.youtube.com/watch?v=fhOHn9TClXY


What's fun is that every Smalltalk contained the tools to make their successors while still running themselves

Да, а дальше про то, что реанимировать настоящие сообщения в Smalltalk — могло бы быть увлекательной задачей для опытных смалтолкеров. Впрочем, думаю, задачка достаточно нетривиальная.

выделили отдельный «концепт», более того, описали его на том же Smalltalk-е.

Ну смотрите, я по сути дал определение сообщения в Erlang:
Сообщение — данные, отправляемые адресату (актору) выбранным способом (обработка зависит от способа отправки, а не от пересылаемых данных).


Можете дать определение для Smalltalk? В принципе, наверно, стоило начать с определений, чтобы понять отличия.


этот самый обмен не обязателен для того, чтобы «настоящие» сообщения были: Erlang этот самый обмен на требует, но при этом имеет те самые «настоящие» сообщения.

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


Что касается внутреннего устройства акторов, это другой уровень — уровень реализации. Нет ничего страшного, что внутри клетки нет других клеток. Это позволяет прекрасно соблюдать SRP, Bounded Context и прочие важные архитектурные паттерны. У вас практически нет возможности напортачить со всем этим. В то же время, системы с обычным ООП повально страдают от несоблюдения этих принципов. И одна из основных причин почему так происходит — программист просто не понимает как правильно выделить классы. Вспоминается знаменитый холивар "строка печатает себя на принтере" vs "принтер печатает строку".


Понимание как правильно готовить ООП приходит с многолетним опытом, да и то не ко всем. А Erlang/Elixir с ходу даёт правильный подход из коробки. И уже набив шишки с популярным ООП, понимаешь насколько Армстронг был прав, говоря "любая отказоустойчивая архитектура в конце концов окажется похожей на Erlang".


Наделяя данные и структуры данных признаками объектов, мы совершали самую большую ошибку в применении ООП. Ужасно усложняя себе работу по сопровождению кода.


Объект никогда не должен строиться вокруг данных, он должен строиться вокруг поведения — определённой ответственности / должностной инструкции / etc. Простейшее утверждение! При этом одно оно позволит избежать практически всех антипаттернов ООП. А большинство паттернов — просто следствия из него.

Информация

В рейтинге
3 250-й
Откуда
Россия
Работает в
Зарегистрирован
Активность