Получение экземпляра функтора, а не последнего аргумента типа
Связанный с этим вопросом, который я задал ранее сегодня.
У меня есть тип данных 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