Читаем Учебник по Haskell полностью

не можем изменять состояния, мы можем лишь давать новые имена или строить новые выражения из уже

существующих.

Но в этот статичный мир описаний не вписывается взаимодействие с пользователем. Предположим, что

мы хотим написать такую программу: мы набираем на клавиатуре имя файла, нажимаем Enter и программа

показывает на экране содержимое этого файла, затем мы набираем текст, нажимаем Enter и текст дописыва-

ется в конец файла, файл сохраняется. Это описание предполагает упорядоченность действий. Мы не можем

сначала сохранить текст, затем прочитать обновления. Тогда текст останется прежним.

Ещё один пример. Предположим у нас есть функция getChar, которая читает букву с клавиатуры. И

функция print, которая выводит строку на экран И посмотрим на такое выражение:

let c = getChar

in

print $ c : c : []

О чём говорит это выражение? Возможно, прочитай с клавиатуры букву и выведи её на экран дважды.

Но возможен и другой вариант, если в нашем языке все определения это синонимы мы можем записать это

выражение так:

print $ getChar : getChar : []

Это выражение уже говорит о том, что читать с клавиатуры необходимо дважды! А ведь мы сделали обыч-

ное преобразование, заменили вхождения синонима на его определение, но смысл изменился. Взаимодей-

ствие с пользователем нарушает чистоту функций, нечистые функции называются функциями с побочными

эффектами.

Как быть? Можно ли внести в мир описаний порядок выполнения, сохранив преимущества функциональ-

ной чистоты? Долгое время этот вопрос был очень трудным для чистых функциональных языков. Как можно

пользоваться языком, который не позволяет сделать такие базовые вещи как ввод/вывод?

126 | Глава 8: IO

8.2 Монада IO

Где-то мы уже встречались с такой проблемой. Когда мы говорили о типе ST и обновлении значений. Там

тоже были проблемы порядка вычислений, нам удалось преодолеть их с помощью скрытой передачи фиктив-

ного состояния. Тогда наши обновления были чистыми, мы могли безболезненно скрыть их от пользователя.

Теперь всё гораздо труднее. Нам всё-таки хочется взаимодействовать с внешним миром. Для обозначения

внешнего мира мы определим специальный тип и назовём его RealWorld:

module IO(

IO

) where

data RealWorld = RealWorld

newtype IO a = IO (ST RealWorld a)

instance Functor

IO where ...

instance Applicative

IO where ...

instance Monad

IO where ...

Тип IO (от англ. input-output или ввод-вывод) обозначает взаимодействие с внешним миром. Внешний

мир словно является состоянием наших вычислений. Экземпляры классов композиции специальных функций

такие же как и для ST (а следовательно и для State). Но при этом, поскольку мы конкретизировали первый

параметр типа ST, мы уже не сможем воспользоваться функцией runST.

Тип RealWorld определён в модуле Control.Monad.ST, там же можно найти и функцию:

stToIO :: ST RealWorld a -> IO a

Интересно, что класс Monad был придуман как раз для решения проблемы ввода-вывода. Классы типов

изначально задумывались для решения проблемы определения арифметических операций на разных числах

и функции сравнения на равенство для разных типов, мало кто тогда догадывался, что классы типов сыграют

такую роль, станут основополагающей особенностью языка.

a

f

IO b

b

g

IO c

До

После

a

g

f

IO c

a

f>>g

IO c

Рис. 8.1: Композиция для монады IO

Посмотрим на (рис. 8.1). Это рисунок для класса Kleisli. Здесь под >> понимается композиция, как мы

её определяли в главе 6, а не метод класса Monad, вспомним определение:

class Kleisli m where

idK

:: a -> m a

(>> ) :: (a -> m b) -> (b -> m c) -> (a -> m c)

Монада IO | 127

Композиция специальных функций типа a -> IO b вносит порядок вычисления. Считается, что сначала

будет вычислена функция слева от композиции, а затем функция справа от композиции. Это происходит за

счёт скрытой передачи фиктивного состояния. Теперь перейдём к классу Monad. Там композиция заменяется

на применение или операция связывания:

ma >>= mf

Для типа IO эта запись говорит о том, что сначала будет выполнено выражение ma и результат будет под-

ставлен в выражение mf и только затем будет выполнено mf. Оператор связывания для специальных функций

вида:

a -> IO b

раскалывает наш статический мир на “до” и “после”. Однажды попав в сети IO, мы не можем из них

выбраться, поскольку теперь у нас нет функции runST. Но это не так страшно. Тип IO дробит наш статический

мир на кадры. Но мы спокойно можем создавать статические чистые функции и поднимать их в мир IO лишь

там где это действительно нужно.

Рассмотрим такой пример, программа читает с клавиатуры начальное значение, затем загружает файл

настроек. Потом запускается, какая-то сложная функция и в самом конце мы выводим результат на экран.

Схематично мы можем записать эту программу так:

program = liftA2 algorithm readInit (readConfig ”file”) >>= print

-- функции с побочными эффектами

readInit

:: IO Int

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных