free-foil-0.3.2: Efficient Type-Safe Capture-Avoiding Substitution for Free (Scoped Monads)
Safe HaskellNone
LanguageHaskell2010

Data.ZipMatchK.TH

Description

Template Haskell derivation of ZipMatchK instances.

The generic instance (the one you get by writing instance ZipMatchK TermSig with no body) converts a node into its Generics.Kind representation on every comparison, and converts the result back. The representation of a constructor is a chain of L1/R1 wrappers as long as that constructor's index, so the cost grows with the number of constructors in the signature, and comparing terms is most of what a typechecker does.

The derivers here generate the instance that one would otherwise write out by hand: a case over the two nodes, allocating only its result. On a 44-constructor signature that is worth a factor of 1.8 in time and 2.3 in allocation on alphaEquiv (see the zipmatchk benchmark), and the derived instance does not get slower as the signature grows.

The module the splice appears in needs at least

{-# LANGUAGE GADTs #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

Signatures that refer to one another — as the ones generated by mkFreeFoil from a grammar with several syntactic categories do — have to be derived in a single splice, since a top-level splice ends a declaration group and an instance from a later group is not visible to an earlier one:

concat <$> traverse deriveZipMatchK2 [''Term'Sig, ''OpArg'Sig, ''Type'Sig]
Synopsis

Documentation

deriveZipMatchK :: Name -> Q [Dec] Source #

Derive a ZipMatchK instance, zipping all type parameters.

For a signature bifunctor

data TermSig scope term = AppSig term term | LamSig scope
deriveZipMatchK ''TermSig

this generates

instance ZipMatchK TermSig where
  zipMatchWithK (f :^: g :^: M0) x y = case (x, y) of
    (AppSig l1 l2, AppSig r1 r2) -> AppSig <$> g l1 r1 <*> g l2 r2
    (LamSig l1, LamSig r1)       -> LamSig <$> f l1 r1
    _                            -> Nothing

Use deriveZipMatchK2 when the type has extra parameters that should stay fixed (an annotation, say), as a signature generated by Control.Monad.Free.Foil.TH.MkFreeFoil does.

deriveZipMatchK1 :: Name -> Q [Dec] Source #

Derive a ZipMatchK instance for a functor, zipping the last type parameter and fixing the rest.

deriveZipMatchK2 :: Name -> Q [Dec] Source #

Derive a ZipMatchK instance for a signature bifunctor, zipping the last two type parameters (the scoped terms and the terms) and fixing the rest.

For a signature with an extra parameter (a source position, for instance)

data Term'Sig a scope term = AppSig a term term | LamSig a scope
deriveZipMatchK2 ''Term'Sig

this generates

instance ZipMatchK a => ZipMatchK (Term'Sig a) where
  zipMatchWithK (f :^: g :^: M0) x y = case (x, y) of
    (AppSig l1 l2 l3, AppSig r1 r2 r3) ->
      AppSig <$> zipMatchWithK M0 l1 r1 <*> g l2 r2 <*> g l3 r3
    ...

deriveZipMatchKWith :: Maybe Int -> Name -> Q [Dec] Source #

Derive a ZipMatchK instance, zipping the last n type parameters and fixing the rest. Nothing zips all of them.

Every fixed parameter that occurs in a field gets a ZipMatchK constraint in the instance context.