Я взглянул на пример , связанный со статьей, на которую ссылался Делнан.Посмотрев немного, я, наконец, понял, что происходит:
Он начинается с этого типа:
class BuildList a r | r-> a where
build' :: [a] -> a -> r
Этот бит после канала (|) являетсяфункциональная зависимость.Это говорит о том, что тип, представленный a
, может быть определен типом, представленным r
.Другими словами, вам запрещено определять два экземпляра класса типов BuildList
с одним и тем же r
(тип возвращаемого значения), но другим a
.
Немного перепрыгивая туда, где build'
фактически используется функция:
> build True :: [Bool]
Поскольку build
просто вызывает build'
с пустым списком в качестве первого параметра, это то же самое, что:
> build' [] True :: [Bool]
В этомНапример, build'
явно возвращает список.Из-за функциональной зависимости мы можем только привязываться к этому экземпляру класса BuildList
:
instance BuildList a [a] where
build' l x = reverse$ x:l
Довольно просто.Второй пример более интересный.Расширяя определение build
, оно становится:
> build' [] True False :: [Bool]
Какой тип build'
в данном случае?Что ж, правила предшествования в Haskell означают, что вышеприведенное также можно записать так:
> (build' [] True) False :: [Bool]
Теперь становится ясно, что мы передаем два параметра в build'
и затем применяем результат этого выражения кпараметр со значением «False».Другими словами, ожидается, что выражение (build' [] True)
вернет функцию типа Bool -> [Bool]
.И это связывает нас со вторым экземпляром класса типов BuildList
:
instance BuildList a r => BuildList a (a->r) where
build' l x y = build'(x:l) y
В этом вызове l = []
и x = True
и y = False
, поэтому определение расширяется до build' [True] False :: [Bool]
.Эта подпись связана с первым экземпляром build'
, и довольно очевидно, откуда она идет.