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

>instance YesNo Int where

>   yesno 0 = False

>   yesno _ = True

Пустые списки (и, соответственно, строки) считаются имеющими ложное значение; не пустые списки истинны.

>instance YesNo [a] where

>   yesno [] = False

>   yesno _ = True

Обратите внимание, как мы записали тип-параметр для того, чтобы сделать список конкретным типом, но не делали никаких предположений о типе, хранимом в списке. Что ещё? Гм-м… Я знаю, что тип >Bool также содержит информацию об истинности или ложности, и сообщает об этом довольно недвусмысленно:

>instance YesNo Bool where

>   yesno = id

Что? Какое >id?.. Это стандартная библиотечная функция, которая принимает параметр и его же и возвращает. Мы всё равно записали бы то же самое. Сделаем экземпляр для типа >Maybe:

>instance YesNo (Maybe a) where

>   yesno (Just _) = True

>   yesno Nothing = False

Нам не нужно ограничение на класс параметра, потому что мы не делаем никаких предположений о содержимом типа >Maybe. Мы говорим, что он истинен для всех значений >Just и ложен для значения >Nothing. Нам приходится писать >(Maybe a) вместо просто >Maybe, потому что, если подумать, не может существовать функции >Maybe –> Bool, так как >Maybe – не конкретный тип; зато может существовать функция >Maybe a –> Bool. Круто – любой тип вида >Maybe <нечто> является частью >YesNo независимо от того, что представляет собой это «нечто»!

Ранее мы определили тип >Tree для представления бинарного поискового дерева. Мы можем сказать, что пустое дерево должно быть аналогом ложного значения, а не пустое – истинного.

>instance YesNo (Tree a) where

>   yesno EmptyTree = False

>   yesno _ = True

Есть ли аналоги истинности и ложности у цветов светофора? Конечно. Если цвет красный, вы останавливаетесь. Если зелёный – идёте. Ну а если жёлтый? Ну, я обычно бегу на жёлтый: жить не могу без адреналина!

>instance YesNo TrafficLight where

>   yesno Red = False

>   yesno _ = True

Ну что ж, мы определили несколько экземпляров, а теперь давайте поиграем с ними:

>ghci> yesno $ length []

>False

>ghci> yesno "ха-ха"

>True

>ghci> yesno ""

>False

>ghci> yesno $ Just 0

>True

>ghci> yesno True

>True

>ghci> yesno EmptyTree

>False

>ghci> yesno []

>False

>ghci> yesno [0,0,0]

>True

>ghci> :t yesno

>yesno :: (YesNo a) => a –> Bool

Та-ак, работает. Теперь сделаем функцию, которая работает, как оператор >if, но со значениями типов, для которых есть экземпляр класса >YesNo:

>yesnoIf :: (YesNo y) => y –> a –> a –> a

>yesnoIf yesnoVal yesResult noResult =

>     if yesno yesnoVal

>         then yesResult

>         else noResult

Всё довольно очевидно. Функция принимает значение для определения истинности и два других параметра. Если значение истинно, возвращается первый параметр; если нет – второй.