Каковы преимущества ссылочной прозрачности для программиста?


18

Каковы преимущества ссылочной прозрачности в программировании ?

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

Конечно, у них будут свои академические объяснения того, насколько это «чисто» и «элегантно», но как это делает его лучше, чем менее «чистый» код? Как это помогает мне в моем повседневном программировании?

Примечание: это не дубликат Что такое ссылочная прозрачность? Последний затрагивает тему того, что такое RT, в то время как этот вопрос посвящен его преимуществам (которые могут быть не такими интуитивными).




3
Ссылочная прозрачность позволяет использовать эквациональные рассуждения для: 1) проверки свойств кода и 2) написания программ. Есть несколько книг о Haskell, где авторы должны узнать, как вы можете начать с некоторых уравнений, для которых вы хотите, чтобы функция полностью выполнялась, и, используя только эквалайзерное обоснование, вы в конечном итоге получите реализацию указанной функции, что, следовательно, безусловно, правильно. Теперь, насколько это может быть применено в "повседневном" программировании, вероятно, зависит от контекста ...
Бакуриу

2
@err Вам нравится код, который легче реорганизовать, потому что вы знаете, что вызов функции дважды - это то же самое, что сохранение ее значения в переменной с последующим двойным использованием этой переменной? Вы бы сказали, что это полезно для ежедневного программирования?
Андрес Ф.

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

Ответы:


37

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

Возьмите пример computeProductPriceметода.

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

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

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

пример

Представьте, что в структуре есть метод, который анализирует число:

decimal math.parse(string t)

У него нет ссылочной прозрачности, потому что это зависит от:

  • Переменная окружения, которая определяет систему нумерации, то есть Base 10 или что-то еще.

  • Переменная в mathбиблиотеке, которая определяет точность чисел для анализа. Таким образом, при значении 1, синтаксический анализ строки "12.3456"даст 12.3.

  • Культура, которая определяет ожидаемое форматирование. Например, с fr-FR, разбор "12.345"даст 12345, потому что символ разделения должен быть ,, а не.

Представьте, как легко или сложно было бы работать с таким методом. С одним и тем же вводом вы можете получить радикально разные результаты в зависимости от момента, когда вы вызываете метод, потому что что-то где-то изменило переменную среды или переключило культуру или установило другую точность. Недетерминированный характер метода приведет к большему количеству ошибок и большему количеству отладочного кошмара. Вызов math.parse("12345")и получение 5349в качестве ответа, так как какой-то параллельный код анализировал восьмеричные числа, нехорошо.

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

decimal math.parse(string t, base=10, precision=20, culture=cultures.en_us)

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


4
Просто дополнение: ссылочная прозрачность применяется ко всем выражениям в языке, а не только к функциям.
садовник

3
Обратите внимание, что существуют ограничения на то, насколько прозрачным вы можете быть. Создание packet = socket.recv()ссылочной прозрачности, скорее, побеждает точку функции.
Отметить

1
Должна быть культура = культура. Инвариант. Если вы не хотите случайно создать программное обеспечение, которое работает только в США.
user253751

@immibis: хм, хороший вопрос. Каковы будут правила синтаксического анализа invariant? Либо они такие же, как en_usи в этом случае, зачем, или они соответствуют какой-то другой стране, в этом случае, какой и почему вместо этого en_us, или у них есть свои конкретные правила, которые в любом случае не соответствуют ни одной стране , который был бы бесполезен. Там действительно нет «правильный ответ» между 12,345.67и 12 345,67: любые «правила по умолчанию» будет работать в течение нескольких странах, и не будет работать для других.
Арсений Мурзенко

3
@ArseniMourzenko Обычно это «наименьший общий знаменатель» и похож на синтаксис, который используют многие языки программирования (который также является инвариантным для культуры). 12345разбирается как 12345, 12 345или 12,345либо 12.345является ошибкой. 12.345Анализируемый как инвариантное число с плавающей точкой всегда дает 12.345, в соответствии с соглашением языка программирования использования. в качестве десятичного разделителя. Строки сортируются по кодам Unicode и чувствительны к регистру. И так далее.
user253751

11

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

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

Чем проще код для чтения и рассуждения, тем проще обслуживать и обнаруживать ошибки, что экономит время и деньги для вас и вашего работодателя.


10
«Как только вы начинаете добавлять изменяющиеся переменные, некоторые из которых имеют функции, выходящие за пределы одной функции, вы не можете просто прочитать код, вы должны выполнить его либо в своей голове, либо в отладчике, чтобы понять, как он действительно работает. ": Хорошая точка зрения. Другими словами, ссылочная прозрачность означает не только то, что фрагмент кода всегда будет давать один и тот же результат для одних и тех же входных данных, но и то, что полученный результат является единственным эффектом этого фрагмента кода, то есть никакой другой скрытой стороны эффект как изменение некоторой переменной, которая была определена далеко в другом модуле.
Джорджио

Это хороший момент. У меня есть небольшая проблема с тем, чем проще код для чтения / рассуждения аргумента, так как более простое чтение или рассуждение является несколько неопределенным и субъективным атрибутом кода.
Эяль Рот

Как только вы начинаете добавлять переменные переменные, некоторые из которых имеют область действия, выходящую за пределы одной функции, но почему операция присваивания не рекомендуется, даже если область действия переменной является локальной для функции?
rahulaga_dev

9

Люди используют термин «легче рассуждать», но никогда не объясняют, что это значит. Рассмотрим следующий пример:

result1 = foo("bar", 12)
// 100 lines of code
result2 = foo("bar", 12)

Есть result1и result2одинаковые или разные? Без ссылочной прозрачности вы понятия не имеете. Вы должны на самом деле прочитать тело, fooчтобы убедиться, и, возможно, тело любых fooвызовов функций , и так далее.

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

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


1
Вы упомянули , что ссылочная прозрачность позволяет рассуждать о результате обращений к Foo () и знает ли result1и result2то же. Другим важным аспектом является то, что если он foo("bar", 12)является прозрачным по ссылкам, то вам не нужно задавать себе вопрос, произвел ли этот вызов какие-либо эффекты в другом месте (установите некоторые переменные? Удалил файл? Как угодно).
Джорджио

Единственная «ссылочная целостность», с которой я знаком, касается реляционных баз данных.
Отметить

1
@ Марк Это опечатка. Карл имел в виду ссылочную прозрачность, что очевидно из остальной части его ответа.
Андрес Ф.

6

Я бы сказал: ссылочная прозрачность хороша не только для функционального программирования, но и для всех, кто работает с функциями, потому что она следует принципу наименьшего удивления.

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.