Примеры синонимов с несколькими параметрами

Я пытаюсь выяснить, возможно ли (и как) определять экземпляры экземпляров для синонимов с несколькими параметрами.

Например:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

type F a b = a -> b
data DF a b = DF (a -> b)

class C c a b where
    doc :: c a b -> a -> b

Он работает для экземпляра с несколькими параметрами:

instance C DF a b where
    doc (DF f) x = f x

Но это не работает для синонимов типов:

-- ERROR:
--
-- Type synonym `F' should have 2 arguments, but has been given none
-- In the instance declaration for `C F a b'
--
instance C F a b where
    doc f x = f x

Можно ли определить экземпляр класса типа для F?

Ответ 1

Это невозможно, как написано. Типичные синонимы должны быть полностью применены для их использования, особенно как параметр класса типа.

Обратите внимание, что если вы можете eta-уменьшить синоним типа, возможно, экземпляр возможен; это синоним, который должен быть полностью применен, а не тот тип, к которому он относится. Таким образом, это сработает:

type F = (->)

instance C F a b where
    doc f x = f x

Существует расширение LiberalTypeSynonyms, которое смягчает некоторые правила о расширении синонимов типов, но здесь это не помогает - это только позволяет делать такие вещи, как дать частично примененный синоним типа как параметр типа синонима другого типа. Все должно быть полностью расширено для использования в противном случае.

Чтобы увидеть, почему это ограничение необходимо, рассмотрите синоним следующего типа:

type Flip f a b = f b a

И следующий пример:

instance Functor (Flip Either a) where
    fmap _ (Right x) = Right x
    fmap f (Left x) = Left (f x)

Вспомните, что есть также экземпляр Functor (Either a), который делает то же самое, кроме зеркального отображения. Оба являются разумными экземплярами Functor.

Помня о том, что в отличие от newtype, синонимы типов считаются такими же, как тип, на который они ссылаются, каково должно быть значение выражения fmap not (Right True :: Either Bool Bool)?

Ответ 2

Синонимы типов должны быть полностью применены для того, чтобы экземпляр мог быть определен для них. Тип F не является * -> * -> *, как можно было бы ожидать, но вместо этого недействителен до тех пор, пока не будут предоставлены еще два аргумента типа. Попробуйте

type F = (->)

вместо.