Skip to content

Instantly share code, notes, and snippets.

@cblp
Created May 3, 2017 13:22
Show Gist options
  • Save cblp/bd445c74df288287b904d7a797cfefa4 to your computer and use it in GitHub Desktop.
Save cblp/bd445c74df288287b904d7a797cfefa4 to your computer and use it in GitHub Desktop.
Get index of constructor in its type declaration.
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
module ConIndex (conIndex) where
import Data.List (findIndex)
import Language.Haskell.TH (Con (..), Dec (DataD), ExpQ,
Info (DataConI, TyConI), Name, ParentName,
integerL, litE, reify)
-- | Get index of constructor in its type declaration.
-- An alternative to 'fromEnum' for the case of complicated constructors like
-- @... = Foo Bar | Baz Qux IORef@.
--
-- Result is an integer literal expression.
--
-- >>> $(conIndex 'Nothing)
-- 0
-- >>> $(conIndex 'Just)
-- 1
conIndex :: Name -> ExpQ
conIndex name = do
nameInfo <- reify name
typeName <- case nameInfo of
DataConI _ _ (typeName :: ParentName) _ -> pure typeName
_ ->
fail . unwords $
[ "conIndex: argument is expected to be a data constructor name,"
, "but it is", show nameInfo
]
typeInfo <- reify typeName
typeDeclaration <- case typeInfo of
TyConI (typeDeclaration :: Dec) -> pure typeDeclaration
_ ->
fail . unwords $
[ "conIndex: argument is expected to be a data constructor name,"
, "but it is a constructor of", show typeInfo
]
cons <- case typeDeclaration of
DataD _ _ _ (cons :: [Con]) _ -> pure cons
_ ->
fail . unwords $
[ "conIndex: argument is expected to be a data constructor name,"
, "but it is a constructor of type", show typeDeclaration
]
i <-
case findIndex ((name ==) . conName) cons of
Nothing ->
fail . unwords $
[ "conIndex: Impossible! Didn't find constructor", show name
, "in its data declaration", show typeName
]
Just i -> pure i
litE . integerL $ toInteger i
conName :: Con -> Name
conName = \case
NormalC name _ -> name
RecC name _ -> name
InfixC _ name _ -> name
ForallC _ _ con -> conName con
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment