Deriving an instance of functor, not an argument of the last type
Refers to this issue , which I already talked about today.
I have an AST datatype with a lot of cases, which is parameterized by the "annotation" type
data Expr ann def var = Plus a Int Int
| ...
| Times a Int Int
deriving (Data, Typeable, Functor)
I have specific instances for def and var like Def
and Var
.
I want to automatically deduce fmap
which works like a functor on the first argument. I want to get a function that looks like this:
fmap :: (a -> b) -> (Expr a Def Var) -> (Expr b Def Var)
When I use normal fmap
, I get a compiler message that indicates that fmap is trying to apply its function to an argument of the last type, not the first.
Is there a way I can get as described without writing a bunch of boilerplate? I tried to do this:
newtype Expr' a = E (Expr a Def Var)
deriving (Data, Typeable, Functor)
But I am getting the following error:
Constructor `E' must use the type variable only as the last argument of a data type
I'm working with some other code base, so it would be ideal if I didn't have to switch the order of type arguments everywhere.
source to share
Short answer: this is not possible because it Functor
requires the type variable to change at the last position. Only type constructors * -> *
can have instances Functor
, and yours Expr
doesn't have that type.
Do you really need a copy Functor
? If you just want to avoid the pattern of writing a function like fmap, something like SYB is the best solution (but actually the pattern pattern is not that bad and you only write it once).
If you need it Functor
for any other reason (perhaps you want to use this data structure in some constrained function Functor
), you will need to choose whether you want the instance or type variables in the current order.
source to share
You can use a type synonym to minimize source code changes:
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
The rest of the code can continue to use Expr
unchanged. The only exceptions are instances of the class, such as Functor
, which you have noticed, require a certain ordering in the parameters. Hopefully this Functor
is the only such class you need.
The auto-derivative function fmap
is of type
fmap :: (a -> b) -> Expr' def var a -> Expr' def var b
which can be written as
fmap :: (a -> b) -> Expr a def var -> Expr b def var
source to share