52700.fb2
На основе этих рассуждений вырисовывается следующий тип для сообщений:
data Query = Quit | NewGame Int | Play Move
Значение типа Query (запрос) может быть константа Quit (выход), запрос новой игры NewGame с числом,
которое указывает на сложность новой игры, также игрок может просто сделать ход Play Move.
А каков формат наших ответов? Все наши ответы на самом деле будут вызовами функции putStrLn мы
будем отвечать пользователю изменениями экрана. Поэтому у нас нет специального типа для ответов. Итак
у нас есть каркас, который можно начинать покрывать значениями. На этом этапе у нас есть два модуля. Это
модуль Loop:
module Loop where
import Game
data Query = Quit | NewGame Int | Play Move
202 | Глава 13: Поиграем
И модуль Game:
module Game where
import Data.Array
data Move = Up | Down | Left | Right
deriving (Enum)
type Label = Int
type Pos = (Int, Int)
type Board = Array Pos Label
data Game = Game {
emptyField
:: Pos,
gameBoard
:: Board }
Ленивое программирование
Мы уже знаем как происходят ленивые вычисления. Мы принимаем выражение и начинаем очищать его
от синонимов от корня к листьям или сверху вниз. Оказывается таким способом можно писать программы.
Более того в функциональном программировании это очень распространённый подход. Мы начинаем со
спецификации задачи (неформального описания) и потихоньку вытягиваем из него выражения языка Haskell.
Начинаем мы с корня, с самой верхней функции. Эта функция будет состоять из подвыражений. Когда мы
напишем верхнюю функцию, мы перейдём к подвыражениям. И так мы будем спускаться пока не напишем
всю программу.
Кажется, что такой подход очень не надёжен. Ведь мы сможем запустить программу только когда напи-
шем её целиком. На каждом промежуточном шаге у нас есть неопределённые подвыражения. Получается,
что очень долгое время мы будем писать программу, не зная работает она или нет.
Оказывается, что в Haskell есть решение этой проблемы. Нам поможет значение undefined. Мы будем
писать только тип функции (и мысленно будем говорить, пусть она делает то-то), а вместо определения
будем писать undefined. При этом конечно мы не сможем выполнять программу, вычислитель подорвётся
на первом же значении, но мы сможем узнать осмысленна ли наша программа с точки зрения компилятора,
проходит ли она проверку типов. В Haskell это большой плюс. Если программа прошла проверку типов, то
скорее всего она будет работать.
Такой подход написания программ называется написанием сверху вниз. Мы начинаем с самой верхней
функции и потихоньку вычищаем все undefined. Если вспомнить ленивые вычисления, то там роль undefined
выполняли отложенные вычисления.
В чём преимущества такого подхода? Посмотрим на дерево (рис. ?? ). Если мы идём сверху вниз, то в
самом начале у нас лишь одна задача, потом их становится всё больше и больше. Они дробятся, но источ-
ник у них один. Мы всегда знаем, что нам нужно чтобы закончить нашу задачу. Написать это, это и это
подвыражение. Беда только в том, что это подвыражение содержит ещё больше подвыражений. Но сложные