Изучай Haskell во имя добра! (Липовача) - страница 117

.

ПРИМЕЧАНИЕ. Пустой кортеж имеет значение >(), его тип – также >().

Когда будет выполнено действие ввода-вывода? Вот для чего нужна функция >main. Операции ввода-вывода выполняются, если мы поместим их в функцию >main и запустим нашу программу.

Объединение действий ввода-вывода

Возможность поместить в программу всего один оператор ввода-вывода не очень-то вдохновляет. Но мы можем использовать ключевое слово >do для того, чтобы «склеить» несколько операторов ввода-вывода в один. Рассмотрим пример:

>main = do

>   putStrLn "Привет, как тебя зовут?"

>   name <– getLine

>   putStrLn ("Привет, " ++ name ++ ", ну ты и хипстота!")

О, новый синтаксис!.. И он похож на синтаксис императивных языков. Если откомпилировать и запустить эту программу, она будет работать так, как вы и предполагаете. Обратите внимание: мы записали ключевое слово >do и затем последовательность шагов, как сделали бы в императивном языке. Каждый из этих шагов – действие ввода-вывода. Расположив их рядом с помощью ключевого слова >do, мы свели их в одно действие ввода-вывода. Получившееся действие имеет тип >IO(); это тип последнего оператора в цепочке.

По этой причине функция >main всегда имеет тип >main :: IO <нечто>, где ><нечто> – некоторый конкретный тип. По общепринятому соглашению обычно не пишут декларацию типа для функции >main.

В третьей строке можно видеть ещё один не встречавшийся нам ранее элемент синтаксиса, >name><–>getLine. Создаётся впечатление, будто считанная со стандартного входа строка сохраняется в переменной с именем >name. Так ли это на самом деле? Давайте посмотрим на тип >getLine.

>ghci> :t getLine

>getLine :: IO String

Ага!.. Функция >getLine – действие ввода-вывода, которое содержит результирующий тип – строку. Это понятно: действие ждёт, пока пользователь не введёт что-нибудь с терминала, и затем это нечто будет представлено как строка. Что тогда делает выражение >name <– getLine? Можно прочитать его так: «выполнить действие >getLine и затем связать результат выполнения с именем >name». Функция >getLine имеет тип >IO String, поэтому образец >name будет иметь тип >String. Можно представить действие ввода-вывода в виде ящика с ножками, который ходит в реальный мир, что-то в нём делает (рисует граффити на стене, например) и иногда приносит обратно какие-либо данные. Если ящик что-либо принёс, единственный способ открыть его и извлечь данные – использовать конструкцию с символом <–. Получить данные из действия ввода-вывода можно только внутри другого действия ввода-вывода. Таким образом, язык Haskell чётко разделяет чистую и «грязную» части кода. Функция