Хорошо, так что название немного щелкает мышью, но если серьезно, я был на высоте , не проси пинка некоторое время. Мне нравится, как он поощряет использование методов в качестве сообщений в истинно объектно-ориентированном виде. Но у меня есть ноющая проблема, которая гремит в моей голове.
Я подозреваю, что хорошо написанный код может следовать принципам ОО и принципам работы одновременно. Я пытаюсь примирить эти идеи, и большой камень преткновения, на котором я приземлился, - это return
.
Чистая функция имеет два качества:
Повторный вызов с одинаковыми входами всегда дает один и тот же результат. Это подразумевает, что он неизменен. Его состояние устанавливается только один раз.
Это не производит никаких побочных эффектов. Единственное изменение, вызванное вызовом, - это получение результата.
Итак, как же быть чисто функциональным, если вы поклялись использовать в return
качестве способа передачи результатов?
Телль, не проси идея работает, используя то , что некоторые рассмотрит побочный эффект. Когда я имею дело с объектом, я не спрашиваю его о его внутреннем состоянии. Я говорю ему, что мне нужно сделать, и он использует свое внутреннее состояние, чтобы выяснить, что делать с тем, что я сказал, чтобы сделать. Как только я говорю это, я не спрашиваю, что это сделало. Я просто ожидаю, что это сделало что-то с тем, что было сказано.
Я думаю о «Расскажи, не спрашивай» как о чем-то большем, чем просто другое имя для инкапсуляции. Когда я пользуюсь, return
я понятия не имею, как меня называют. Я не могу говорить это протокол, я должен заставить его иметь дело с моим протоколом. Что во многих случаях выражается как внутреннее состояние. Даже если то, что выставлено, не совсем в состоянии, это обычно просто некоторые вычисления, выполняемые для аргументов состояния и ввода. Наличие интерфейса, через который можно реагировать, дает возможность втиснуть результаты во что-то более значимое, чем внутреннее состояние или вычисления. Это передача сообщений . Смотрите этот пример .
Еще в тот день, когда на дисках фактически были диски, а в машине было то, что вы делали в машине, когда колесо было слишком холодным, чтобы прикасаться к нему пальцами, меня учили, как раздражающие люди считают функции, у которых есть параметры. void swap(int *first, int *second)
казалось очень удобным, но нам было предложено написать функции, которые возвращали результаты. Поэтому я принял это близко к сердцу на вере и начал следовать этому.
Но теперь я вижу людей, строящих архитектуры, где объекты позволяют тому, как они были построены, контролировать, куда они отправляют свои результаты. Вот пример реализации . Внедрение объекта выходного порта выглядит снова как идея с выходным параметром. Но именно так объекты «говорите, не спрашивайте» сообщают другим объектам о том, что они сделали.
Когда я впервые узнал о побочных эффектах, я подумал об этом как о выходном параметре. Нам сказали не удивлять людей тем, что некоторые работы происходят удивительным образом, то есть не следуя return result
конвенции. Теперь, конечно, я знаю, что есть куча параллельных асинхронных потоковых проблем, с которыми не справляются побочные эффекты, но return - это на самом деле просто соглашение, согласно которому вы оставляете результат помещенным в стек, так что при любом вызове вы можете удалить его позже. Это все, что есть на самом деле.
Что я действительно пытаюсь спросить:
Является ли return
это единственным способом избежать всех этих побочных эффектов и получить безопасность потоков без блокировок и т. Д. Или я могу сказать, что не спрашивайте чисто функциональным способом?
источник
Ответы:
Если функция не имеет побочных эффектов и ничего не возвращает, то функция бесполезна. Это так просто.
Но я думаю, что вы можете использовать некоторые читы, если хотите следовать букве правил и игнорировать основополагающие рассуждения. Например, использование параметра out, строго говоря, не использование возврата. Но он все равно делает то же самое, что и возвращение, только в более запутанном виде. Таким образом, если вы считаете, что возвращение плохо по какой-то причине , то использование параметра out явно плохо по тем же причинам.
Вы можете использовать более запутанные читы. Например, Haskell известен трюком с монадой IO, где вы можете получить побочные эффекты на практике, но, строго говоря, побочные эффекты не имеют теоретической точки зрения. Стиль прохождения продолжения - это еще один трюк, позволяющий избежать возврата за счет превращения вашего кода в спагетти.
Суть в том, что при отсутствии глупых трюков два принципа функций без побочных эффектов и «без возврата» просто несовместимы. Кроме того, я укажу, что оба они действительно плохие принципы (на самом деле, догмы), но это другое обсуждение.
Правила типа «скажи, не спрашивай» или «никаких побочных эффектов» не могут применяться повсеместно. Вы всегда должны учитывать контекст. Программа без побочных эффектов буквально бесполезна. Даже чисто функциональные языки признают это. Скорее они стремятся отделить чистые части кода от побочных эффектов. Смысл монад State или IO в Haskell не в том, что вы избегаете побочных эффектов - потому что вы не можете - но в том, что наличие побочных эффектов явно указывается сигнатурой функции.
Правило Tell-Dont-Ask применяется к другому типу архитектуры - стилю, в котором объекты в программе являются независимыми «участниками», взаимодействующими друг с другом. Каждый актер в основном автономен и инкапсулирован. Вы можете отправить ему сообщение, и оно решает, как на него реагировать, но вы не можете проверить внутреннее состояние актера извне. Это означает, что вы не можете сказать, меняет ли сообщение внутреннее состояние субъекта / объекта. Состояние и побочные эффекты скрыты дизайном .
источник
Скажите, не спрашивайте приходит с некоторыми фундаментальными предположениями:
Ничто из этого не относится к чистым функциям.
Итак, давайте рассмотрим, почему у нас есть правило «Скажи, не спрашивай». Это правило является предупреждением и напоминанием. Это можно обобщить так:
Другими словами, классы несут исключительную ответственность за поддержание своего собственного состояния и принятие мер в соответствии с ним. Вот что такое инкапсуляция.
От Фаулера :
Напомним, что ни одно из этого не имеет ничего общего с чистыми функциями или даже с нечистыми, если вы не подвергаете состояние класса внешнему миру. Примеры:
Нарушение TDA
Не нарушение ТДА
или же
В обоих последних примерах светофор сохраняет контроль над своим состоянием и действиями.
источник
Вы не только спрашиваете о его внутреннем состоянии , вы также не спрашиваете, есть ли у него внутреннее состояние .
Также скажи, не спрашивай! вовсе не означает , не получает результат в виде возвращаемого значения ( при условии , посредством
return
заявления внутри метода). Это просто означает, что мне все равно, как ты это делаешь, но делай эту обработку! , И иногда вы сразу же хотите получить результат обработки ...источник
next()
метод итераторов должен не только возвращать текущий объект, но и изменять внутреннее состояние итераторов, чтобы следующий вызов возвращал следующий объект ...Если вы считаете
return
«вредным» (чтобы оставаться в вашей картине), то вместо того, чтобы сделать функцию, каксоздайте его способом передачи сообщений:
Пока
f
иg
являются побочным эффектом свободный, сцепление их вместе будет побочный эффект , а также бесплатный. Я думаю, что этот стиль похож на то, что также называется стилем прохождения продолжения .Если это действительно приводит к «лучшим» программам является спорным, так как она нарушает некоторые соглашения. Немецкий инженер-программист Ральф Вестфаль создал целую модель программирования вокруг этого, он назвал ее «Компонентами на основе событий» с техникой моделирования, которую он называет «Flow Design».
Чтобы увидеть некоторые примеры, начните с раздела «Перевод в события» этой записи блога . Для полного подхода я рекомендую его электронную книгу «Обмен сообщениями как модель программирования - выполнение ООП, как если бы вы это имели в виду» .
источник
If this really leads to "better" programs is debatable
Нам нужно только взглянуть на код, написанный на JavaScript в течение первого десятилетия этого столетия. Jquery и его плагины были склонны к обратным вызовам этой парадигмы ... обратные вызовы везде . В определенный момент слишком много вложенных обратных вызовов делало отладку кошмаром. Код все еще должен быть прочитан людьми независимо от эксцентричности разработки программного обеспечения и ее «принципов»return
получать данные из функций и по-прежнему придерживаться режима «Не спрашивай», пока вы не управляете состоянием объекта.Передача сообщений по своей сути эффективна. Если вы скажете объекту что- то сделать , вы ожидаете, что он на что-то повлияет. Если обработчик сообщений был чистым, вам не нужно отправлять ему сообщение.
В распределенных системах акторов результат операции обычно отправляется в виде сообщения обратно отправителю исходного запроса. Отправитель сообщения либо неявно становится доступным во время выполнения субъекта, либо он (по соглашению) явно передается как часть сообщения. При синхронной передаче сообщений отдельный ответ сродни
return
утверждению. В асинхронной передаче сообщений использование ответных сообщений особенно полезно, так как оно позволяет выполнять параллельную обработку в нескольких субъектах, в то же время доставляя результаты.Передача «отправителя», которому результат должен быть доставлен явно, в основном моделирует стиль передачи продолжения или ужасные параметры - за исключением того, что он передает им сообщения, а не изменяет их напрямую.
источник
Весь этот вопрос мне кажется «нарушением уровня».
У вас есть (как минимум) следующие уровни в крупном проекте:
И так далее, вплоть до отдельных токенов.
На самом деле нет необходимости возвращать сущность на уровне метода / функции (даже если она просто возвращается
this
). И нет (в вашем описании) никакой необходимости в том, чтобы сущность на уровне Actor возвращала что-либо (в зависимости от языка, который может быть даже невозможен). Я думаю, что путаница заключается в объединении этих двух уровней, и я бы сказал, что они должны быть четко сформулированы (даже если какой-либо конкретный объект на самом деле охватывает несколько уровней).источник
Вы упоминаете, что хотите соответствовать как принципу ООП «говори, не спрашивай», так и принципу чистых функций, но я не совсем понимаю, как это привело вас к отказу от оператора return.
Относительно распространенный альтернативный способ следовать обоим этим принципам - пойти ва-банк в операторах return и использовать неизменяемые объекты только с геттерами. В этом случае подход заключается в том, чтобы некоторые из получателей возвращали похожий объект с новым состоянием, а не изменяли состояние исходного объекта.
Одним из примеров такого подхода является встроенный Python
tuple
иfrozenset
типы данных. Вот типичное использование Frozenset:В результате будет напечатано следующее, демонстрирующее, что метод union создает новый frozenset со своим собственным состоянием, не затрагивая старые объекты:
Другим обширным примером подобных неизменяемых структур данных является библиотека Immutable.js Facebook . В обоих случаях вы начинаете с этих строительных блоков и можете создавать доменные объекты более высокого уровня, которые следуют тем же принципам, достигая функционального подхода ООП, который помогает вам легче инкапсулировать данные и рассуждать о них. Кроме того, неизменность позволяет вам воспользоваться возможностью совместного использования таких объектов между потоками, не беспокоясь о блокировках.
источник
Я старался изо всех сил примирить некоторые из преимуществ, в частности, императивного и функционального программирования (естественно, не получая всех преимуществ, а пытаясь получить львиную долю обоих), хотя
return
на самом деле это фундаментально для простой способ для меня во многих случаях.Что касается попыток избежать
return
прямых заявлений, я пытался обдумать это в течение прошедшего часа или около того и, по сути, много раз переполнял мой мозг. Я могу видеть его привлекательность с точки зрения обеспечения наивысшего уровня инкапсуляции и сокрытия информации в пользу очень автономных объектов, которым просто говорят, что делать, и мне нравится исследовать конечности идей, только для того, чтобы попытаться получить лучшее понимание того, как они работают.Если мы воспользуемся примером светофора, то сразу наивная попытка захочет дать такому светофору информацию обо всем мире, который его окружает, и это, безусловно, было бы нежелательно с точки зрения связи. Поэтому, если я правильно понимаю, вы абстрагируете это и разъединяете в пользу обобщения концепции портов ввода / вывода, которые далее распространяют сообщения и запросы, а не данные, по конвейеру и в основном внедряют эти объекты с желаемыми взаимодействиями / запросами между собой. не обращая внимания друг на друга.
Узловой трубопровод
И эта диаграмма о том, насколько я пытался набросать это (и хотя я был прост, мне приходилось все время менять и переосмысливать). Сразу я склоняюсь к мысли, что дизайн с таким уровнем развязки и абстракции будет очень трудно рассуждать в форме кода, потому что оркестраторы, которые связывают все эти вещи для сложного мира, могут столкнуться с трудностями. отслеживать все эти взаимодействия и запросы для создания желаемого конвейера. В визуальной форме, однако, может быть достаточно просто нарисовать эти вещи в виде графика и связать все вместе и увидеть, как все происходит в интерактивном режиме.
С точки зрения побочных эффектов, я мог бы видеть, что это освобождает от «побочных эффектов» в том смысле, что эти запросы могут в стеке вызовов приводить к цепочке команд для каждого выполняемого потока, например (я не считаю это как «побочный эффект» в прагматическом смысле, поскольку он не изменяет состояния, относящиеся к внешнему миру, до тех пор, пока такие команды не будут фактически выполнены - для меня в большинстве программ практическая цель - не устранить побочные эффекты, а отложить и централизовать их) , Более того, выполнение команды может создать новый мир, а не изменять существующий. Мой мозг действительно обременителен, просто пытаясь понять все это, однако, без каких-либо попыток прототипирования этих идей. Я тоже не
Как это устроено
Поэтому, чтобы уточнить, я представлял, как вы на самом деле программируете это. На самом деле, я видел, как это работает, на диаграмме выше, отражающей рабочий процесс на стороне пользователя (программиста). Вы можете перетащить светофор в мир, перетащить таймер, дать ему истекший период (после «постройки»). Таймер имеет
On Interval
событие (выходной порт), вы можете подключить его к светофору, чтобы при таких событиях он велел свету переключаться между его цветами.Затем при переключении на определенные цвета светофор может испускать выходные сигналы (события), например,
On Red
в этот момент мы можем перетащить пешехода в наш мир и заставить это событие сказать пешеходу, что он должен идти ... или мы можем втянуть птиц в наша сцена и сделать так, чтобы, когда свет стал красным, мы говорим птицам начать летать и взмахнуть крыльями ... или, может быть, когда свет станет красным, мы скажем бомбе взорваться - все, что мы хотим, и с объектами, являющимися совершенно не обращая внимания друг на друга и ничего не делая, но косвенно говоря друг другу, что делать с помощью этой абстрактной концепции ввода / вывода.И они полностью инкапсулируют свое состояние и ничего о нем не раскрывают (если только эти «события» не рассматриваются как TMI, и в этот момент мне придется многое переосмыслить), они говорят друг другу, что делают косвенно, а не спрашивают. И они уберемся от развязки. Ничто не знает ни о чем, кроме этой обобщенной абстракции порта ввода / вывода.
Случаи практического использования?
Я мог видеть, что этот тип вещей полезен как высокоуровневый специфичный для домена встроенный язык в определенных доменах для организации всех этих автономных объектов, которые ничего не знают об окружающем мире, ничего не раскрывают об их внутреннем состоянии после создания и в основном просто распространяют запросы друг с другом, которые мы можем изменить и настроить в соответствии с содержанием наших сердец. В данный момент я чувствую, что это очень специфично для предметной области, или, может быть, я просто недостаточно обдумал это, потому что мне очень трудно обернуть свой мозг типами вещей, которые я регулярно разрабатываю (я часто работаю с скорее код среднего уровня), если бы я интерпретировал «Скажи, не спрашивай» о таких конечностях и хотел бы получить самый сильный уровень инкапсуляции, какой только можно себе представить. Но если мы работаем с высокоуровневыми абстракциями в определенной области,
Сигналы и слоты
Этот дизайн выглядел странно знакомым для меня, пока я не понял, что это в основном сигналы и слоты, если не принимать во внимание нюансы того, как он реализован. Главный вопрос для меня заключается в том, насколько эффективно мы можем запрограммировать эти отдельные узлы (объекты) в графе как строго соблюдающие принцип «Говори, не спрашивай», доведенные до степени избегания
return
утверждений, и можем ли мы оценить указанный граф без мутаций (в параллельно, например, отсутствует блокировка). Вот в чем заключаются магические преимущества не в том, как мы связываем эти вещи вместе, а в том, как они могут быть реализованы до такой степени инкапсуляции, при отсутствии мутаций. Оба из них кажутся мне выполнимыми, но я не уверен, насколько широко это будет применимо, и вот где я немного озадачен, пытаясь проработать возможные варианты использования.источник
Я ясно вижу утечку уверенности здесь. Кажется, что «побочный эффект» является хорошо известным и общепринятым термином, но на самом деле это не так. В зависимости от ваших определений (которые на самом деле отсутствуют в OP), побочные эффекты могут быть абсолютно необходимыми (как удалось объяснить @JacquesB) или беспощадно неприемлемыми. Или, делая один шаг к разъяснению, необходимо провести различие между желаемыми побочными эффектами, которые никто не любит скрывать (в этот момент появляется известный ввод-вывод Хаскелла: это не что иное, как способ быть явным) и нежелательными побочными эффектами как результат ошибок в коде и тому подобное . Это довольно разные проблемы и поэтому требуют разных рассуждений.
Итак, я предлагаю начать с перефразирования себя: «Как мы определяем побочный эффект и что говорят данные определения о его взаимосвязи с утверждением« return »?».
источник