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