Как монады обработки ошибок, подобные Eithers, достигают ссылочной прозрачности?

#scala #functional-programming #monads #scalaz

#scala #функциональное программирование #монады #scalaz

Вопрос:

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

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

Что меня смущает, так это то, как именно монады достигают этого. Предположим, у меня есть этот код с использованием scalaz

 def couldThrowException: Exception / Boolean = ??
val f  = couldThrowException()
val g = couldThrowException()
  

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

Ответ №1:

f() и g() должны оцениваться с тем же значением при тех же входных данных.

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

Имеет больше смысла возвращать либо, если couldThrowException принимает параметр. Если это чистая функция, она будет иметь ссылочную прозрачность, поэтому некоторые входные данные всегда будут приводить, /-(true) а некоторые входные данные всегда будут приводить -/(NullPointerException) .

В Scala вы вполне можете использовать функцию, которая не является чистой и не является ссылочно прозрачной. Возможно, это класс Java. Возможно, он использует часть Scala, которая не является чистой.

Но я предполагаю, что вы заинтересованы в соединении между чистым миром FP и нечистыми библиотеками. Классическим примером этого является ввод-вывод. println может произойти сбой по разным причинам — разрешения, заполнение файловой системы и т. Д.

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

Часто монада используется для инкапсуляции «мира».

Вы можете много узнать об этом подходе, прочитав о монаде ввода-вывода Haskell.

Core Scala IO не является полностью чистым, поэтому println может вызывать и исключать, и, следовательно, как вы заметили, не является полностью ссылочно прозрачным. Scalaz предоставляет монаду ввода-вывода, аналогичную Haskell.

Следует отметить одну вещь, потому что это сбивает с толку многих новичков: в «мировом» подходе нет ничего, что требовало бы монады, а IO — это не самая простая монада, на которую стоит обратить внимание, когда впервые узнаешь, что такое монада и почему они полезны.

Комментарии:

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

2. Нет, я говорю, что вы используете нечистые функции, которые не являются ссылочно прозрачными. Но я описал другой способ сделать это, который является чистым FP, и это то, что вы получаете в Haskell и в Scalaz.effect.