Функциональное программирование в Scala объясняет влияние побочного эффекта на нарушение прозрачности ссылок:
побочный эффект, который подразумевает некоторое нарушение ссылочной прозрачности.
Я прочитал часть SICP , в которой обсуждается использование «модели замещения» для оценки программы.
Поскольку я примерно понимаю модель замещения с ссылочной прозрачностью (RT), вы можете разложить функцию на ее простейшие части. Если выражение RT, вы можете декомпозировать выражение и всегда получать один и тот же результат.
Однако, как указано в приведенной выше цитате, использование побочных эффектов может / нарушит модель замещения.
Пример:
val x = foo(50) + bar(10)
Если foo
и bar
не имеют побочных эффектов, то выполнение любой функции всегда будет возвращать один и тот же результат x
. Но, если у них есть побочные эффекты, они изменят переменную, которая разрушает / бросает ключ в модель замещения.
Я чувствую себя комфортно с этим объяснением, но я не совсем понимаю.
Пожалуйста, исправьте меня и заполните любые дыры в отношении побочных эффектов, нарушающих RT, обсуждая также влияние на модель замещения.
источник
RT
отключает вас от использованияsubstitution model.
. Большая проблема с невозможностью использованияsubstitution model
- это сила использования его для рассуждения о программе?Представьте, что вы пытаетесь построить стену, и вам дали ассортимент коробок разных размеров и форм. Вам необходимо заполнить определенное L-образное отверстие в стене; Вы должны искать L-образную коробку или можете заменить две прямые коробки соответствующего размера?
В функциональном мире ответ таков: любое решение будет работать. При построении своего функционального мира вам никогда не придется открывать коробки, чтобы увидеть, что находится внутри.
В императивном мире опасно строить свою стену, не осматривая содержимое каждой коробки и сравнивая их с содержимым каждой другой коробки:
Я думаю, что остановлюсь, прежде чем тратить ваше время на более невероятные метафоры, но я надеюсь, что точка зрения сделана; Функциональные кирпичи не содержат скрытых сюрпризов и полностью предсказуемы. Поскольку вы всегда можете использовать меньшие блоки правильного размера и формы для замены большего, и нет разницы между двумя блоками одинакового размера и формы, у вас есть ссылочная прозрачность. С императивными кирпичами недостаточно иметь что-то правильного размера и формы - вы должны знать, как был построен кирпич. Не ссылочно прозрачно.
На чистом функциональном языке все, что вам нужно - это подпись функции, чтобы знать, что она делает. Конечно, вы можете заглянуть внутрь, чтобы увидеть, насколько хорошо он работает, но вам не нужно смотреть.
На императивном языке вы никогда не знаете, какие сюрпризы могут скрываться внутри.
источник
(a, b) -> a
может быть толькоfst
функцией, а функция типаa -> a
может быть толькоidentity
функцией, но вы не можете ничего сказать, например, о функции типа(a, a) -> a
.Да, интуиция совершенно права. Вот несколько указателей, чтобы получить более точную информацию:
Как вы сказали, любое выражение RT должно иметь
single
«результат». То есть, учитываяfactorial(5)
выражение в программе, оно всегда должно давать один и тот же «результат». Таким образом, если определенное числоfactorial(5)
находится в программе и оно дает 120, оно всегда должно давать 120 независимо от того, какой «порядок шагов» он расширяет / вычисляет - независимо от времени .Пример:
factorial
функция.Есть несколько соображений с этим объяснением.
Прежде всего, имейте в виду, что разные модели оценки (см. Аппликативный и нормальный порядок) могут давать разные «результаты» для одного и того же выражения RT.
В приведенном выше коде,
first
иsecond
являются референциально прозрачными, и тем не менее, выражение в конце дает разные «результаты» , если оценены при нормальном порядке и аппликативном порядке ( в соответствии с последним, выражение не останавливается)..... что приводит к использованию "результата" в кавычках. Поскольку для остановки выражения не требуется, оно может не давать значения. Таким образом, использование «результата» довольно размыто. Можно сказать, что выражение RT всегда дает то же самое
computations
при модели оценки.В-третьих, может потребоваться, чтобы два приложения
foo(50)
появлялись в программе в разных местах как разные выражения - каждое из них давало свои собственные результаты, которые могли бы отличаться друг от друга. Например, если язык допускает динамическую область видимости, оба выражения, хотя и лексически идентичные, различны. В Perl:Динамический охват вводит в заблуждение, потому что он позволяет легко думать, что
x
это единственный вход дляfoo
, когда в действительности это такx
и естьy
. Один из способов увидеть разницу состоит в том, чтобы преобразовать программу в эквивалентную без динамической области видимости - то есть, передавая явно параметры, поэтому вместо определенияfoo(x)
мы определяемfoo(x, y)
и передаемy
явно в вызывающих программах.Дело в том, что мы всегда находимся в
function
мышлении: учитывая определенный вклад в выражение, нам дают соответствующий «результат». Если мы даем один и тот же вклад, мы всегда должны ожидать один и тот же «результат».А как насчет следующего кода?
foo
Процедура ломает RT , потому что есть переопределения. То есть, мы определилиy
в одной точке, а затем переопределили то же самоеy
. В приведенном выше примере perl,y
s - это разные привязки, хотя они имеют одно и то же буквенное имя «y». Здесьy
с на самом деле то же самое. Вот почему мы говорим, что (пере) присваивание является мета- операцией: вы фактически меняете определение своей программы.Грубо говоря, люди обычно изображают разницу следующим образом: в установке без побочных эффектов у вас есть отображение
input -> output
. В «императивной» обстановке у вас естьinput -> ouput
контекст,state
который может меняться со временем.Теперь вместо того, чтобы просто подставлять выражения для их соответствующих значений, нужно также применять преобразования к
state
каждой операции, которая в этом нуждается (и, конечно, выражения могут обращатьсяstate
к ним для выполнения вычислений).Таким образом, если в программе, свободной от побочных эффектов, все, что нам нужно знать для вычисления выражения, это его индивидуальный ввод, в императивной программе нам нужно знать входные данные и все состояние для каждого шага вычисления. Рассуждение является первым ударом по большому счету (теперь, чтобы отладить проблемную процедуру, вам нужны входные данные и дамп ядра). Некоторые трюки оказываются непрактичными, как запоминание. Но также параллелизм и параллелизм становятся гораздо более сложными.
источник