Pull to refresh
12
0
Send message
А в Модели Акторов интерфейсы выражаются через перечисление сообщений.

Именно! Только в модели акторов нет интерфейсов, и сообщения нигде не перечислены. Ну, кроме как в исходном коде самого актора.

Ну и зачем это нужно, если temperature_sensors и risk_managers и так будут вынуждены приспосабливаться к интерфейсу connection_pool-а на уровне сообщений acquire, release, take и failure?

Ровно затем же, зачем нужна типобезопасность и интерфейсы в классическом ООП: там ведь тоже объекты вынуждены приспосабливаться к интерфейсам других объектов, посредством вызова методов друг друга. Так зачем интерфейсы, если и без них объект A может вызвать метод acquire у объекта B? Сообщения — это по сути те же методы. Только в интерфейсе они нигде не прописаны. В общем, эти все вопросы из разряда PHP vs Java. В PHP можно сделать $anyVar->anyMethod(), и получить ошибку на этапе исполнения, а Java этого не пропустит еще на этапе компиляции.


P.S. Прошу прощения за раздробленный ответ.

В данном случае, A1 и A2 — это всего лишь интерфейсы, которые говорят о том, что эти акторы могут принимать сообщения M1 и M2 соответсвтенно. То есть, если завтра вашему connection_pool-у захочет отправлять сообщения другой актор — он просто реализует необходимый интерфейс для получения типизированных ответов от connection_pool-а. При этом, в самом connection_pool ничего не нужно менять, так как сообщения-ответы не меняются и интерфейс любого получателя ему известен заранее.

А зачем ее решать на этапе компиляции?
А зачем это нужно? В каких практических задачах это может потребоваться?

Все это нужно для обеспечения типобезопасности. Все эти вопросы Akka пытается решить. Раз пытается — значит, видит необходимость.


Введение требований к типам агентов-consumer-ов приведет к тому...

Это ведь не совсем ограничение на тип, это ограничение на реализуемый ими интерфейс. В Akka приводится пример реализации протокола, когда в ответ на вот такое сообщение актор может отправить вот такое сообщение, а следом за этим сообщением "тип" (интерфейс) актора меняется на вот такой (то есть он уже может получать-отправлять другие сообщения). Протокол ограничен типами с двух сторон, то есть, актор A1, получая сообщение типа M1, уверен на этапе компиляции, что его ему отправил актор A2, которому можно ответить сообщением M2. Как-то так.


Добавлено: отправка сообщения типизированному актору — это же ведь только половина задачи типизации. Актор же должен иметь возможноть точно так же типобезопасным способом на это сообщение ответить.

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

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


По поводу ответов — мне это таким уж тривиальным не кажется. Есть актор A, он получил сообщение M. От кого он его получил? Какой тип имеет отправитель этого сообщения? Какие сообщения он может принимать в ответ? Гарантировать тип отправителя можно, например, в том случае, если "зашить" его в протокол, то есть ограничить интерфейс с двух сторон: что вот это сообщение может приходить только от такого типа акторов, и только такому типу акторов. В Akka этот механизм тоже претерпевает эволюционные изменения, и об этом по приведенным ссылкам тоже упоминается.

Ну так я же привел выше 2 ссылки, там даже с примерами кода на Scala. Там же и для Java, думаю, можно найти аналогичные разделы в доках, если есть необходимость. А в двух словах: если поведение актора меняется (в ответ на сообщение) — то в типизированном интерфейсе это должно как-то отражаться. А типизированный sender: актору должен быть известен тип отправителя, когда он получает от кого-то сообщение. Для ответа, например.

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

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

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

вы на своем опыте поимели много неприятностей из-за нетипизированности акторов?

Да, иначе бы я об этом не писал. Статья же о шишках — вот я своих еще в копилку подбросил. Могу вспомнить пару-тройку неудобств из-за отсутсвия типизированного интерфейса у акторов:


  1. Отсутствие подсказок в IDE. Что приводит к частой необходимости лезть либо в исходный код актора, либо в документацию (если это сторонняя библиотека, и/или поведение в исходном коде не очевидно). Это ощутимо замедляет процесс разработки.
  2. Дополнительные тесты на то, что актор не отправляет ничего "левого" другому актору. С типизированными интерфейсами это невозможно "by design". Опять разработка замедляется: часть работы компилятора перекладывается на программиста.
  3. С эволюцией приложения какой-то компонент обновляется, какие-то эти обновления подхватывают, а какие-то обновить забыли — и они продолжают использовать старый "интерфейс". Компилятор это не отлавливает, приходится сталкиваться с последствиями в рантайме и долго и нудно разгребать логи, чтобы понять, где причина. А ошибки рантайма, как известно, гораздо более дорого обходятся в конечном счете. С типизированными интерфейсами вероятность такой ситуации — минимальная. Разве что если в методе два однотипных аргумента зачем-то местами поменять.

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

Язык, фреймворк и психическая адекватность программиста. А так-то да, кошмар сплошной кругом. Бывает, булочку в магазин выйдешь купить — а где гарантия, что то вообще булочка, а не рогалик, а за прилавком не марсианин переодетый, а человек?

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

Во-первых, кому попало все равно можно отправить.

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


В общем, типизированные акторы не на ровном месте появились, и развитие в этом направлении продолжается.

Статическая типизация никуда не девается.

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

Это вы вообще все в кучу смешали. Если уж вашими категориями рассуждать — то где вообще гарантия, что программист Василий написал программу, а не ушел пить кофе и играться в приставку?


Гарантия доставки сообщений — это отдельная история. Причем тут она к статической типизации?


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


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

А как вы планируете типами гарантировать, что актор послал сообщения тем кому «должен» послать и от кого «должен» принять? Может все таки не «должен», а «может»? )

  1. Таких гарантий нет ни в одном приложении. Это все зависит только от разработчика. Он может забыть что угодно. В одном месте добавил метод, в другом забыл его «дернуть».
  2. Таких гарантий тоже нет ни в одном приложении, см п 2.

Про интерфейсы в ООП не слышали? А про статическую типизацию? То и другое вместе дает целый ряд определенных гарантий. Модель акторов просто лишает разработчика всего вот этого. В Akka, например, для решения этой проблемы существуют Typed Actors (которые сейчас в процессе замещения проектом Typed Akka). По сути, из актора получается все тот же объект из ООП, со строго типизированным интерфейсом, дающим определенные гарантии, но с асинхронным вызовом методов (с пересылкой сообщений, асинхронными ответами, тайм-аутами и прочими акторскими плюшками "под капотом"). И это гарантирует на этапе компиляции, что сообщение будет отправлено нужному агенту, а не кому попало. Иначе приложение просто не скомпилируется. А если агент на каком-то этапе разработки приложения перестанет обрабатывать сообщения какого-то типа — то из его интерфейса пропадет соответствующий метод, и компилятор об это сообщит, и покажет все места, в которых кто-то все еще пытается этот метод дергать.

Это вроде как в 2.13/Dotty обещают зарелизить. Первый майлстоун уже в апреле!

Я так понимаю что Мартин хотел бы чтобы мы использовали для этого case classы.

С ними есть несколько проблем: они не так наглядны (потому что объявлены где-то еще, а не там, где используются), приходится выделять дополнительное место для их объявления (companion object, например, или package object), их со временем накапливается очень много, и им нужно придумывать какие-то названия, которые иногда сходу бывает придумать довольно трудно (когда, например, есть несколько функций, которые возвращают кортежи, которые во многом похожи, но чуть-чуть отличаются). И это все сильно тормозит рабочий процесс.


Это как если бы в Скале не поддерживались лямбды, и все функции приходилось бы где-то объявлять и как-то называть. Несправедливо же: анонимные классы есть, анонимные функции/лямбды есть, а анонимных кортежей с именованными полями — нет.


Я думаю что для начала можно посмотреть на сгенерированный scalac-ом код. Если оптимизации и происходят, то скорее всего так.

Да, так и есть, я сначала отправил коммент, а потом об этом подумал.

Information

Rating
Does not participate
Location
Киев, Киевская обл., Украина
Registered
Activity