Допишите ограничение в сигнатуре. Предскажите результат вызова.
showWithPrefix :: ____________________ => a ->String->StringshowWithPrefix x prefix = prefix ++show xshowWithPrefix Warning"level: "
showWithPrefix ::Show a => a ->String->String
Результат: "level: WARNING".
ΔShow a => — требование: для типа a должна быть доступна реализация Show.
Задачки: инстанс для пары
Напишите целиком инстанс Show для пары (a, b), включая необходимые ограничения. Формат результата: "(True,3)" для (True, 3 :: Int).
instance (Show a, Show b) =>Show (a, b) whereshow (x, y) ="("++show x ++","++show y ++")"
Задачки: свой класс
Придумайте и напишите целиком класс для парсинга данных MyRead. Его метод должен принимать строку и возвращать разобранное значение либо отсутствие результата при неудаче.
classMyRead a where readMaybe ::String->Maybe a
Варьируемый типовой параметр может использоваться где угодно.
Все значения типа
Напишите сигнатуру и реализацию allValues, перебирающей все значения конечного перечислимого типа. Какие классы нужны для границ и перехода к следующему значению?
allValues :: (Enum a, Bounded a) => [a]allValues = [minBound..maxBound]
allValues :: [Bool] даёт [False, True].
Равенство функций на конечной области
Реализуйте проверку равенства функций: напишите целиком инстанс Eq для (a -> b) с необходимыми ограничениями. Область входов конечная и небольшая; используйте allValues.
instance (Enum a, Bounded a, Eq b) =>Eq (a -> b) where (==) :: (a -> b) -> (a -> b) ->Bool f == g =map f allValues ==map g allValues
NB Полный перебор полезен лишь для небольшой конечной области, например Bool. Для Int такой способ непрактичен.
Один класс требует другой
Нужны x == y и x < y. Достаточно ли Ord a или нужно (Eq a, Ord a)? Проверьте по объявлению класса.
classEq a =>Ord a where compare :: a -> a ->Ordering-- ...
Ord a уже требует Eq a — отдельно повторять его не нужно.
Классы типов высших кайндов
map — одна идея для разных структур
Знакомый map, transform из П5 и преобразование значения внутри Maybe.
map :: (a -> b) -> [a] -> [b]transform :: (a -> b) -> Tree a -> Tree bmapMaybe :: (a -> b) -> Maybe a -> Maybe b
список
дерево из П5
Maybe
True
False
True
False
True
Just
True
map — одна идея для разных структур
В каждой структуре применяем not к значениям; форма структуры сохраняется.
map :: (a -> b) -> [a] -> [b]transform :: (a -> b) -> Tree a -> Tree bmapMaybe :: (a -> b) -> Maybe a -> Maybe b
список
дерево из П5
Maybe
not True
not False
not True
not False
not True
Just
not True
map — одна идея для разных структур
Одно имя fmap: конкретный конструктор типа выбирает реализацию.
fmap :: (a -> b) -> [a] -> [b]fmap :: (a -> b) -> Tree a -> Tree bfmap :: (a -> b) -> Maybe a -> Maybe b
список
дерево из П5
Maybe
False
True
False
True
False
Just
False
map — одна идея для разных структур
Functor объединяет эти операции: параметр f задаёт структуру.
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
f = []
f = Tree
f = Maybe
False
True
False
True
False
Just
False
fmap: меняется значение, остаётся структура
Предскажите fmap show (Just True) и fmap show Nothing. Что должно сохраняться?
fmap show (Just True)
Just
True
структура
значение типа Bool
fmap: меняется значение, остаётся структура
Функция show применяется к значению внутри Just: из Bool получается String.
Just (show True)
Just
show True
структура
преобразуем только значение
fmap: меняется значение, остаётся структура
Конструктор Just сохраняется; значение и его тип меняются.
Just "True"
Just
"True"
структура
результат типа String
Функторы: от разбора к пропускам
e.g. Разбор Maybe:
instanceFunctorMaybewherefmap f Nothing=Nothingfmap f (Just x) =Just (f x)
Допишите инстанс списка по его конструкторам.
instanceFunctor [] wherefmap f [] = ____________________fmap f (x:xs) = __________________________
instanceFunctor [] wherefmap f [] = []fmap f (x:xs) = f x :fmap f xs
Это map: можно написать fmap = map.
Частичное применение типовых конструкторов
Определите кайнды (,), (->) и (,) Int. Реализуйте функтор для пары.
(,) :: * -> * -> *
(->) :: * -> * -> *
(,) Int :: * -> *
instanceFunctor ((,) e) where-- (,) :: * -> * -> * fmap :: (a -> b) -> ((,) e) a -> ((,) e) b-- :: (a -> b) -> (e, a) -> (e, b)fmap f (w, x) = (w, f x)
Что теперь умеем
Использовать один символ для разных типов
Определять классы типов и их инстансы
Использовать частичное применение типовых конструкторов
⚙ Реализация классов типов в GHC
⚙ Полиморфная сортировка: базовые классы типов
Хотим сортировать элементы разных типов. Как их сравнивать?
sort :: [a] -> [a]
⚙ Полиморфная сортировка: базовые классы типов
Передаём сравнение явно: для каждого типа — своя реализация.
sort :: (a -> a ->Bool) -> [a] -> [a]
⚙ Полиморфная сортировка: базовые классы типов
Объединяем операции в запись — словарь.
dataMyOrd a =MyOrd { less :: a -> a ->Bool }sort ::MyOrd a -> [a] -> [a]
intMyOrd ::MyOrdIntintMyOrd =MyOrd { less = (<) }
sort intMyOrd [3,2,1]
Результат: [1,2,3].
⚙ Свойство упорядоченности
Через data
Определение словаря функций.
dataMyOrd a =MyOrd { less :: a -> a ->Bool }
Экземпляр словаря для конкретного типа — именованное значение.
intMyOrd ::MyOrdIntintMyOrd =MyOrd { less = (<) }
Явный параметр функции.
sort ::MyOrd a -> [a] -> [a]
Передаётся пользователем.
test =sort intMyOrd [3, 2, 1]
Через class
Определение класса типов.
classMyOrd a where less :: a -> a ->Bool
Объявление типа представителем класса — не имеет имени.
instanceMyOrdIntwhere less = (<)
Неявный параметр функции.
sort ::MyOrd a => [a] -> [a]
Передаётся компилятором.1
test =sort [3, 2, 1]
⚙ От словаря элемента к словарю списка
Строитель словаря и зависимый инстанс выражают одно требование.
Явно
listMyOrd ::MyOrd a ->MyOrd [a]listMyOrd aOrd =MyOrd { less =... }
Неявно
instanceMyOrd a =>MyOrd [a] where less =...
sort (listMyOrd intMyOrd) [[3,2], [], [1]]
⇒intMyOrd даёт сравнение элементов; listMyOrd intMyOrd — сравнение списков.
⚙ От словаря элемента к словарю списка
Строитель словаря и зависимый инстанс выражают одно требование.
Явно
listMyOrd ::MyOrd a ->MyOrd [a]listMyOrd aOrd =MyOrd { less =... }