Возвращение считается вредным? Может ли код быть функциональным без него?

45

Хорошо, так что название немного щелкает мышью, но если серьезно, я был на высоте , не проси пинка некоторое время. Мне нравится, как он поощряет использование методов в качестве сообщений в истинно объектно-ориентированном виде. Но у меня есть ноющая проблема, которая гремит в моей голове.

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

Чистая функция имеет два качества:

  1. Повторный вызов с одинаковыми входами всегда дает один и тот же результат. Это подразумевает, что он неизменен. Его состояние устанавливается только один раз.

  2. Это не производит никаких побочных эффектов. Единственное изменение, вызванное вызовом, - это получение результата.

Итак, как же быть чисто функциональным, если вы поклялись использовать в returnкачестве способа передачи результатов?

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

Я думаю о «Расскажи, не спрашивай» как о чем-то большем, чем просто другое имя для инкапсуляции. Когда я пользуюсь, returnя понятия не имею, как меня называют. Я не могу говорить это протокол, я должен заставить его иметь дело с моим протоколом. Что во многих случаях выражается как внутреннее состояние. Даже если то, что выставлено, не совсем в состоянии, это обычно просто некоторые вычисления, выполняемые для аргументов состояния и ввода. Наличие интерфейса, через который можно реагировать, дает возможность втиснуть результаты во что-то более значимое, чем внутреннее состояние или вычисления. Это передача сообщений . Смотрите этот пример .

Еще в тот день, когда на дисках фактически были диски, а в машине было то, что вы делали в машине, когда колесо было слишком холодным, чтобы прикасаться к нему пальцами, меня учили, как раздражающие люди считают функции, у которых есть параметры. void swap(int *first, int *second)казалось очень удобным, но нам было предложено написать функции, которые возвращали результаты. Поэтому я принял это близко к сердцу на вере и начал следовать этому.

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

Когда я впервые узнал о побочных эффектах, я подумал об этом как о выходном параметре. Нам сказали не удивлять людей тем, что некоторые работы происходят удивительным образом, то есть не следуя return resultконвенции. Теперь, конечно, я знаю, что есть куча параллельных асинхронных потоковых проблем, с которыми не справляются побочные эффекты, но return - это на самом деле просто соглашение, согласно которому вы оставляете результат помещенным в стек, так что при любом вызове вы можете удалить его позже. Это все, что есть на самом деле.

Что я действительно пытаюсь спросить:

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

candied_orange
источник
3
Если вы решите не игнорировать разделение командных запросов, считаете ли вы, что ваша проблема решена?
rwong
30
Учтите, что попадание в ногу может указывать на то, что вы занимаетесь догматическим дизайном, а не размышлять о плюсах и минусах каждой конкретной ситуации.
Blrfl
3
В общем, когда люди говорят о «возврате», который вреден, они говорят, что это против структурированного программирования, а не функционального, и одного оператора возврата в конце процедуры (и, возможно, в конце обеих сторон блока if / else, который сам по себе последний элемент) не входит в это.
Random832
16
@jameslarge: Ложная дихотомия. Не позволять себе увлекаться замыслами с помощью догматического мышления - это не то же самое, что Дикий Запад / все идет так, как вы говорите. Дело в том, чтобы не позволить догме помешать хорошему, простому, очевидному коду. Всеобщий закон для лакеев; контекст для королей.
Никол Болас,
4
Для меня есть одно, что неясно в вашем вопросе: вы говорите, что дали клятву, используя «возврат», но, насколько я могу судить, вы явно не связываете это с вашими другими концепциями и не говорите, почему вы это сделали. В сочетании с определением чистой функции, включая создание результата, вы создаете нечто, что невозможно решить.
Даников

Ответы:

96

Если функция не имеет побочных эффектов и ничего не возвращает, то функция бесполезна. Это так просто.

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

Вы можете использовать более запутанные читы. Например, Haskell известен трюком с монадой IO, где вы можете получить побочные эффекты на практике, но, строго говоря, побочные эффекты не имеют теоретической точки зрения. Стиль прохождения продолжения - это еще один трюк, позволяющий избежать возврата за счет превращения вашего кода в спагетти.

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

Правила типа «скажи, не спрашивай» или «никаких побочных эффектов» не могут применяться повсеместно. Вы всегда должны учитывать контекст. Программа без побочных эффектов буквально бесполезна. Даже чисто функциональные языки признают это. Скорее они стремятся отделить чистые части кода от побочных эффектов. Смысл монад State или IO в Haskell не в том, что вы избегаете побочных эффектов - потому что вы не можете - но в том, что наличие побочных эффектов явно указывается сигнатурой функции.

Правило Tell-Dont-Ask применяется к другому типу архитектуры - стилю, в котором объекты в программе являются независимыми «участниками», взаимодействующими друг с другом. Каждый актер в основном автономен и инкапсулирован. Вы можете отправить ему сообщение, и оно решает, как на него реагировать, но вы не можете проверить внутреннее состояние актера извне. Это означает, что вы не можете сказать, меняет ли сообщение внутреннее состояние субъекта / объекта. Состояние и побочные эффекты скрыты дизайном .

JacquesB
источник
20
@CandiedOrange: есть ли у метода побочные эффекты прямо или косвенно, хотя многие уровни вызовов ничего не изменяют концептуально. Это все еще побочный эффект. Но если дело в том, что побочные эффекты происходят только через инъецированные объекты, поэтому вы можете контролировать, какие побочные эффекты возможны, тогда это звучит как хороший дизайн. Это просто не побочные эффекты бесплатно. Это хорошо ОО, но не чисто функционально.
JacquesB
12
@LightnessRacesinOrbit: у grep в тихом режиме есть код возврата процесса, который будет использоваться. Если бы этого не было, то это было бы действительно бесполезно
unperson325680
10
@progo: код возврата не является побочным эффектом; это эффект. Все, что мы называем побочным эффектом, называется побочным эффектом, потому что это не код возврата;)
Легкость мчится с Моникой
8
@ Кубик, в этом все дело. Программа без побочных эффектов бесполезна.
Йенс Шаудер
5
@mathreadler Это побочный эффект :)
Андрес Ф.
32

Скажите, не спрашивайте приходит с некоторыми фундаментальными предположениями:

  1. Вы используете объекты.
  2. Ваши объекты имеют состояние.
  3. Состояние ваших объектов влияет на их поведение.

Ничто из этого не относится к чистым функциям.

Итак, давайте рассмотрим, почему у нас есть правило «Скажи, не спрашивай». Это правило является предупреждением и напоминанием. Это можно обобщить так:

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

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

От Фаулера :

Tell-Don't-Ask - это принцип, который помогает людям помнить, что объектная ориентация заключается в объединении данных с функциями, которые работают с этими данными. Это напоминает нам, что вместо того, чтобы запрашивать у объекта данные и действовать на основе этих данных, мы должны вместо этого сказать объекту, что делать. Это побуждает нас перемещать поведение в объект, чтобы идти с данными.

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

Нарушение TDA

var color = trafficLight.Color;
var elapsed = trafficLight.Elapsed;
If (color == Color.Red && elapsed > 2.Minutes)
    trafficLight.ChangeColor(green);

Не нарушение ТДА

var result = trafficLight.ChangeColor(Color.Green);

или же

var result = await trafficLight.ChangeColorWhenReady(Color.Green);     

В обоих последних примерах светофор сохраняет контроль над своим состоянием и действиями.

Роберт Харви
источник
Подождите секунду, закрытия могут быть чистыми. у них есть состояние (они называют это лексической областью). и эта лексическая область может повлиять на их поведение. Вы уверены, что TDA имеет отношение только к объектам?
candied_orange
7
@CandiedOrange замыкания являются чистыми, только если вы не изменяете закрытые привязки. В противном случае вы теряете ссылочную прозрачность при вызове функции, возвращаемой из замыкания.
Джаред Смит
1
@JaredSmith и не то же самое, когда вы говорите об объектах? Разве это не просто проблема неизменности?
candied_orange
1
@bdsl - И теперь вы случайно указали, куда каждое обсуждение этого типа идет с вашим примером trafficLight.refreshDisplay. Если вы будете следовать правилам, вы получите очень гибкую систему, которую никто, кроме оригинального кодера, не понимает. Я бы даже поспорил, что после пары лет перерыва даже оригинальный кодер, вероятно, тоже не поймет, что они сделали.
Данк
1
«Глупо», как в «Не открывайте другие объекты и не смотрите на их кишки, а вместо этого проливайте свои собственные кишки (если они у вас есть) в методы других объектов;
Joker_vD
30

Когда я имею дело с объектом, я не спрашиваю его о его внутреннем состоянии. Я говорю ему, что мне нужно сделать, и он использует свое внутреннее состояние, чтобы выяснить, что делать с тем, что я сказал, чтобы сделать.

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

Также скажи, не спрашивай! вовсе не означает , не получает результат в виде возвращаемого значения ( при условии , посредством returnзаявления внутри метода). Это просто означает, что мне все равно, как ты это делаешь, но делай эту обработку! , И иногда вы сразу же хотите получить результат обработки ...

Тимоти Тракл
источник
1
CQS подразумевает, что изменение состояния и получение результатов должны быть разделены
jk.
7
@jk. Как обычно: обычно вы должны разделять изменение состояния и возвращать результат, но в редких случаях есть веские причины для объединения этого. Например: next()метод итераторов должен не только возвращать текущий объект, но и изменять внутреннее состояние итераторов, чтобы следующий вызов возвращал следующий объект ...
Тимоти Траклле
4
Именно так. Я думаю, что проблема ОП заключается просто в недопонимании / неправильном применении «говори, не спрашивай». И исправление этого недоразумения устраняет проблему.
Конрад Рудольф
@KonradRudolph Plus, я не думаю, что это единственное недоразумение здесь. Их описание «чистой функции» включает в себя «Ее состояние устанавливается только один раз». Другой комментарий указывает, что это может означать контекст замыкания, но эта фраза звучит странно для меня.
Иската,
17

Если вы считаете return«вредным» (чтобы оставаться в вашей картине), то вместо того, чтобы сделать функцию, как

ResultType f(InputType inputValue)
{
     // ...
     return result;
}

создайте его способом передачи сообщений:

void f(InputType inputValue, Action<ResultType> g)
{
     // ...
     g(result);
}

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

Если это действительно приводит к «лучшим» программам является спорным, так как она нарушает некоторые соглашения. Немецкий инженер-программист Ральф Вестфаль создал целую модель программирования вокруг этого, он назвал ее «Компонентами на основе событий» с техникой моделирования, которую он называет «Flow Design».

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

Док Браун
источник
24
If this really leads to "better" programs is debatableНам нужно только взглянуть на код, написанный на JavaScript в течение первого десятилетия этого столетия. Jquery и его плагины были склонны к обратным вызовам этой парадигмы ... обратные вызовы везде . В определенный момент слишком много вложенных обратных вызовов делало отладку кошмаром. Код все еще должен быть прочитан людьми независимо от эксцентричности разработки программного обеспечения и ее «принципов»
Laiv
1
даже тогда в какой-то момент вам нужно предоставить действие, которое выполняет побочный эффект, или вам нужен какой-то способ вернуться из части CPS
jk.
12
@Laiv CPS была изобретена как технология оптимизации для компиляторов, и никто не ожидал, что программисты напишут код вручную.
Joker_vD
3
@CandiedOrange В форме слогана «возвращение - это просто продолжение, что делать». Действительно, создание схемы было мотивировано попыткой понять модель актера Хьюитта и пришло к выводу, что актеры и замыкания - это одно и то же.
Дерек Элкинс
2
Гипотетически, конечно, вы могли бы написать все ваше приложение как серию вызовов функций, которые ничего не возвращают. Если вы не против того, чтобы он был тесно связан, вам даже не нужны порты. Но большинство вменяемых приложений используют функции, которые возвращают вещи, потому что ... ну, они вменяемые. И, как я полагаю, я адекватно продемонстрировал в своем ответе, вы можете returnполучать данные из функций и по-прежнему придерживаться режима «Не спрашивай», пока вы не управляете состоянием объекта.
Роберт Харви
7

Передача сообщений по своей сути эффективна. Если вы скажете объекту что- то сделать , вы ожидаете, что он на что-то повлияет. Если обработчик сообщений был чистым, вам не нужно отправлять ему сообщение.

В распределенных системах акторов результат операции обычно отправляется в виде сообщения обратно отправителю исходного запроса. Отправитель сообщения либо неявно становится доступным во время выполнения субъекта, либо он (по соглашению) явно передается как часть сообщения. При синхронной передаче сообщений отдельный ответ сродни returnутверждению. В асинхронной передаче сообщений использование ответных сообщений особенно полезно, так как оно позволяет выполнять параллельную обработку в нескольких субъектах, в то же время доставляя результаты.

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

Берги
источник
5

Весь этот вопрос мне кажется «нарушением уровня».

У вас есть (как минимум) следующие уровни в крупном проекте:

  • Уровень системы, например, платформа электронной коммерции
  • Уровень подсистемы, например, проверка пользователя: сервер, AD, интерфейс
  • Уровень отдельной программы, например, один из компонентов в приведенном выше
  • Уровень актера / модуля [становится темным в зависимости от языка]
  • Метод / функциональный уровень.

И так далее, вплоть до отдельных токенов.

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

Джаред Смит
источник
2

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

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

Одним из примеров такого подхода является встроенный Python tupleи frozensetтипы данных. Вот типичное использование Frozenset:

small_digits = frozenset([0, 1, 2, 3, 4])
big_digits = frozenset([5, 6, 7, 8, 9])
all_digits = small_digits.union(big_digits)

print("small:", small_digits)
print("big:", big_digits)
print("all:", all_digits)

В результате будет напечатано следующее, демонстрирующее, что метод union создает новый frozenset со своим собственным состоянием, не затрагивая старые объекты:

small: frozenset ({0, 1, 2, 3, 4})

большой: фрозенцет ({5, 6, 7, 8, 9})

все: Frozenset ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

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

yoniLavi
источник
1

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

Я старался изо всех сил примирить некоторые из преимуществ, в частности, императивного и функционального программирования (естественно, не получая всех преимуществ, а пытаясь получить львиную долю обоих), хотя returnна самом деле это фундаментально для простой способ для меня во многих случаях.

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

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

Узловой трубопровод

введите описание изображения здесь

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

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

Как это устроено

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

Затем при переключении на определенные цвета светофор может испускать выходные сигналы (события), например, On Redв этот момент мы можем перетащить пешехода в наш мир и заставить это событие сказать пешеходу, что он должен идти ... или мы можем втянуть птиц в наша сцена и сделать так, чтобы, когда свет стал красным, мы говорим птицам начать летать и взмахнуть крыльями ... или, может быть, когда свет станет красным, мы скажем бомбе взорваться - все, что мы хотим, и с объектами, являющимися совершенно не обращая внимания друг на друга и ничего не делая, но косвенно говоря друг другу, что делать с помощью этой абстрактной концепции ввода / вывода.

И они полностью инкапсулируют свое состояние и ничего о нем не раскрывают (если только эти «события» не рассматриваются как TMI, и в этот момент мне придется многое переосмыслить), они говорят друг другу, что делают косвенно, а не спрашивают. И они уберемся от развязки. Ничто не знает ни о чем, кроме этой обобщенной абстракции порта ввода / вывода.

Случаи практического использования?

Я мог видеть, что этот тип вещей полезен как высокоуровневый специфичный для домена встроенный язык в определенных доменах для организации всех этих автономных объектов, которые ничего не знают об окружающем мире, ничего не раскрывают об их внутреннем состоянии после создания и в основном просто распространяют запросы друг с другом, которые мы можем изменить и настроить в соответствии с содержанием наших сердец. В данный момент я чувствую, что это очень специфично для предметной области, или, может быть, я просто недостаточно обдумал это, потому что мне очень трудно обернуть свой мозг типами вещей, которые я регулярно разрабатываю (я часто работаю с скорее код среднего уровня), если бы я интерпретировал «Скажи, не спрашивай» о таких конечностях и хотел бы получить самый сильный уровень инкапсуляции, какой только можно себе представить. Но если мы работаем с высокоуровневыми абстракциями в определенной области,

Сигналы и слоты

Этот дизайн выглядел странно знакомым для меня, пока я не понял, что это в основном сигналы и слоты, если не принимать во внимание нюансы того, как он реализован. Главный вопрос для меня заключается в том, насколько эффективно мы можем запрограммировать эти отдельные узлы (объекты) в графе как строго соблюдающие принцип «Говори, не спрашивай», доведенные до степени избегания returnутверждений, и можем ли мы оценить указанный граф без мутаций (в параллельно, например, отсутствует блокировка). Вот в чем заключаются магические преимущества не в том, как мы связываем эти вещи вместе, а в том, как они могут быть реализованы до такой степени инкапсуляции, при отсутствии мутаций. Оба из них кажутся мне выполнимыми, но я не уверен, насколько широко это будет применимо, и вот где я немного озадачен, пытаясь проработать возможные варианты использования.

Энергия Дракона
источник
0

Я ясно вижу утечку уверенности здесь. Кажется, что «побочный эффект» является хорошо известным и общепринятым термином, но на самом деле это не так. В зависимости от ваших определений (которые на самом деле отсутствуют в OP), побочные эффекты могут быть абсолютно необходимыми (как удалось объяснить @JacquesB) или беспощадно неприемлемыми. Или, делая один шаг к разъяснению, необходимо провести различие между желаемыми побочными эффектами, которые никто не любит скрывать (в этот момент появляется известный ввод-вывод Хаскелла: это не что иное, как способ быть явным) и нежелательными побочными эффектами как результат ошибок в коде и тому подобное . Это довольно разные проблемы и поэтому требуют разных рассуждений.

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

Сережа Боголюбов
источник
1
«в этот момент возникает известный ввод-вывод Хаскелла: это не что иное, как способ быть явным» - явность, безусловно, является преимуществом монадического ввода-вывода Хаскелла, но есть еще один момент: он дает возможность изолировать побочный эффект, чтобы быть полностью вне языка - хотя обычные реализации на самом деле этого не делают, в основном из-за проблем с эффективностью, концептуально это так: монаду IO можно рассматривать как способ возврата инструкции в некоторую среду, которая полностью находится за пределами Программа на Haskell вместе со ссылкой на функцию продолжится после завершения.
Жюль