Выбрать главу

С помощью класса Applicative можно применять специальные значения к обычным функциям любого

числа аргументов:

class Functor f => Applicative f where

pure

:: a -> f a

<*>

:: f (a -> b) -> f a -> f b

liftA

:: Applicative f => (a -> b) -> f a -> f b

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c

liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d

...

С помощью класса Monad можно применять специальные значения к специальным функциям.

class Monad m where

return

:: a -> m a

(>>=)

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

Функция return является функцией id в мире специальных функций, а функция >>= является функцией

применения ($), с обратным порядком следования аргументов. Вспомним также класс Kleisli, на примере

котором мы узнали много нового из жизни специальных функций:

class Kleisli m where

idK

:: a -> m a

(*> )

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

Мы узнали несколько стандартных специальных функций:

Частично определённые функции

a -> Maybe b

data Maybe a = Nothing | Just a

Многозначные функции

a -> [b]

data [a] = [] | a : [a]

6.6 Упражнения

В первых упражнениях вам предлагается по картинке специальной функции написать экземпляр классов

Kleisli и Monad.

Функции с состоянием

b

a

f

s

s

Рис. 6.6: Функция с состоянием

100 | Глава 6: Функторы и монады: теория

В Haskell нельзя изменять значения. Новые сложные значения описываются в терминах базовых значе-

ний. Но как же тогда мы сможем описать функцию с состоянием? Функцию, которая принимает на вход

значение, составляет результат на основе внутреннего состояния и значения аргумента и обновляет состоя-

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

состояния на вход вместе с аргументом и возвращать обновлённое состояние на выходе. У нас получится

такой тип:

a -> s -> (b, s)

Функция принимает одно значение типа a и состояние типа s, а возвращает пару, которая состоит из

результата типа b и обновлённого состояния. Если мы введём синоним:

type State s b = s -> (b, s)

И вспомним о частичном применении, то мы сможем записать тип функции с состоянием так:

a -> State s b

В Haskell пошли дальше и выделили для таких функций специальный тип:

data State s a = State (s -> (a, s))

runState :: State s a -> s -> (a, s)

runState (State f) = f

b

c

a

f

b

g

s

s

s

s

b

c

a

g

f

s

s

s

c

a

f*>g

s

s

Рис. 6.7: Композиция функций с состоянием

Функция runState просто извлекает функцию из оболочки State.

На (рис. 6.6) изображена схема функции с состоянием. В сравнении с обычной функцией у такой функции

один дополнительный выход и один дополнительный вход типа s. По ним течёт и изменяется состояние.

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

и Monad для типа State s (рис. 6.7).

Подсказка: В этом определении есть одна хитрость, в отличае от типов Maybe и [a] у типа State два

параметра, это параметр состояния и параметр значения. Но мы делаем экземпляр не для State, а для State

s, то есть мы свяжем тип с некоторым произвольным типом s.

instance Kleisli (State s) where

...

Упражнения | 101

a

f

b

env

Рис. 6.8: Функция с окружением

Функции с окружением

Сначала мы рассмотрим функции с окружением. Функции с окружением – это такие функции, у которых

есть некоторое хранилище данных или окружение, из которых они могут читать информацию. Но в отличие

от функций с состоянием они не могут это окружение изменять. Функция с окружением похожа на функцию

с состоянием без одного выхода для состояния (рис. 6.8).

Функция с окружением принимает аргумент a и окружение env и возвращает результат b: