Подтвердить что ты не робот

Хорошие ли побочные эффекты?

Я считаю этот термин довольно уничижительным. Следовательно, я ошеломлен двумя предложениями в Википедии:

Императивное программирование известно использование побочных эффектов для программ. функциональная программирование в свою очередь известно своим минимизация побочных эффектов. [1]

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

4b9b3361

Ответ 1

Каждый так часто я вижу вопрос о SO, который заставляет меня потратить полчаса на редактирование очень плохой статьи в Википедии. Статья теперь только умеренно плохая. В той части, которая касается вашего вопроса, я написал следующее:

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

Побочные эффекты необходимы, чтобы программа могла взаимодействовать с внешним миром (люди, файловые системы, другие компьютеры в сетях). Но степень использования побочных эффектов зависит от парадигмы программирования. Императивное программирование известно неконтролируемым, беспорядочным использованием побочных эффектов. В функциональном программировании побочные эффекты редко используются. Функциональные языки, такие как Standard ML и Scheme, не ограничивают побочные эффекты, но для программистов принято избегать их. Функциональный язык Haskell ограничивает побочные эффекты системой статического типа; только функция, которая производит результат типа IO, может иметь побочные эффекты.

Ответ 2

Побочные эффекты - это необходимое зло, и нужно стремиться минимизировать/локализовать их.

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

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

Ответ 3

В машинах von-Neumann побочные эффекты - это то, что заставляет машину работать. По сути, независимо от того, как вы пишете свою программу, для работы (на низком уровне) придется выполнять побочные эффекты.

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

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

Ответ 4

Pro:

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

Con:

  • Чистый код легко распараллеливается.
  • Побочные эффекты могут сделать код сложным.
  • Чистый код проще доказать правильность.

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

Ответ 5

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

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

Ответ 6

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

Как оружие, у вас есть побочные эффекты всех разных видов различной степени летальности.

В С++ побочные эффекты абсолютно не ограничены, благодаря указателям. Если переменная объявлена ​​как "private", вы все равно можете ее получить или изменить, используя трюки указателя. Вы даже можете изменять переменные, которые не входят в область видимости, например параметры и локали вызывающей функции. С небольшой помощью от операционной системы (mmap) вы можете даже изменить свой программный код во время выполнения! Когда вы пишете на языке, таком как С++, вы возводитесь в ранг Бит Бога, владеете всей памятью в своем процессе. Все оптимизации, которые компилятор делает для вашего кода, сделаны с предположением, что вы не злоупотребляете своими полномочиями.

В Java ваши способности более ограничены. Все переменные в области находятся под вашим контролем, включая переменные, разделяемые разными потоками, но вы всегда должны придерживаться системы типов. Тем не менее, благодаря подсистеме ОС, находящейся в вашем распоряжении, и наличию статических полей, ваш код может иметь нелокальные эффекты. Если отдельный поток как-то закрывает System.out, он будет казаться магии. И это будет волшебство: боковая эффектная магия.

Haskell (несмотря на пропаганду о чистоте) имеет монаду IO, которая требует, чтобы вы регистрировали все свои побочные эффекты с помощью системы типов. Упаковка вашего кода в монаде IO похожа на 3-дневный период ожидания для пистолетов: вы все равно можете удалять свою собственную ногу, но пока вы не будете в порядке с правительством. Там также небезопасноPerformIO и его ilk, которые являются черным рынком Haskell IO, давая вам побочные эффекты, "без вопросов".

Миранда, предшественник Haskell, - это чистый функциональный язык, созданный до того, как монады стали популярными. Миранда (насколько я узнал... если я ошибаюсь, замените Lambda Calculus) вообще не имеет примитивов ввода-вывода. Единственное выполнение ввода-вывода - это компиляция программы (вход) и запуск программы и печать результата (выход). Здесь у вас полная чистота. Порядок исполнения совершенно неактуальен. Все "эффекты" являются локальными для функций, объявляющих их, что не означает, что две непересекающиеся части кода влияют друг на друга. Это утопия (для математиков). Или, что то же самое, дистра. Это скучно. Ничего не происходит. Вы не можете создать для него сервер. Вы не можете записать в нее ОС. Вы не можете писать SNAKE или Tetris. Все просто выглядят математически.

Ответ 7

Жюри все еще не работает. И процесс проходит с самого начала вычислений, поэтому не ожидайте приговора в ближайшее время.

Ответ 8

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

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

Все это хорошо =) Вы должны использовать разные парадигмы в соответствии с проблемой, которую вы решаете.

Ответ 9

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

Рассмотрим следующую аналогию: процессор с набором команд, который не имел инструкций о разветвлении, был бы совершенно бесполезен. Однако из этого не следует, что программисты должны использовать goto все время. Напротив, оказалось, что структурированное программирование и более поздние языки ООП, такие как Java, могут обойтись без использования инструкции goto, и никто не пропустил ее.

(Конечно, в Java еще есть goto, теперь он называется break, продолжить и throw.)

Ответ 10

Без побочных эффектов вы не можете выполнять операции ввода-вывода; поэтому вы не можете сделать полезное приложение.

Ответ 11

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

Как правило: Отдельные чистые методы и методы с побочными эффектами. Метод, который печатает что-то на консоли, должен делать только это и не вычислять какое-то интересное значение, которое вы могли бы использовать где-то еще.

Ответ 12

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

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

Ответ 13

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

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

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

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

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

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

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

Средство визуализации должно зависеть от остального мира, чтобы он мог полностью изолировать эти побочные эффекты от экрана; остальной мир не должен зависеть от визуализатора. Такая же аналогия для файловой заставки. Хранитель файла не должен сообщать, что нужно сохранить внешнему миру. Он должен смотреть на окружающий мир и выяснить, что сохранить самостоятельно. Таков был путь проектирования, который направлен на выделение и сдерживание побочных эффектов; он имеет тенденцию быть более тяговым, чем push-based. Результат имеет тенденцию вводить немного больше связи (хотя он может быть свободным), если вы вычисляете зависимости, поскольку сберегатель может потребоваться связать с вещами, которые он даже не заинтересован в сохранении, или для рендеринга может потребоваться доступ только для чтения к вещам он даже не заинтересован в том, чтобы открыть для себя вещи, которые он заинтересован в визуализации.

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

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

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