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

Этот класс является классом Category в мире наших специальных функций. Если мы сотрём все буквы m,

то мы получим обычные типы для тождества и композиции. В этом мире должны выполняться те же правила:

f

*> idK

== f

idK *> f

== f

f *> (g *> h) == (f *> g) *> h

Взаимодействие с внешним миром

С помощью класса Kleisli мы можем составлять из одних специальных функций другие. Но как мы

сможем комбинировать специальные функции с обычными?

Поскольку слева у нашей специальной функции обычный общий тип, то с этой стороны мы можем вос-

пользоваться обычной функцией композиции >> . Но как быть при композиции справа? Нам нужна функция

типа:

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

Оказывается мы можем составить её из методов класса Kleisli. Мы назовём эту функцию композиции

(+> ).

(+> ) :: Kleisli m => (a -> m b) -> (b -> c) -> (a -> m c)

f +> g = f *> (g >> idK)

С помощью метода idK мы можем погрузить в мир специальных функций любую обычную функцию.

Композиция функций | 87

Три композиции

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

аргументы

|

результат

обычная

>>

обычная

==

обычная

специальная

+>

обычная

==

специальная

специальная

*>

специальная

==

специальная

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

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

Обобщённая формулировка категории Клейсли

Отметим, что мы могли бы сформулировать класс Kleisli и в более общем виде с помощью класса

Category:

class Kleisli m where

idK

:: Category cat => cat a (m a)

(*> ) :: Category cat => cat a (m b) -> cat b (m c) -> cat a (m c)

(+> ) :: (Category cat, Kleisli m)

=> cat a (m b) -> cat b c -> cat a (m c)

f +> g = f *> (g >> idK)

Мы заменили функциональный тип на его обобщение. Для наглядности мы будем пользоваться специ-

альной формулировкой со стрелочным типом.

Для этого мы определим модуль Kleisli. hs

module Kleisli where

import Prelude hiding (id, (>> ))

class Category cat where

id

:: cat a a

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

class Kleisli m where

idK

:: a -> m a

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

(+> ) :: Kleisli m => (a -> m b) -> (b -> c) -> (a -> m c)

f +> g = f *> (g >> idK)

-- Экземпляр для функций

instance Category (-> ) where

id

= \x -> x

f >> g

= \x -> g (f x)

Мы не будем импортировать функцию id, а определим её в классе Category. Также в Prelude уже опре-

делена функция (>> ) мы спрячем её с помощью специальной директивы hiding для того, чтобы она нам не

мешалась. Далее мы будем дополнять этот модуль экземплярами класса Kleisli и примерами.

6.2 Примеры специальных функций

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

Частично определённые функции – это такие функции, которые определены не для всех значений аргу-

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

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

пользоваться специальным типом Maybe. Посмотрим на его определение:

data Maybe a = Nothing | Just a

deriving (Show, Eq, Ord)

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

a

f

b

Nothing

Рис. 6.2: Частично определённая функция

Частично определённая функция имеет тип a -> Maybe b (рис. 6.2), если всё в порядке и значение было

вычислено, она вернёт (Just a), а в случае ошибки будет возвращено значение Nothing. Теперь мы можем

определить нашу функцию так:

pred :: Nat -> Maybe Nat

pred Zero

= Nothing

pred (Succ a)

= Just a

Для Zero предыдущий элемент не определён .

Составляем функции вручную

Значение функции pred завёрнуто в упаковку Maybe, и для того чтобы воспользоваться им нам придётся

разворачивать его каждый раз. Как будет выглядеть функция извлечения дважды предыдущего натурального

числа:

pred2 :: Nat -> Maybe Nat

pred2 x =

case pred x of

Just (Succ a) -> Just a

_

-> Nothing

Если мы захотим определить pred3, мы заменим pred в case-выражении на pred2. Вроде не такое уж и

длинное решение. Но всё же мы теряем все преимущества гибких функций, все преимущества бесточечного

стиля. Нам бы хотелось написать так:

pred2 :: Nat -> Maybe Nat

pred2 = pred >> pred

pred3 :: Nat -> Maybe Nat

pred3 = pred >> pred >> pred

Но компилятор этого не допустит.

Композиция

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

ние графически (рис. 6.3). Сверху изображены две частично определённых функции. Если функция f вернула

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

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

C++: базовый курс
C++: базовый курс

В этой книге описаны все основные средства языка С++ - от элементарных понятий до супервозможностей. После рассмотрения основ программирования на C++ (переменных, операторов, инструкций управления, функций, классов и объектов) читатель освоит такие более сложные средства языка, как механизм обработки исключительных ситуаций (исключений), шаблоны, пространства имен, динамическая идентификация типов, стандартная библиотека шаблонов (STL), а также познакомится с расширенным набором ключевых слов, используемым в .NET-программировании. Автор справочника - общепризнанный авторитет в области программирования на языках C и C++, Java и C# - включил в текст своей книги и советы программистам, которые позволят повысить эффективность их работы. Книга рассчитана на широкий круг читателей, желающих изучить язык программирования С++.

Герберт Шилдт

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