Conversion from type `T a` to` T b` without template

So I have an AST datatype with a lot of cases, which is parameterized by the "annotation" type

data Expr a = Plus a Int Int
    | ...
    | Times a Int Int

      

I have annotation types S

and T

as well as some function f :: S -> T

. I want to take Expr S

and convert it to Expr T

using my conversion f

for each S

that occurs within the Expr value.

Is there a way to do this with SYB or generics and avoid having to match patterns in each case? It looks like the type of thing for which it fits. I'm just not familiar with SYB to know how to do this.

+3


source to share


2 answers


It looks like you need an instance Functor

. This can be automatically deduced by GHC using the extension DeriveFunctor

.



+6


source


Based on your follow-up question, it seems that a generics library is more appropriate for your situation than Functor. I would recommend using the function listed on the SYB wiki page :

{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables, FlexibleContexts #-}
import Data.Generics
import Unsafe.Coerce

newtype C a = C a deriving (Data,Typeable)

fmapData :: forall t a b. (Typeable a, Data (t (C a)), Data (t a)) =>
    (a -> b) -> t a -> t b
fmapData f input = uc . everywhere (mkT $ \(x::C a) -> uc (f (uc x)))
                    $ (uc input :: t (C a))
    where uc = unsafeCoerce

      



The reason for the extra type C

is to avoid the problematic corner case where there are occurrences of fields of the same type as a

(more on wiki). The caller fmapData

does not need to see it.

This function has several additional requirements compared to the real one fmap

: there must be instances Typeable

for a

and Data

for t a

. In your case t a

there is Expr a

which means you need to add deriving Data

to the definition Expr

and also have an instance Data

in scope for any a

re using.

+2


source







All Articles