Search code examples
haskelltemplate-haskellaesontype-families

How to auto derive FromJSON using Template Haskell, Aeson, and type families


I would like to use template haskell to auto generate the ToJSON (Bar Baz), or FromJSON (Bar Baz) instances. the deriveJSON is of type Options -> Name -> Q [Dec] how to I construct the Name type when the type takes parameters?

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}

import Data.Aeson
import Data.Aeson.TH

data Baz = Baz String

class Foo a where
  data Bar a :: *


instance Foo Baz where
  data Bar Baz = ExampleRecord { exampleField1 :: String
                               , exampleField2 :: String
                               }
data Biz = Biz String

instance Foo Biz where
  data Bar Biz = ExampleBizRecord1 { biz1 :: String } 
               | ExampleBizRecord2 { biz2 :: String }

type BarBaz = Bar Baz

-- doesn't work
deriveJSON defaultOptions (''Bar ''Baz)
deriveJSON defaultOptions mkName "Bar Baz"

-- Creates the Name but not supported by Data.Aeson.TH
derive JSON defaultOptions ''BarBaz

How to use the deriveJSON when Foo takes a type as a parameter?


Solution

  • If you try the "obvious" thing it won't work, but the TH error gives a helpful hint:

    Exception when trying to run compile-time code:
      Data.Aeson.TH.withType: Cannot use a data family name. Use a data family instance constructor instead.
    Code: deriveJSON defaultOptions ''Bar
    

    Doing what is suggests seems to work:

    deriveJSON defaultOptions 'ExampleRecord 
    

    gets you an instance {From/To}JSON (Bar Baz).


    The same thing works for the update example. Just use any one constructor name:

    data Biz = Biz String
    
    instance Foo Biz where
      data Bar Biz = ExampleBizRecord1 { biz1 :: String } 
                   | ExampleBizRecord2 { biz2 :: String }
    
    deriveJSON defaultOptions 'ExampleBizRecord1 
    -- XOR 
    deriveJSON defaultOptions 'ExampleBizRecord2