{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE UnicodeSyntax #-} module Abt.Tutorial where import Abt.Class import Abt.Types import Abt.Concrete.LocallyNameless import Control.Applicative import Control.Monad.Trans import Control.Monad.Trans.State.Strict -- | We'll start off with a monad in which to manipulate ABTs; we'll need some -- state for fresh variable generation. -- newtype M α = M { _M ∷ State Int α } deriving (Functor, Applicative, Monad) -- | We'll run an ABT computation by starting the variable counter at @0@. -- runM ∷ M α → α runM (M m) = evalState m 0 -- | Check out the source to see fresh variable generation. -- instance MonadVar Var M where fresh = M $ do n ← get let n' = n + 1 put n' return $ Var Nothing n' named a = do v ← fresh return $ v { _varName = Just a } -- | Next, we'll define the operators for a tiny lambda calculus as a datatype -- indexed by arities. -- data Lang ns where Lam ∷ Lang '[S Z] Ap ∷ Lang '[Z,Z] instance Show1 Lang where show1 = \case Lam → "lam" Ap → "ap" instance HEq1 Lang where Lam === Lam = True Ap === Ap = True _ === _ = False -- | Check out the source to see this example term: note that number of -- arguments and presence of abstractions is guaranteed by the types. The -- representation is not scope-safe (i.e. free variables are permitted), but -- that's how we want it. -- example ∷ M [Var] example = do x ← fresh y ← fresh let tm = Lam $$ x \\ (Ap $$ var x :* var y :* Nil) :* Nil freeVars tm