packages feed

th-lego-0.2: library/THLego/Lambdas.hs

module THLego.Lambdas
where

import THLego.Prelude
import THLego.Helpers
import Language.Haskell.TH
import qualified TemplateHaskell.Compat.V0208 as Compat


{-|
Simulates lambda-case without the need for extension.
-}
matcher :: [Match] -> Exp
matcher matches =
  LamE [VarP aName] (CaseE (VarE aName) matches)

{-|
Lambda expression, which extracts a product member by index.
-}
productGetter ::
  {-| Constructor name. -}
  Name ->
  {-| Total amount of members. -}
  Int ->
  {-| Index of the member. -}
  Int ->
  {-|
  Lambda expression of the following form:
  
  > product -> member
  -}
  Exp
productGetter conName numMembers index =
  LamE [pat] exp
  where
    varName =
      alphabeticIndexName index
    pat =
      ConP conName pats
      where
        pats =
          replicate index WildP <>
          pure (VarP varName) <>
          replicate (numMembers - index - 1) WildP
    exp =
      VarE varName

{-|
Lambda expression, which sets a product member by index.
-}
productSetter ::
  {-| Constructor name. -}
  Name ->
  {-| Total amount of members. -}
  Int ->
  {-| Index of the member. -}
  Int ->
  {-|
  Lambda expression of the following form:

  > product -> member -> product
  -}
  Exp
productSetter conName numMembers index =
  LamE [stateP, valP] exp
  where
    memberName =
      alphabeticIndexName index
    memberNames =
      fmap alphabeticIndexName (enumFromTo 0 (pred numMembers))
    stateP =
      ConP conName pats
      where
        pats =
          fmap VarP memberNames
    valP =
      VarP memberName
    exp =
      foldl' AppE (ConE conName) (fmap VarE memberNames)

{-|
Lambda expression, which maps a product member by index.
-}
productMapper ::
  {-| Constructor name. -}
  Name ->
  {-| Total amount of members. -}
  Int ->
  {-| Index of the member. -}
  Int ->
  {-|
  Lambda expression of the following form:

  > (member -> member) -> product -> product
  -}
  Exp
productMapper conName numMembers index =
  LamE [mapperP, stateP] exp
  where
    memberName =
      alphabeticIndexName index
    memberNames =
      fmap alphabeticIndexName (enumFromTo 0 (pred numMembers))
    valName =
      alphabeticIndexName index
    fnName =
      mkName "fn"
    mapperP =
      VarP fnName
    stateP =
      ConP conName pats
      where
        pats =
          fmap VarP memberNames
    exp =
      foldl' AppE (ConE conName) $
        fmap (VarE . alphabeticIndexName) (enumFromTo 0 (pred index)) <>
        pure (AppE (VarE fnName) (VarE valName)) <>
        fmap (VarE . alphabeticIndexName) (enumFromTo (succ index) (pred numMembers))

{-|
Lambda expression, which maps a sum member by index.
-}
sumMapper ::
  {-| Constructor name. -}
  Name ->
  {-| Total amount of members. -}
  Int ->
  {-|
  Lambda expression of the following form:

  > (membersTuple -> membersTuple) -> sum -> sum
  -}
  Exp
sumMapper conName numMembers =
  LamE [mapperP] (matcher matches)
  where
    fnName =
      mkName "fn"
    mapperP =
      VarP fnName
    matches =
      [pos, neg]
      where
        pos =
          Match (ConP conName memberPats) (NormalB bodyExp) []
          where
            memberVarNames =
              fmap alphabeticIndexName (enumFromTo 0 (pred numMembers))
            memberPats =
              fmap VarP memberVarNames
            bodyExp =
              AppE (tupleToProduct conName numMembers)
                (multiAppE (VarE fnName) (fmap VarE memberVarNames))
        neg =
          Match (VarP aName) (NormalB (VarE aName)) []

adtConstructorNarrower :: Name -> Int -> Exp
adtConstructorNarrower conName numMembers =
  matcher [positive, negative]
  where
    positive =
      Match (ConP conName (fmap VarP varNames)) (NormalB exp) []
      where
        varNames =
          fmap alphabeticIndexName (enumFromTo 0 (pred numMembers))
        exp =
          AppE (ConE 'Just) (Compat.tupE (fmap VarE varNames))
    negative =
      Match WildP (NormalB (ConE 'Nothing)) []

enumConstructorToBool :: Name -> Exp
enumConstructorToBool constructorName =
  matcher [positive, negative]
  where
    positive =
      Match (ConP constructorName []) (NormalB bodyExp) []
      where
        bodyExp =
          ConE 'True
    negative =
      Match WildP (NormalB bodyExp) []
      where
        bodyExp =
          ConE 'False

singleConstructorAdtToTuple :: Name -> Int -> Exp
singleConstructorAdtToTuple conName numMembers =
  LamE [pat] exp
  where
    varNames =
      fmap alphabeticIndexName (enumFromTo 0 (pred numMembers))
    pat =
      ConP conName (fmap VarP varNames)
    exp =
      Compat.tupE (fmap VarE varNames)

tupleToProduct :: Name -> Int -> Exp
tupleToProduct conName numMembers =
  LamE [pat] exp
  where
    varNames =
      fmap alphabeticIndexName (enumFromTo 0 (pred numMembers))
    pat =
      TupP (fmap VarP varNames)
    exp =
      multiAppE (ConE conName) (fmap VarE varNames)

namedFieldSetter :: Name -> Exp
namedFieldSetter fieldName =
  LamE [VarP aName, VarP bName] (RecUpdE (VarE aName) [(fieldName, VarE bName)])