Pull to refresh
102
0.2
Роман Смирнов@Source

Head of Elixir at Ecom.tech

Send message

Так в этом и дело, что когда объекты общаются посредством отсылки сообщений (как это в определении ООП было заложено), то у каждого объекта есть своя «очередь сообщений».
Проблема что по факту это реализовано только в модели акторов. А ООЯП на классах и вызовах методов изначально свернули не туда. В результате знаменитые холивары на темы типа "принтер печатает строку" 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. Простейшее утверждение! При этом одно оно позволит избежать практически всех антипаттернов ООП. А большинство паттернов — просто следствия из него.

Да, содержимое сообщения — это просто данные. Но чтобы его отправить нужно ещё выбрать способ отправки и получателя. Итого, данные, отправляемые адресату выбранным способом, — это сообщение. Пока данные никому не отправляются — это не сообщение. Этого достаточно чтобы выделить отдельный концепт.


Наконец, я же могу на Erlang-е и без акторов обойтись?

В том то и вся прелесть, что не можете. Всё есть акторы и ничего вне акторов не существует.

Можно ссылочку на негодование Кэя по этому поводу?

Есть цитата по поводу почему отказались от полноценных сообщений:


Take a look at the first implemented Smalltalk (-72). It implemented objects internally as a "receive the message" mechanism — a kind of quick parser — and didn't have dedicated selectors. (You can find "The Early History of Smalltalk" via Google to see more.)
This made the first Smalltalk "automatically extensible" in the dimensions of form, meaning, and pragmatics.

When Xerox didn't come through with a replacement for the Alto we (and others at Parc) had to optimize for the next phases, and this led to the compromise of Smalltalk-76 (and the succeeding Smalltalks). Dan Ingalls chose the most common patterns that had proved useful and made a fixed syntax that still allowed some extension via keywords. This also eliminated an ambiguity problem, and the whole thing on the same machine was about 180 times faster.

В общем, в Smalltalk когда-то были полноценные сообщения:


In 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.

Но их выпилили из-за performance-проблем, а терминология осталась. Отсюда и вся путаница.

Да не обязательно. Никто не заставляет как-то обрабатывать этот ответ. Этим сообщения и отличаются от вызова метода. Получатель может их игнорировать.


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

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

Нет. Я могу знать, что какое-то сообщение попадёт в handle_call или в handle_cast, но от сообщения это никак не зависит. Обработчик зависит от того каким способом отправлено сообщение, а не от его содержания.


А может быть, есть шанс получить акторы без Erlang-овских заморочек?

Каких заморочек? Накостылись можно что угодно, но зачем? Когда есть проверенная десятилетиями реализация, которая отлично работает.


ограничиваясь «нормальными» объектами там в остальных случаях

Зачем нужны «нормальные» объекты? Если не нужен полноценный объект, ограничьтесь обычным значением или структурой.
Не знаю, как там Smalltalk обходит performance-проблемы от этой бесполезной упаковки простых данных в объекты. А бедные C#/Java/etc. с бесконечным boxing/unboxing только маются.

То есть встретить объект, состоящий из других объектов было бы тоже крайне странно. ;)

Угу, поэтому состояние объекта и не должно состоять из других объектов.


  1. Objects have their own memory, which consists of other objects.

Это формулировка Тима Бадда из книги "An Introduction to Object-Oriented Programming", которая со Smalltalk не особо связана.
А у Кея было так:


  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).

Причём он сам был недоволен, как Бадд переврал его определение, изменив смысл. Вот тут можно подробнее почитать, в том числе и про то, что версии Smalltalk до Smalltalk-80 базировались на модели акторов. В принципе, можно сказать, что Smalltalk-80 свернул на кривую дорожку и отказался от ООП в исходном понимании.


P.S. Собственно, чего тут спорить, Кей тут на днях сам написал, что
Erlang is much closer to the original ideas I had about “objects” and how to use them.
Можете с ним лично поспорить, если хотите :-)

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

Эх, я помню, как в 2005 году качал DRKB (Delphi Russian Knowledge Base) в CHM-формате, которую надо было ещё порезать на 3 части, чтобы на дискеты влезло. И это получилось провернуть только со второго раза. В первый раз отведённого часа в интернет-центре не хватило, чтобы скачать эти 3.5 Mb

Дело в том, что «Erlang-овский ООП» не предполагает никакого ООП на уровне «ниже процессов»

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


А по поводу внутреннего устройства, так это и к клеткам применимо. Даже если любой организм состоит из клеток, то внутри клетки очевидно не другие клетки, а митохондрии, рибосомы и т.д. Другими словами, встретить объект внутри объекта было бы крайне странно.
Поэтому я бы сказал, что пункт «все есть объект» в Erlang тоже выполняется, т.к. нет никакой возможности выполнить какой-то код вне актора. А пункта «объект должен состоять из других объектов» вроде никогда не было.

На следующем митапе можем обсудить )

В Smalltalk-е тоже прием сообщения и код реакции не связаны напрямую!

Вообще связаны, в штатном для Smalltalk случае, в самом "сообщении" зашита информация какой метод будет вызван для его обработки.


Да, если вы будете принимать все сообщения через doesNotUnderstand, потом их как-то асинхронно обрабатывать, и возвращать результат через thisContext — то есть шанс получить кривую и не очень стабильную реализацию Erlang поверх Smalltalk. Весь вопрос в том, что это будет неестественно для Smalltalk. Настоящие сообщения неестественны для Smalltalk, но да — теоретически их можно эмулировать, правда, для этого ещё придётся полностью переписать стандартную библиотеку.


сообщение не обязательно связывается с выполняемым кодом по имени метода

Вы "не" не туда поставили. Должно быть "обязательно не связывается".

экземпляр BackTalker-а получая сообщение talkBack, посылает сообщение отправителю — обмен сообщениями? Обмен!

Если вы будете все результаты вычислений пересылать через thisContext — это весьма быстро превратится в треш и угар. В Smalltalk это скорее хак для редкого применения, чем что-то чем можно пользоваться для ежедневного применения.
А так да, из любого Тьюринг-полного языка можно сделать плохой Erlang (эмуляцию обмена сообщениями и т.д.), с этим никто не спорит. Даже в статье про это было.


Нет обязательного обмена сообщениями — как и в Erlang-е, кстати ;)

Что так? В Erlang он вполне себе обязателен на уровне акторов, никакого другого пути кроме обмена сообщениями в нём просто нет.


Ага, и нет зайцев, как отдельного от животных понятия?

Ну, допустим, зайца, не являющегося животным, найти можно: плюшевого, нарисованного и т.п.


А в Erlang-е нет понятия сообщения как отдельного от понятия валидный терм (т.е. от понятия «данные»). Бардак!!! ;)

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

Здравому смыслу :-)
Нельзя сказать, что объекты обмениваются сообщениями, если сообщения — это тоже объекты. Получается, объекты обмениваются объектами, а точнее не обмениваются, потому что ответное сообщение невозможно отправить тому, кто прислал входящее. Короче, бардак: нет ни обмена сообщениями, ни самих сообщений, как отдельного от объектов понятия.

я могу реализовать средствами языка возврат значения через посылку сообщения; в этом случае что-то сразу поменяется?

Если у вас не будет никакого другого способа вернуть значение, то да — многое поменяется.


сами сообщения в Smalltalk остаются при этом не менее настоящими, чем в Erlang? или же в первом что-то не так именно с самими сообщениями? что именно?

В конечном итоге, в Smalltalk всё равно есть соответствие "сообщение — метод", кроме спец.исключения doesNotUnderstand. В Erlang все сообщения обрабатываются через стандартные behaviours, типа этого. Таким образом, приём сообщений и код реакции не связаны напрямую. Например, вы можете переименовать функцию, которая реализует реакцию на сообщение, но вся остальная программа об этом не узнает, не надо будет менять код отправки этого сообщения. И даже роль почтальона играет отдельный модуль


gen_server:call(pid_or_pname, message)
Вы упорно отказываетесь различать объекты и акторы, а это, очевиднейшим образом, разные вещи, что прекрасно видно на приведенном вами примере.

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


I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea. The big idea is "messaging".
I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages. © Alan Kay


Что тут важно? Биологическая клетка или компьютер в сети — это активная действующая единица. На самом деле, термин "объект" крайне неудачно подходит для самой концепции, поскольку имеется в виду субъект действия, который может общаться с другими субъектами посредством обмена сообщениями. Всё остальное вторично.


Именно так реализованы акторы в Erlang/OTP. А объекты в Smalltalk реализованы не так (хотели так, но видимо для 1969 года идея оказалась слишком сложной). Глупо думать о книге, о строке, или о числе, как об отдельном компьютере в сети (даже умозрительно это был бы абсолютно иррациональный оверхэд, вдобавок затрудняющий понимание функционирования сети), поэтому они не могут быть объектами в Кеевском понимании. Вот собственно и всё резюме.


Объекты (по умолчанию) ничего не делают сами по себе, они лишь реагируют на сообщения

Сообщение можно интерпретировать как приказ/задачу сделать что-либо. Дальше объект должен мочь что-то сделать сам по себе (отреагировать). Книга, как объект физической реальности, не способна никак отреагировать, хоть сколько ей ни приказывай. Вам придётся самому что-то с ней сделать, чтобы она как-то изменилась, не путём сообщений, а путём прямого физического воздействия.


Книга не может менять название? Расскажите об этом писателям.

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


P.S. Не воспринимайте всё так, как-будто я не знаком с ООП во всех его современных проявлениях. Я специально пишу в такой манере, чтобы Вы могли взглянуть на вещи под другим углом, скинув привычные шоры.

Information

Rating
3,193-rd
Location
Россия
Works in
Registered
Activity