IO
- это способ, которым Haskell различает код, который является прозрачным по отношению к нему, и код, который не является прозрачным. IO a
- это тип действия ввода-вывода, которое возвращает a
.
Вы можете думать о действии ввода-вывода как о фрагменте кода, который оказывает некоторое влияние на реальный мир, ожидающий выполнения. Из-за этого побочного эффекта действие ввода-вывода не является ссылочно прозрачным; следовательно, порядок исполнения имеет значение. Задача main
программы Haskell состоит в правильной последовательности и выполнении всех операций ввода-вывода. Таким образом, когда вы пишете функцию, которая возвращает IO a
, вы фактически пишете функцию, которая возвращает действие, которое в конечном итоге - при выполнении main
- выполняет действие и возвращает a
.
Еще несколько объяснений:
Ссылочная прозрачность означает, что вы можете заменить функцию на ее значение. Ссылочно-прозрачная функция не может иметь побочных эффектов; в частности, ссылочно-прозрачная функция не может получить доступ к каким-либо аппаратным ресурсам, таким как файлы, сеть или клавиатура, поскольку значение функции будет зависеть от чего-то другого, кроме ее параметров.
Ссылочно-прозрачные функции на функциональном языке, таком как Haskell похожи на математические функции (отображения между доменом и кодоменом), гораздо больше, чем последовательность обязательных инструкций о том, как вычислить значение функции. Следовательно, код Haskell сообщает компилятору, что функция применяется к ее аргументам , но не говорит, что функция называется и, таким образом, фактически вычисляется.
Следовательно, ссылочно-прозрачные функции не подразумевают порядок выполнения. Компилятор Haskell может оценивать функции любым удобным для него способом или не оценивать их вообще, если в этом нет необходимости (это называется отложенной оценкой). Единственный порядок возникает из зависимостей данных , когда одна функция требует вывода другой функции в качестве входных данных.
Реальные побочные эффекты не являются ссылочно прозрачными. Вы можете думать о реальном мире как о некоем неявном глобальном состоянии, в котором мутируют эффективные функции. Из-за этого состояния порядок выполнения имеет значение: имеет значение, если вы сначала читаете из базы данных, а затем обновляете ее, или наоборот.
Haskell - это чисто функциональный язык, все его функции Ссылочная прозрачность и компиляция опирается на эту гарантию. Как же тогда мы можем иметь дело с эффективными функциями, которые манипулируют каким-то глобальным состоянием реального мира и которые должны выполняться в определенном порядке? Путем введения зависимости данных между этими функциями.
Это именно то, что делает IO: под капотом тип IO оборачивает эффективную функцию вместе с фиктивным параметром состояния. Каждое действие ввода-вывода принимает это фиктивное состояние в качестве ввода и предоставляет его в качестве вывода. Передача этого фиктивного параметра состояния от одного действия ввода-вывода к следующему создает зависимость данных и, таким образом, сообщает компилятору Haskell, как правильно упорядочивать все действия ввода-вывода.
Вы не видите фиктивный параметр состояния, потому что он скрыт за каким-то сахаром syntacti c: запись do
в main
и других действиях ввода-вывода и внутри типа IO
.