Комментарии 4
Гениальная статья! Все понял и чувствую себя почти что просвещенным :) Большое спасибо за перевод.
Скажите, а верна ли моя догадка, что IO на самом деле является внутренней монадой компилятора (подобно встроенным типам, таким, как Int), и когда main возвращает эту монаду, как бы «за кадром» начинается выполнение программы? То есть ни в каком Prelude не объявлен соответствующий экземпляр класса Monad.
Скажите, а верна ли моя догадка, что IO на самом деле является внутренней монадой компилятора (подобно встроенным типам, таким, как Int), и когда main возвращает эту монаду, как бы «за кадром» начинается выполнение программы? То есть ни в каком Prelude не объявлен соответствующий экземпляр класса Monad.
+1
У меня есть подозрение, что Prelude вообще лишь «агрегатор» многих функций из многих модулей. Что касается монады IO, то в литературе, которая мне встречалась, говорилось, что IO ничем от других монад не отличается, за исключением того что его конструктор (собственно, «IO») не экспортируется из модуля System.IO, — и таким нехитрым образом (через цепочку следствий) нельзя написать функцию с чистым типом, у которой внутри вызывались бы функции из монады IO.
Однако, ручаться не буду, потому что в реальности оно обычно не так, как на самом деле. :) Для уточнения этого вопроса я, пожалуй, загляну накануне в исходники какого-нибудь компилятора (ну, GHC, скорее всего). Точно знаю: «зашитые» в язык вещи есть. Например, конструктор списков (квадратные скобки — []). Ничто не мешает и монаде IO быть каким-то образом «зашитой» там же.
Однако, ручаться не буду, потому что в реальности оно обычно не так, как на самом деле. :) Для уточнения этого вопроса я, пожалуй, загляну накануне в исходники какого-нибудь компилятора (ну, GHC, скорее всего). Точно знаю: «зашитые» в язык вещи есть. Например, конструктор списков (квадратные скобки — []). Ничто не мешает и монаде IO быть каким-то образом «зашитой» там же.
0
У каждой монады свой способ «распаковать» монадическое значение
Оператор >>= принимает… «распаковывает» его в обычное (немонадическое) значение (распаковка для всех монад выглядит по-разному), и затем передает это обычное значение в монадическую функцию. В ней уже производится монадическое значение («действие») и возвращается финальный результат.
Мне кажется, что представление о том, что происходит распаковка не совсем удачно и вводит читателя в заблуждение. Взять, например, тот же список. Где там распаковка в том смысле, как Вы описывали extract?
Аналогично с IO — распаковка в смысле extract должна была бы осуществлять действия, связанные с вводом-выводом. Но мы-то знаем, что при >>= никаких действий ввода-вывода не осуществляется.
Если Вы имели в виду какой-то другой смысл слова «распаковка», то стоит это пояснить.
Я бы использовал другое объяснение — >>= производит применения функции к значению внутри монады, не производя распаковки, а затем получающееся «дважды монадическое» значение некоторым специфичным для монады образом (известным, как функция join) перепаковывает в просто монадическое.
На примере списка — монадическая функция применяется к каждому элементу списка, затем получившийся список списков делается плоским.
Оператор >>= принимает… «распаковывает» его в обычное (немонадическое) значение (распаковка для всех монад выглядит по-разному), и затем передает это обычное значение в монадическую функцию. В ней уже производится монадическое значение («действие») и возвращается финальный результат.
Мне кажется, что представление о том, что происходит распаковка не совсем удачно и вводит читателя в заблуждение. Взять, например, тот же список. Где там распаковка в том смысле, как Вы описывали extract?
Аналогично с IO — распаковка в смысле extract должна была бы осуществлять действия, связанные с вводом-выводом. Но мы-то знаем, что при >>= никаких действий ввода-вывода не осуществляется.
Если Вы имели в виду какой-то другой смысл слова «распаковка», то стоит это пояснить.
Я бы использовал другое объяснение — >>= производит применения функции к значению внутри монады, не производя распаковки, а затем получающееся «дважды монадическое» значение некоторым специфичным для монады образом (известным, как функция join) перепаковывает в просто монадическое.
На примере списка — монадическая функция применяется к каждому элементу списка, затем получившийся список списков делается плоским.
0
Я буду рад перевести ваше сообщение и отправить его автору. :)
Вообще-то, конечно, тонкости здесь есть. Мы можем собрать последовательность действий при помощи bind или do-нотации и сохранить их в соответствующей структуре до будущего выполнения. В этом случае, действительно, никакой «распаковки» не происходит. Но когда дойдет до выполнения, оператор >>= в монаде IO все-таки занимается «распаковкой». Он берет значение типа IO a, достает из него значение a (все так же находясь в монаде IO) и передает его в следующую функцию. Не стоит путать «выполнение» и «распаковку»: при ленивом порядке вычислений выполнение может начаться не в том же участке кода, где оно написано. То есть, «выполнение» — это реальные действия, которые печатают что-то в консоли, берут из консоли, запускают генератор случайных чисел и так далее, а «распаковка» — это указание, что когда будем выполнять эти инструкции, то должны извлечь вот эти данные. «Распаковка» декларирует действие для будущего выполнения, но не запускает его тут же, немедленно.
Вообще-то, конечно, тонкости здесь есть. Мы можем собрать последовательность действий при помощи bind или do-нотации и сохранить их в соответствующей структуре до будущего выполнения. В этом случае, действительно, никакой «распаковки» не происходит. Но когда дойдет до выполнения, оператор >>= в монаде IO все-таки занимается «распаковкой». Он берет значение типа IO a, достает из него значение a (все так же находясь в монаде IO) и передает его в следующую функцию. Не стоит путать «выполнение» и «распаковку»: при ленивом порядке вычислений выполнение может начаться не в том же участке кода, где оно написано. То есть, «выполнение» — это реальные действия, которые печатают что-то в консоли, берут из консоли, запускают генератор случайных чисел и так далее, а «распаковка» — это указание, что когда будем выполнять эти инструкции, то должны извлечь вот эти данные. «Распаковка» декларирует действие для будущего выполнения, но не запускает его тут же, немедленно.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Публикации
Изменить настройки темы
Еще Одно Руководство по Монадам (часть 2: функции >>= и return)