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.

+3


source to share


2 answers


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.

+3


source


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

      

+2


source







All Articles