packages feed

free-foil-0.6.0: bench/binders/Main.hs

{-# LANGUAGE BangPatterns        #-}
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE DeriveTraversable   #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell     #-}
{-# LANGUAGE TypeFamilies        #-}
-- The raw types below only serve Template Haskell.
{-# OPTIONS_GHC -Wno-unused-top-binds #-}

-- | What it costs to list the binders of a pattern with
-- 'Foil.nameBinderListOf', and the operations that go through it:
-- 'Foil.addSubstPattern', 'Foil.unsinkNameSet' (in 'supportOf') and the
-- default 'Foil.unifyPatterns' (in 'alphaEquiv').
--
-- The pattern types cover each way to get a 'Foil.CoSinkable' instance: the
-- library's own instances (with 'Telescope'), 'mkCoSinkableFoilPattern',
-- 'mkFreeFoil', instances written by hand and the generic default.
--
-- Each pattern type is measured twice. At a known type, a call can be
-- specialised. Through 'SomePattern', the 'Foil.CoSinkable' dictionary is
-- passed at run time, as it is in code that is polymorphic in the pattern
-- type, such as a generic typechecker.
module Main (main) where

import           Data.Bifunctor.TH
import           Test.Tasty.Bench

import qualified Control.Monad.Foil            as Foil
import qualified Control.Monad.Foil.Example    as Example
import           Control.Monad.Foil.Telescope  (Telescope (..))
import           Control.Monad.Foil.TH         (mkCoSinkableFoilPattern, mkFoilPattern)
import           Control.Monad.Free.Foil
import           Data.ZipMatchK.TH       (deriveZipMatchK)
import           Generics.Kind.TH        (deriveGenericK)

import           Generated               (FFBPattern (..))

-- | A pattern of two binders, with every instance taken from its generic
-- default, as a client that derives 'GenericK' gets it.
data PairPattern (n :: Foil.S) (l :: Foil.S) where
  PairPattern :: Foil.NameBinder n i -> Foil.NameBinder i l -> PairPattern n l

deriveGenericK ''PairPattern
instance Foil.SinkableK PairPattern
instance Foil.HasNameBinders PairPattern
instance Foil.CoSinkable PairPattern
instance Foil.UnifiablePattern PairPattern

-- | 'PairPattern' with 'Foil.gnameBinderListOf' for 'Foil.nameBinderListOf'.
data GPairPattern (n :: Foil.S) (l :: Foil.S) where
  GPairPattern :: Foil.NameBinder n i -> Foil.NameBinder i l -> GPairPattern n l

deriveGenericK ''GPairPattern
instance Foil.SinkableK GPairPattern
instance Foil.HasNameBinders GPairPattern
instance Foil.CoSinkable GPairPattern where
  nameBinderListOf = Foil.gnameBinderListOf

-- | A pattern of variables and pairs with the instance that
-- 'mkCoSinkableFoilPattern' generates, as in a language generated from a BNFC grammar.
newtype RawIdent = RawIdent String
data RawPattern = RawPatternVar RawIdent | RawPatternPair RawPattern RawPattern

mkFoilPattern ''RawIdent ''RawPattern
mkCoSinkableFoilPattern ''RawIdent ''RawPattern

-- | A pattern of two binders with 'Foil.withPattern' written by hand and no
-- other method.
data HandPair (n :: Foil.S) (l :: Foil.S) where
  HandPair :: Foil.NameBinder n i -> Foil.NameBinder i l -> HandPair n l

instance Foil.CoSinkable HandPair where
  coSinkabilityProof rename (HandPair x y) cont =
    Foil.coSinkabilityProof rename x $ \rename' x' ->
      Foil.coSinkabilityProof rename' y $ \rename'' y' ->
        cont rename'' (HandPair x' y')

  withPattern withBinder _unit comp scope (HandPair x y) cont =
    withBinder scope x $ \fx x' ->
      let scope' = Foil.extendScope x' scope
       in withBinder scope' y $ \fy y' ->
            cont (comp fx fy) (HandPair x' y') (Foil.extendScope y' scope')

-- | A copy of the pattern of "Language.LambdaPi.Impl.Foil" and its instance:
-- wildcards, variables and pairs, with a recursive 'Foil.withPattern' and
-- 'Foil.nameBinderListOf' written by hand.
data LPPattern (n :: Foil.S) (l :: Foil.S) where
  LPWildcard :: LPPattern n n
  LPVar      :: Foil.NameBinder n l -> LPPattern n l
  LPPair     :: LPPattern n i -> LPPattern i l -> LPPattern n l

instance Foil.CoSinkable LPPattern where
  coSinkabilityProof rename pattern cont =
    case pattern of
      LPWildcard -> cont rename LPWildcard
      LPVar x ->
        Foil.coSinkabilityProof rename x $ \rename' x' ->
          cont rename' (LPVar x')
      LPPair l r ->
        Foil.coSinkabilityProof rename l $ \rename' l' ->
          Foil.coSinkabilityProof rename' r $ \rename'' r' ->
            cont rename'' (LPPair l' r')

  withPattern withNameBinder id' combine scope pattern cont =
    case pattern of
      LPWildcard -> cont id' LPWildcard scope
      LPVar x    -> withNameBinder scope x $ \f x' ->
        cont f (LPVar x') (Foil.extendScope x' scope)
      LPPair l r -> Foil.withPattern withNameBinder id' combine scope l $ \fl l' scope' ->
        Foil.withPattern withNameBinder id' combine scope' r $ \fr r' scope'' ->
          cont (combine fl fr) (LPPair l' r') scope''

  nameBinderListOf pattern = go pattern Foil.NameBinderListEmpty
    where
      go :: LPPattern x y -> Foil.NameBinderList y z -> Foil.NameBinderList x z
      go LPWildcard   rest = rest
      go (LPVar x)    rest = Foil.NameBinderListCons x rest
      go (LPPair l r) rest = go l $! go r rest

-- | A telescope of @k@ steps @(x1 : \\y. y) (x2 : x1) ... (xk : x(k-1))@,
-- with the terms of "Control.Monad.Foil.Example" for payloads.
withTelescope
  :: forall r. Int
  -> (forall l. Telescope () Example.Expr Foil.VoidS l -> r)
  -> r
withTelescope k cont =
  Foil.withFresh Foil.emptyScope $ \y ->
    Foil.withFresh Foil.emptyScope $ \x ->
      go (k - 1) (Foil.extendScope x Foil.emptyScope) (Foil.nameOf x) $ \rest ->
        cont (TelescopeCons () (Example.LamE y (Example.VarE (Foil.nameOf y))) x rest)
  where
    go :: Foil.Distinct n
       => Int -> Foil.Scope n -> Foil.Name n
       -> (forall l. Telescope () Example.Expr n l -> r) -> r
    go 0 _ _ cont' = cont' TelescopeEmpty
    go j scope prev cont' = Foil.withFresh scope $ \b ->
      go (j - 1) (Foil.extendScope b scope) (Foil.nameOf b) $ \rest ->
        cont' (TelescopeCons () (Example.VarE (Foil.sink prev)) b rest)

data LamSig scope term
  = App term term
  | Lam scope
  deriving (Functor, Foldable, Traversable)

deriveBifunctor ''LamSig
deriveBifoldable ''LamSig
deriveBitraversable ''LamSig
deriveZipMatchK ''LamSig

-- | A pattern whose 'Foil.CoSinkable' dictionary travels with it.
data SomePattern where
  SomePattern :: Foil.CoSinkable pattern => pattern Foil.VoidS l -> SomePattern

-- | The length of a list of binders, which forces its spine.
size :: Foil.NameBinderList n l -> Int
size = go 0
  where
    go :: Int -> Foil.NameBinderList n l -> Int
    go !acc Foil.NameBinderListEmpty         = acc
    go !acc (Foil.NameBinderListCons _ rest) = go (acc + 1) rest

-- | 'Foil.nameBinderListOf' through a dictionary. Not inlined, so that the
-- dictionary stays unknown at the call.
{-# NOINLINE listedSome #-}
listedSome :: SomePattern -> Int
listedSome (SomePattern pattern) = size (Foil.nameBinderListOf pattern)

-- | 'Foil.addSubstPattern' through a dictionary.
{-# NOINLINE substSome #-}
substSome :: [AST PairPattern LamSig Foil.VoidS] -> SomePattern -> Bool
substSome args (SomePattern pattern) =
  Foil.nullSubst (Foil.addSubstPattern Foil.identitySubst pattern args)

-- | A chain of λ-abstractions, each binding a pair of names, with a variable
-- bound by the outermost one at the bottom.
pairChain :: Int -> AST PairPattern LamSig Foil.VoidS
pairChain depth =
  withPair Foil.emptyScope $ \pattern x scope ->
    Node (Lam (ScopedAST pattern (go scope x (depth - 1))))
  where
    go :: Foil.Distinct n => Foil.Scope n -> Foil.Name n -> Int -> AST PairPattern LamSig n
    go _scope x 0 = Var x
    go scope x d = withPair scope $ \pattern _ scope' ->
      Node (Lam (ScopedAST pattern (go scope' (Foil.sink x) (d - 1))))

    withPair
      :: Foil.Distinct n
      => Foil.Scope n
      -> (forall l. Foil.DExt n l => PairPattern n l -> Foil.Name l -> Foil.Scope l -> r)
      -> r
    withPair scope cont =
      Foil.withFresh scope $ \b1 ->
        let scope1 = Foil.extendScope b1 scope
         in Foil.withFresh scope1 $ \b2 ->
              cont (PairPattern b1 b2) (Foil.sink (Foil.nameOf b1)) (Foil.extendScope b2 scope1)

main :: IO ()
main =
  Foil.withFresh Foil.emptyScope $ \b1 ->
  Foil.withFresh (Foil.extendScope b1 Foil.emptyScope) $ \b2 ->
  Foil.withFreshNameBinderList [(), (), (), ()] Foil.emptyScope Foil.emptyNameMap $ \_ list4 _ ->
  withTelescope 4 $ \tele4 ->
    let pair = PairPattern b1 b2
        var = FoilRawPatternVar b1
        thPair = FoilRawPatternPair (FoilRawPatternVar b1) (FoilRawPatternVar b2)
        ffPair = FFBPatternPair (FFBPatternVar b1) (FFBPatternVar b2)
        handPair = HandPair b1 b2
        lpPair = LPPair (LPVar b1) (LPVar b2)
        gpair = GPairPattern b1 b2
        args = [Node (App t t), t]
        t = pairChain 1
        chain = pairChain 1000
     in defaultMain
          [ bgroup "nameBinderListOf"
              [ bgroup "NameBinder"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) b1
                  , bench "a dictionary" $ whnf listedSome (SomePattern b1)
                  ]
              , bgroup "TH pattern of 1"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) var
                  , bench "a dictionary" $ whnf listedSome (SomePattern var)
                  ]
              , bgroup "NameBinderList of 4"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) list4
                  , bench "a dictionary" $ whnf listedSome (SomePattern list4)
                  ]
              , bgroup "mkCoSinkableFoilPattern pair of 2"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) thPair
                  , bench "a dictionary" $ whnf listedSome (SomePattern thPair)
                  ]
              , bgroup "mkFreeFoil pair of 2"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) ffPair
                  , bench "a dictionary" $ whnf listedSome (SomePattern ffPair)
                  ]
              , bgroup "hand-written pair of 2"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) handPair
                  , bench "a dictionary" $ whnf listedSome (SomePattern handPair)
                  ]
              , bgroup "lambda-pi pattern, pair of 2"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) lpPair
                  , bench "a dictionary" $ whnf listedSome (SomePattern lpPair)
                  ]
              , bgroup "Telescope of 4"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) tele4
                  , bench "a dictionary" $ whnf listedSome (SomePattern tele4)
                  ]
              , bgroup "generic pattern of 2"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) pair
                  , bench "a dictionary" $ whnf listedSome (SomePattern pair)
                  ]
              , bgroup "generic pattern of 2 with gnameBinderListOf"
                  [ bench "known type"   $ whnf (size . Foil.nameBinderListOf) gpair
                  , bench "a dictionary" $ whnf listedSome (SomePattern gpair)
                  ]
              ]
          , bgroup "addSubstPattern, generic pattern of 2"
              [ bench "known type"   $
                  whnf (\p -> Foil.nullSubst (Foil.addSubstPattern Foil.identitySubst p args)) pair
              , bench "a dictionary" $ whnf (substSome args) (SomePattern pair)
              ]
          , bgroup "1000 nested generic patterns of 2"
              [ bench "alphaEquiv" $ whnf (alphaEquiv Foil.emptyScope chain) chain
              , bench "supportOf"  $ whnf (Foil.nameSetSize . supportOf) chain
              ]
          ]