Статьи

Инвертирующие функции: привязка потока эффектов для актеров без состояния

Функциональное программирование можно воспринимать как «сложное». Да, проводите время с ним, и это становится проще, а преимущества делают ваш код определенно лучше. Тем не менее, когда ошибки типов могут начать занимать несколько строк, это предполагает, что абстрактные понятия могут быть «трудными» для четкого понимания.

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

Теперь, когда функциональное программирование в значительной степени опирается на математику, я обращаю внимание на высказывание известного математика в начале 1800 года о том, как лучше понять сложные проблемы:

«Инверт, всегда инверт», математик Карл Якоби (хотя он и был немцем, так что на самом деле это был « человек, иммер »)

Я понимаю, что Карл Якоби говорил о том, что нужно перевернуть проблему с другой точки зрения, чтобы попытаться получить ответ. Поэтому, может быть, в инвертировании функционального программирования мы можем получить ясность в том, чтобы сделать его более «легким».

Так что же мы можем инвертировать в функции?

Мы инвертируем муфту.

Я написал об инверсии управления сцеплением в объектно-ориентированных парадигмах. Это даже до момента определения ОО Матрицы . Таким образом, есть 5 связей метода, которые ограничивают нас:

  1. Название метода
  2. Различное количество параметров
  3. Различное количество исключений
  4. Тип возврата
  5. Выполнение потока

Но какое это имеет отношение к функциональному программированию?

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

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

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

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

Мои рассуждения основаны на этих наблюдениях:

  • Актеры инкапсулируют изменения в State, чтобы сделать этот потокобезопасным
  • Эффекты могут переносить изменения в состояние (как внутри системы, так и снаружи).
  • Многопоточность обеспечивает более эффективное выполнение блокирующих эффектов (таких как ввод / вывод) *
  • Threading настроен на актера

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

Поэтому в моделировании акторов я вижу следующие отношения:


Тема (пул) -> Актер -> Государство
Когда это должно быть на самом деле:


Тема (пул) -> (Блокировка / Длительный запуск) Эффект
В попытке получить правильное разделение системы на актеров, мы должны одновременно:

  • Рассмотрим актера как эффект, чтобы назначить ему соответствующий поток (пул)
  • Разложить состояние системы на эти произвольные акторы

Теперь мы оказались в шаткой проблеме, пытаясь найти правильное сочетание логики, потоков и декомпозиции состояний. Это «сложная» проблема, чтобы получить право. Как только многопоточность становится вовлеченной, это немедленно становится проблемой только для средних и старших разработчиков. Мы больше не можем позволить младшим разработчикам помогать в системе (или, по крайней мере, они нуждаются в значительном контроле).

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

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

Это достигается следующим образом:

1
2
def Actor(m: Message, s: State, t: Thread): Array[Effect]
def Effect(r: Resource): AlteredState

Упрощение этого вниз:

1
def Actor(m: Message, s: State, t: Thread, r: Array[Resource]): AlteredState

Привет! Эффект был просто удален!

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

Мы принимаем решение по отображению потоков на основе ресурсов, используемых эффектом. Если для эффекта требуется подключение к базе данных, он может блокировать вызовы SQL. Если для эффекта требуется HTTP-клиент, он может блокировать HTTP-вызовы. Однако если Эффект использует только неблокирующие ресурсы, он вряд ли будет блокировать.

Теперь, если мы извлечем поток из вышеупомянутого актера, мы получим:

1
2
def ActorEffect(m: Message, s: State, r: Array[Resources]): AlteredState
def Actor(e: ActorEffect, t: Thread): AlteredState

Младший разработчик теперь свободен от потоков для написания ActorEffect.

Позже старший разработчик может сделать правильные сопоставления Thread с ActorEffect. Это связано с тем, что определение может быть сделано с помощью ресурсов (блокирующих или нет), используемых ActorEffect.

Кроме того, ActorEffect может быть записан как функция с переданным State. Это делает ActorEffect без состояний, поэтому младший разработчик не сильно вовлечен в безопасность потоков.

ActorEffect теперь может быть написано уровнем квалификации младшего разработчика.

Есть дальнейшие улучшения в объединении ActorEffects. В следующей статье приведен рабочий код того, как функции младшего уровня могут быть сплетены вместе с потоками, которые впоследствии настраиваются отдельно:

Сплетение вместе функций (и других парадигм)

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

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

См. Оригинальную статью здесь: Инвертирующие функции: привязка потока эффектов для актеров без состояния

Мнения, высказанные участниками Java Code Geeks, являются их собственными.