Получение экземпляра функтора, а не последнего аргумента типа

Связанный с этим вопросом, который я задал ранее сегодня.

У меня есть тип данных AST с большим количеством случаев, который параметризуется типом "аннотации"

data Expr ann def var = Plus a Int Int
    | ...
    | Times a Int Int
    deriving (Data, Typeable, Functor)

У меня есть конкретные примеры для def и var, скажем Def а также Var,

То, что я хочу, это автоматически получить fmap который действует как функтор первого аргумента. Я хочу получить функцию, которая выглядит следующим образом:

fmap :: (a -> b) -> (Expr a Def Var) -> (Expr b Def Var)

Когда я использую нормально fmapЯ получаю сообщение компилятора, которое указывает, что fmap пытается применить свою функцию к последнему аргументу типа, а не к первому.

Есть ли способ, которым я могу получить функцию, как описано, не написав кучу шаблонов? Я пытался сделать это:

newtype Expr' a = E (Expr a Def Var)
    deriving (Data, Typeable, Functor)

Но я получаю следующую ошибку:

  Constructor `E' must use the type variable only as the last argument of a data type

Я работаю с чужой кодовой базой, поэтому было бы идеально, если бы мне не приходилось везде менять порядок аргументов типа.

2 ответа

Решение

Короткий ответ: это невозможно, потому что Functor требует, чтобы изменяемая переменная типа была в последней позиции. Только конструкторы типа * -> * могу иметь Functor случаи, и ваши Expr такого не бывает.

Вы действительно нуждаетесь в Functor пример? Если вы просто хотите избежать шаблона написания fmap-подобной функции, лучше использовать SYB, но на самом деле шаблон не так уж и плох, и вы бы написали его только один раз).

Если тебе надо Functor по какой-то другой причине (возможно, вы хотите использовать эту структуру данных в некоторой функции с Functor ограничение), вам придется выбрать, хотите ли вы экземпляр или переменные типа в текущем порядке.

Вы можете использовать синоним типа для минимизации изменений исходного кода:

data Expr' def var ann = Plus a Int Int   -- change this to Expr', correct order
    | ...
    | Something (Expr ann def var)        -- leave this as it is, with the original order
    deriving (Data, Typeable, Functor)

type Expr ann def var = Expr' def var ann

Остальная часть кода может продолжать использовать Expr, без изменений. Единственными исключениями являются экземпляры классов, такие как Functor, что, как вы заметили, требует определенного порядка в параметрах. С надеждой Functor это единственный такой класс, который вам нужен.

Авто производный fmap функция имеет тип

fmap :: (a -> b) -> Expr' def var a -> Expr' def var b

который можно записать как

fmap :: (a -> b) -> Expr a def var -> Expr b def var
Другие вопросы по тегам