Извините за еще один вопрос о побочных эффектах FP +, но я не смог найти существующий, который вполне ответил на этот вопрос для меня.
Мое (ограниченное) понимание функционального программирования заключается в том, что побочные эффекты состояния должны быть минимизированы и отделены от логики без сохранения состояния.
Я также понял, что подход Хаскелла к этой проблеме, монада ввода / вывода, достигает этого, помещая в контейнер действия с состоянием для последующего выполнения, которые рассматриваются вне рамок самой программы.
Я пытаюсь понять этот шаблон, но на самом деле, чтобы определить, использовать ли его в проекте Python, поэтому, если возможно, избегайте специфики Haskell.
Грубый пример входящего.
Если моя программа преобразует файл XML в файл JSON:
def main():
xml_data = read_file('input.xml') # impure
json_data = convert(xml_data) # pure
write_file('output.json', json_data) # impure
Разве монада IO не подходит эффективно для этого:
steps = list(
read_file,
convert,
write_file,
)
затем снять с себя ответственность, не называя эти шаги, а позволяя переводчику делать это?
Или, другими словами, это все равно что писать:
def main(): # pure
def inner(): # impure
xml_data = read_file('input.xml')
json_data = convert(xml_data)
write_file('output.json', json_data)
return inner
затем ожидать, что кто-то еще позвонит inner()
и скажет, что ваша работа выполнена, потому что main()
она чистая.
Вся программа в конечном итоге будет содержаться в монаде IO.
Когда код фактически выполняется , все после прочтения файла зависит от состояния этого файла, поэтому он по-прежнему будет страдать от тех же ошибок, связанных с состоянием, что и императивная реализация, так что вы действительно получили что-нибудь, как программист, который будет поддерживать это?
Я полностью ценю выгоду от сокращения и изоляции поведения с состоянием, именно поэтому я структурировал императивную версию так: собирать входные данные, делать чистые вещи, выплевывать выходные данные. Надеемся, что он convert()
может быть абсолютно чистым и использовать преимущества кэшируемости, безопасности потоков и т. Д.
Я также ценю, что монадические типы могут быть полезны, особенно в конвейерах, работающих на сопоставимых типах, но я не понимаю, почему IO должен использовать монады, если они уже не находятся в таком конвейере.
Есть ли какое-то дополнительное преимущество в борьбе с побочными эффектами, которые приносит паттерн ввода / вывода, чего мне не хватает?
main
в программе на Haskell IO ()
- действие IO. На самом деле это не функция; это ценность . Вся ваша программа представляет собой чистое значение, содержащее инструкции, которые сообщают языковой среде выполнения, что она должна делать. Все нечистое (фактически выполняющее действия ввода-вывода) выходит за рамки вашей программы.
read_file
) и используете его в качестве аргумента для следующего ( write_file
). Если бы у вас была только последовательность независимых действий, вам не понадобилась бы монада.