По существу, в Python вам нужно иметь нечистую функцию где-то, поэтому нет способа получить 100% -ные функции в этом приложении. В конце вам нужно сделать несколько ввода-вывода, а IO нечисто.
Однако, вы можете попытаться представить определенный уровень абстракции в вашем приложении как чистые функции и изолировать часть, которая делает фактические побочные эффекты в другом модуле. Вы можете сделать это довольно легко по-разному - например, путем накопления содержимого файла, который вы хотите записать, в качестве чистой неизменяемой структуры данных в вашем основном коде. Тогда ваш побочный код может быть уменьшен по размеру, так как все, что ему нужно сделать, это выгрузить строку в файл.
Мы можем посмотреть на Haskell для более строгого способа представления полной мощности побочных операций с чистыми функциями и структурами данных - с использованием абстракции Monad. По сути, Monad - это то, к чему вы можете привязать обратные вызовы, чтобы создать цепочку эффективных вычислений на основе чистых функций. Для монады IO время выполнения Haskell заботится о фактическом выполнении побочных эффектов, как только вы возвращаете значение IO из функции main
, поэтому весь написанный вами код является технически чистыми функциями, а среда выполнения заботится о IO.
Библиотека Effect (отказ от ответственности: я ее написал) в основном реализует определенный аромат Монады (или что-то очень близкое к монаде) в Python. Это позволяет вам представлять произвольные IO (и другие побочные эффекты) как чистые объекты и функции и помещать фактическую производительность этих эффектов в сторону. Таким образом, ваш код приложения может быть на 100% чистым, если у вас есть библиотека с относительно простыми побочными эффектами.
Так, например, реализовать функцию, которая выводит список строк в файл с эффектами, вы могли бы сделать что-то вроде этого:
@do
def write_lines_to_file(lines, filename):
file_handle = yield open_file(filename)
for line in lines:
yield write_data(file_handle, line)
# alternatively:
# from effect.fold import sequence; from functools import partial
# yield sequence(map(partial(write_data, file_handle), lines))
yield close_file(file_handle)
Библиотека Эффект предоставляет этот специальный do
декоратора, который позволяет вы используете императивный вид синтаксиса для описания чистой эффектной операции. Выше функция эквивалентна следующему:
def write_lines_to_file(lines, filename):
file_handle_eff = open_file(filename).on(
lambda file_handle:
sequence(map(partial(write_data, file_handle), lines)).on(
lambda _: close_file(file_handle)))
Они как предполагается, что три функции существуют: open_file, write_data и close_file. Предполагается, что эти функции возвращают объекты Effect, которые представляют намерение выполнить эти действия. В конце концов, эффект по существу является намерением (некоторое прозрачное описание запрошенного действия) и одним или несколькими обратными вызовами для запуска, когда результат этого действия завершен. Интересное различие заключается в том, что write_lines_to_file не фактически записывает строки в файл; он просто возвращает некоторое представление цели , чтобы написать некоторые строки в файл.
Для осуществления этого эффекта вам необходимо использовать функцию sync_perform
, например sync_perform(dispatcher, write_lines_to_file(lines, filename))
. Это нечистая функция, которая фактически запускает исполнителей для всех эффектов, которые использует ваше чистое представление о эффективном вычислении.
Я мог бы подробно рассказать о том, как должны быть реализованы open_file, write_data и close_file, а также подробные сведения о том, что такое аргумент «диспетчер», но действительно документация на https://effect.readthedocs.org/, вероятно, правильная ссылка на эта точка.
Я также прочитал лекцию на Strange Loop об эффекте и его реализации, которые вы можете посмотреть на YouTube: https://www.youtube.com/watch?v=D37dc9EoFus
Стоит отметить, что эффект является довольно неуклюжим способом сохранить код чисто функциональным. Вы можете получить длинный путь к поддерживаемому коду, используя подход «функциональный ядро / императив» и старайтесь изо всех сил писать большую часть своего кода как чистые функции и минимизировать эффективный код. Но если вы заинтересованы в более строгом подходе, я думаю, что эффект хорош. Моя команда использует его в производстве, и это очень помогло, особенно с его API тестирования.
Зачем вам это нужно? Цикл 'for' является наиболее удобочитаемым, и все это делает« правильный »способ сделать это. –
@tobias_k Я не думаю, что буду использовать его в любом серьезном проекте, но я хочу знать, как это делается. Всегда хорошо знать совершенно другую парадигму, даже если вы не планируете ее использовать. –
Я бы определил метод 'forEach', который бы содержал цикл' for' и использовал его. 'def forEach (iterable, func): для i в iterable: func (i)' then' forEach (input_lines, lambda x: print (x, file = output)) '. (в любом случае, функциональное программирование просто перемещает цикл for в другом месте) – njzk2