packages feed

th-lego-0.3: library/THLego/Helpers.hs

module THLego.Helpers where

import qualified Data.Text as Text
import Language.Haskell.TH.Syntax
import THLego.Prelude
import qualified TemplateHaskell.Compat.V0208 as Compat

-- * Decs

typeSynonymDec :: Name -> Type -> Dec
typeSynonymDec a b =
  TySynD a [] b

recordNewtypeDec :: Name -> Name -> Type -> Dec
recordNewtypeDec _name _accessorName _type =
  NewtypeD [] _name [] Nothing _con []
  where
    _con =
      RecC _name [(_accessorName, noBang, _type)]

normalNewtypeDec :: Name -> Type -> Dec
normalNewtypeDec _name _type =
  NewtypeD [] _name [] Nothing _con []
  where
    _con =
      NormalC _name [(noBang, _type)]

recordAdtDec :: Name -> [(Name, Type)] -> Dec
recordAdtDec typeName fields =
  DataD [] typeName [] Nothing [con] []
  where
    con =
      RecC typeName (fmap (\(fieldName, fieldType) -> (fieldName, fieldBang, fieldType)) fields)

productAdtDec :: Name -> [Type] -> Dec
productAdtDec typeName memberTypes =
  DataD [] typeName [] Nothing [con] []
  where
    con =
      NormalC typeName (fmap ((fieldBang,)) memberTypes)

sumAdtDec :: Name -> [(Name, [Type])] -> Dec
sumAdtDec a b =
  DataD [] a [] Nothing (fmap (uncurry sumCon) b) []

sumCon :: Name -> [Type] -> Con
sumCon a b =
  NormalC a (fmap (fieldBang,) b)

enumDec :: Name -> [Name] -> Dec
enumDec a b =
  DataD [] a [] Nothing (fmap (\c -> NormalC c []) b) []

-- *

textName :: Text -> Name
textName =
  mkName . Text.unpack

textTyLit :: Text -> TyLit
textTyLit =
  StrTyLit . Text.unpack

noBang :: Bang
noBang =
  Bang NoSourceUnpackedness NoSourceStrictness

fieldBang :: Bang
fieldBang =
  Bang NoSourceUnpackedness SourceStrict

multiAppT :: Type -> [Type] -> Type
multiAppT base args =
  foldl' AppT base args

multiAppE :: Exp -> [Exp] -> Exp
multiAppE base args =
  foldl' AppE base args

arrowChainT :: [Type] -> Type -> Type
arrowChainT params result =
  foldr (\a b -> AppT (AppT ArrowT a) b) result params

appliedTupleT :: [Type] -> Type
appliedTupleT a =
  foldl' AppT (TupleT (length a)) a

appliedTupleOrSingletonT :: [Type] -> Type
appliedTupleOrSingletonT =
  \case
    [a] -> a
    a -> appliedTupleT a

appliedTupleE :: [Exp] -> Exp
appliedTupleE =
  Compat.tupE

appliedTupleOrSingletonE :: [Exp] -> Exp
appliedTupleOrSingletonE =
  \case
    [a] -> a
    a -> appliedTupleE a

nameString :: Name -> String
nameString (Name (OccName x) _) =
  x

decimalIndexName :: Int -> Name
decimalIndexName =
  mkName . showChar '_' . show

alphabeticIndexName :: Int -> Name
alphabeticIndexName a =
  mkName string
  where
    string =
      showIntAtBase 26 (chr . (+) 97) a ""

enumAlphabeticNames :: Int -> [Name]
enumAlphabeticNames =
  fmap alphabeticIndexName . enumFromTo 0 . pred

-- |
-- Map every element of a list with a new name.
{-# INLINE mapWithAlphabeticName #-}
mapWithAlphabeticName :: (Name -> a -> b) -> [a] -> [b]
mapWithAlphabeticName f list =
  foldr step (const []) list 0
  where
    step a next !index =
      f (alphabeticIndexName index) a : next (succ index)

aName :: Name
aName =
  mkName "a"

bName :: Name
bName =
  mkName "b"

cName :: Name
cName =
  mkName "c"

eqConstraintT :: Name -> Type -> Type
eqConstraintT name =
  AppT (AppT EqualityT (VarT name))

-- *

applicativeChainE :: Exp -> [Exp] -> Exp
applicativeChainE mappingE apEList =
  case apEList of
    h : t ->
      intersperseInfixE
        (VarE '(<*>))
        (InfixE (Just mappingE) (VarE '(<$>)) (Just h) :| t)
    _ ->
      AppE (VarE 'pure) mappingE

intersperseInfixE :: Exp -> NonEmpty Exp -> Exp
intersperseInfixE op =
  foldl1 (\l r -> InfixE (Just l) op (Just r))

textLitE :: Text -> Exp
textLitE =
  LitE . StringL . Text.unpack