Почему лень хорошо сочетается с прозрачностью ссылок?
Я читал учебник по Haskell (Learn You a Haskell), в котором автор сказал, что лень хорошо сочетается с прозрачностью ссылок. После дополнительного чтения и некоторых поисков я все еще не понимаю, почему. Обратите внимание, что я понимаю, что хорошего в ссылочной прозрачности и лени, но меня беспокоит именно их.
Есть ли какая-то особая польза от комбинации двух?
Или, может быть, автор просто хотел сказать, что они оба хороши, и выразил это неоднозначно?
3 ответа
Это действительно легко. Нестрогая (например, ленивая) оценка означает, что задачи могут быть отложены. Но для того, чтобы что-то отложить, вам лучше быть уверенным, что вы получите позже тот же результат, что и сейчас, и это ссылочная прозрачность. Рассмотрим этот императивный код Java:
long start = System.currentTimeMillis(); //get the start time
runBenchmarkFunction();
System.out.println("Run took " + (System.currentTimeMillis() - start) + " ms");
Теперь ленивый язык отложил бы оценку первой строки, потому что запуск необходим только в третьей строке. Таким образом, результат будет 0 (или очень близко к нему). Возможно, это не то, что вы хотите. Причиной этой проблемы может быть то, что System.currentTimeMillis не является ссылочно-прозрачным. В этом случае у вас не возникло бы никаких проблем, если бы это была функция в "математическом смысле", такая как sin или ln, которые являются ссылочно-прозрачными.
Ссылочная прозрачность означает, что функция всегда будет возвращать один и тот же вывод при одинаковом вводе. Так что если не имеет значения, является ли функция ленивой или строгой. Ленивая функция будет вычислять свой вывод в какое-то неизвестное время в будущем, но из-за ссылочной прозрачности вам гарантируется, что выходные данные всегда будут одинаковыми для заданных входных данных.
Таким образом, ссылочная прозрачность гарантирует правильность ленивых функций.
Рассмотрим этот код Python, где генератор используется для ленивого вычисления бесконечной последовательности. Он не имеет ссылочной прозрачности из-за использования глобального состояния, поэтому вызывающая сторона генератора не может быть уверена в том, что на полученный результат не повлияло какое-то другое событие.
foo = 0
def foo_sequence():
global foo
while True:
foo += 1
yield foo
>>> generator = foo_sequence()
>>> generator.next()
1
>>> generator.next()
2
>>> foo = 5
>>> generator.next()
6
В этом случае вызывающая сторона предпочла бы генерировать всю последовательность атомарно, чтобы предотвратить такие события. Следовательно, отсутствие ссылочной прозрачности (в этом надуманном примере) делает лень непривлекательной.