instance Functor A where
вместо instance Functor (A a) where
.
Почему это работает?
Мне легче понять, используя систему сортировки GHC. Давайте начнем с простого случая и поэкспериментируем в GHCi:
> :k Eq Int
Eq Int :: Constraint
Это говорит нам о том, что Eq Int
является ограничением, некоторым свойством, которое может быть проверено во время проверки типа. Действительно, если мы введем check (12 :: Int) == (42 :: Int)
, компилятор проверит, что целые числа можно сравнить, разрешив ограничение Eq Int
.
Что такое Eq
, имя класса без параметра Int
?
> :k Eq
Eq :: * -> Constraint
Это говорит нам о том, что Eq
можно рассматривать как функцию от типов (*
- это тип типов) до ограничения.
Действительно, в Eq Int
, Int
является типом, поэтому у нас Int :: *
делает Int
аргумент с хорошим характером для передачи Eq
.
Достаточно классов классов, как насчет конструкторов типов?
> :k Maybe Int
Maybe Int :: *
Не удивительно, Maybe Int
это тип
> :k Maybe
Maybe :: * -> *
Maybe
- это функция от типов к типам (*->*
). Это действительно то, что делает конструктор типа Maybe
: отображение типа (Int
) на тип (Maybe Int
).
Вернуться к исходному вопросу. Почему мы не можем написать instance Functor (A a)
, но мы можем вместо этого написать instance Functor A
? Ну, у нас есть это
> :k A Int
A Int :: *
> :k A
A :: * -> *
и, самое главное,
> :k Functor
Functor :: (* -> *) -> Constraint
Это говорит о том, что тип класса Functor
отличается от класса Eq
. Eq
ожидает тип в качестве аргумента, в то время как Functor
ожидает что-то вроде (* -> *)
в качестве аргумента. A
соответствует этому виду, а A Int
- нет.
Это происходит, когда в определении класса аргумент применяется к какому-либо другому типу. Э.Г.
class C1 a where
foo :: a -> Bool
приводит к C1 :: * -> Constraint
. Вместо
class C2 f where
bar :: f Int -> Bool
приводит к C2 :: (* -> *) -> Constraint
, поскольку f
само по себе не используется как тип, f Int
- так, f
должен быть параметризованным типом вида * -> *
.