packages feed

hypertypes-0.2.2: src/Hyper/TH/Apply.hs

{-# LANGUAGE TemplateHaskell #-}

-- | Generate 'HApply' and related instances via @TemplateHaskell@
module Hyper.TH.Apply
    ( makeHApply
    , makeHApplyAndBases
    , makeHApplicativeBases
    ) where

import Control.Applicative (liftA2)
import qualified Control.Lens as Lens
import Hyper.Class.Apply (HApply (..))
import Hyper.TH.Functor (makeHFunctor)
import Hyper.TH.Internal.Utils
import Hyper.TH.Nodes (makeHNodes)
import Hyper.TH.Pointed (makeHPointed)
import Language.Haskell.TH

import Hyper.Internal.Prelude

-- | Generate instances of 'HApply',
-- 'Hyper.Class.Functor.HFunctor', 'Hyper.Class.Pointed.HPointed' and 'Hyper.Class.Nodes.HNodes',
-- which together form 'HApplicative'.
makeHApplicativeBases :: Name -> DecsQ
makeHApplicativeBases x =
    sequenceA
        [ makeHPointed x
        , makeHApplyAndBases x
        ]
        <&> concat

-- | Generate an instance of 'HApply'
-- along with its bases 'Hyper.Class.Functor.HFunctor' and 'Hyper.Class.Nodes.HNodes'
makeHApplyAndBases :: Name -> DecsQ
makeHApplyAndBases x =
    sequenceA
        [ makeHNodes x
        , makeHFunctor x
        , makeHApply x
        ]
        <&> concat

-- | Generate an instance of 'HApply'
makeHApply :: Name -> DecsQ
makeHApply typeName = makeTypeInfo typeName >>= makeHApplyForType

makeHApplyForType :: TypeInfo -> DecsQ
makeHApplyForType info =
    do
        (name, _, fields) <-
            case tiConstructors info of
                [x] -> pure x
                _ -> fail "makeHApply only supports types with a single constructor"
        let xVars = makeConstructorVars "x" fields
        let yVars = makeConstructorVars "y" fields
        instanceD
            (makeContext info >>= simplifyContext)
            [t|HApply $(pure (tiInstance info))|]
            [ InlineP 'hzip Inline FunLike AllPhases & PragmaD & pure
            , funD
                'hzip
                [ clause
                    [ consPat name xVars
                    , consPat name yVars
                    ]
                    (normalB (foldl appE (conE name) (zipWith f xVars yVars)))
                    []
                ]
            ]
            <&> (: [])
    where
        bodyFor (Right x) = bodyForPat x
        bodyFor Left{} = [|(<>)|]
        bodyForPat Node{} = [|(:*:)|]
        bodyForPat GenEmbed{} = [|hzip|]
        bodyForPat FlatEmbed{} = [|hzip|]
        bodyForPat (InContainer _ pat) = [|liftA2 $(bodyForPat pat)|]
        f (p, x) (_, y) = [|$(bodyFor p) $(varE x) $(varE y)|]

makeContext :: TypeInfo -> Q [Pred]
makeContext info =
    tiConstructors info >>= (^. Lens._3) & traverse ctxFor <&> mconcat
    where
        ctxFor (Right x) = ctxForPat x
        ctxFor (Left x) = [t|Semigroup $(pure x)|] <&> (: [])
        ctxForPat (InContainer t pat) = (:) <$> [t|Applicative $(pure t)|] <*> ctxForPat pat
        ctxForPat (GenEmbed t) = [t|HApply $(pure t)|] <&> (: [])
        ctxForPat (FlatEmbed t) = makeContext t
        ctxForPat _ = pure []