Ну а что случится, если мы получим на вход пустую строку? В этом случае выполнится часть после ключевого слова then. То есть выполнится выражение return (). Если вам приходилось писать на императивных языках вроде C, Java или на Python, вы наверняка уверены, что знаете, как работает функция return – и, возможно, у вас возникнет искушение пропустить эту часть текста. Но не стоит спешить: функция return в языке Haskell работает совершенно не так, как в большинстве других языков! Её название сбивает с толку, но на самом деле она довольно сильно отличается от своих «тёзок». В императивных языках ключевое слово return обычно прекращает выполнение метода или процедуры и возвращает некоторое значение вызывающему коду. В языке Haskell (и особенно в действиях ввода-вывода) одноимённая функция создаёт действие ввода-вывода из чистого значения. Если продолжать аналогию с коробками, она берёт значение и помещает его в «коробочку». Получившееся в результате действие ввода-вывода на самом деле не выполняет никаких действий – оно просто инкапсулирует некоторое значение. Таким образом, в контексте системы ввода-вывода return "ха-ха" будет иметь тип IO String. Какой смысл преобразовывать чистое значение в действие ввода-вывода, которое ничего не делает? Зачем «пачкать» нашу программу больше необходимого? Нам нужно некоторое действие ввода-вывода для второй части условного оператора, чтобы обработать случай пустой строки. Вот для чего мы создали фиктивное действие ввода-вывода, которое ничего не делает, записав return ().

Вызов функции return не прекращает выполнение блока do – ничего подобного! Например, следующая программа успешно выполнится вся до последней строчки:

main = do

   return ()

   return "ХА-ХА-ХА"

   line <– getLine

   return "ЛЯ-ЛЯ-ЛЯ"

   return 4

   putStrLn line

Всё, что делает функция return, – создаёт действия ввода-вывода, которые не делают ничего, кроме как содержат значения, и все они отбрасываются, поскольку не привязаны к образцам. Мы можем использовать функцию return вместе с символом <– для того, чтобы связывать значения с образцами.

main = do

   let a = "ад"

       b = "да!"

   putStrLn $ a ++ " " ++ b

Как вы можете видеть, функция return выполняет обратную операцию по отношению к операции <–. В то время как функция return принимает значение и помещает его в «коробку», операция <– принимает (и исполняет) «коробку», а затем привязывает полученное из неё значение к имени. Но всё это выглядит лишним, так как в блоках do можно использовать выражение let для привязки к именам, например так:

main = do

   let a = "hell"

       b = "yeah"

   putStrLn $ a ++ " " ++ b

При работе с блоками do мы чаще всего используем функцию return либо для создания действия ввода-вывода, которое ничего не делает, либо для того, чтобы блок do возвращал нужное нам значение, а не результат последнего действия ввода-вывода. Во втором случае мы используем функцию return, чтобы создать действие ввода-вывода, которое будет всегда возвращать нужное нам значение, и эта функция return должна находиться в самом конце блока do.

<p>Некоторые полезные функции для ввода-вывода</p>

В стандартной библиотеке языка Haskell имеется масса полезных функций и действий ввода-вывода. Давайте рассмотрим некоторые из них и увидим, как ими пользоваться.

<p>Функция putStr</p>

Функция putStr похожа на функцию putStrLn – она принимает строку как параметр и возвращает действие ввода-вывода, которое печатает строку на терминале. Единственное отличие: функция putStr не выполняет перевод на новую строку после печати, как это делает putStrLn.

main = do

   putStr "Привет, "

   putStr "я "

   putStrLn "Энди!"

Если мы скомпилируем эту программу, то при запуске получим:

Привет, я Энди!

<p>Функция putChar</p>

Функция putChar принимает символ и возвращает действие ввода-вывода, которое напечатает его на терминале.

main = do

   putChar 'A'

   putChar 'Б'

   putChar 'В'

Функция putStr определена рекурсивно с помощью функции putChar. Базовый случай для функции putStr – это пустая строка. Если печатаемая строка пуста, функция возвращает пустое действие ввода-вывода, то есть return (). Если строка не пуста, функция выводит на терминал первый символ этой строки, вызывая функцию putChar, а затем выводит остальные символы, снова рекурсивно вызывая саму себя.

putStr :: String –> IO ()

putStr [] = return ()

putStr (x:xs) = do

    putChar x

    putStr xs

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

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