How can I programatically produce this datatype from the other?

痞子三分冷 提交于 2019-12-05 20:50:50

Template Haskell is what you want since you are trying to generate declarations. Here is something that works. Put the following in one file called Tag.hs:

{-# LANGUAGE TemplateHaskell #-}

module Tag where

import Language.Haskell.TH

makeTag :: Name -> DecsQ
makeTag name = do
    -- Reify the data declaration to get the constructors.
    -- Note we are forcing there to be no type variables...
    (TyConI (DataD _ _ [] _ cons _)) <- reify name

    pure [ DataD [] tagTyName [PlainTV (mkName "a")] Nothing (fmap tagCon cons) [] ]
  where
  -- Generate the name for the new tag GADT type constructor.
  tagTyName :: Name
  tagTyName = mkName ("Tag" ++ nameBase name)

  -- Given a constructor, construct the corresponding constructor for the GADT.
  tagCon :: Con -> Con
  tagCon (NormalC conName args) =
    let tys = fmap snd args
        tagType = foldl AppT (TupleT (length tys)) tys
    in GadtC [mkName ("Tag" ++ nameBase conName)] []
             (AppT (ConT tagTyName) tagType)

Then you can test it out in another file:

{-# LANGUAGE TemplateHaskell, GADTs #-}

import Tag

data SomeUserType1 = Foo Int | Bar String
data SomeUserType2 = Fooo Int | Baar Char | Baaz Bool String

makeTag ''SomeUserType1
makeTag ''SomeUserType2

If you inspect the second file in GHCi (or look at the generated code by passing -ddump-splices to either ghci or ghc) you'll see that the following is generated:

data TagSomeUserType1 a where
  TagFoo :: TagSomeUserType1 Int
  TagBar :: TagSomeUserType1 String

data TagSomeUserType3 a where
  TagFooo :: TagSomeUserType2 Int
  TagBaar :: TagSomeUserType2 Char
  TagBaaz :: TagSomeUserType2 (Bool, String)

I have to use mkName and not newName because, if you are ever expected to use these generated GADTs, you'll need them to have predictable names you can write. As should be clear from the examples, my convention is to prepend Tag to both the type and data constructors.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!