rebound-0.1.0.0: examples/HOAS.hs
module HOAS where
{-
This module demonstrates how to layer a HOAS-based representation
on top of a de Bruijn representation, to make it easier to generate well scoped
lambda terms.
It is based on Conor McBride's "Classy Hack"
https://mazzo.li/epilogue/index.html%3Fp=773.html
-}
import LC qualified
import Data.Fin
import Rebound.Bind.Single
import Rebound
-- Here are some HOAS lambda calculus terms
tru :: Tm Z
tru = Lam $ \x -> Lam $ \y -> Var x
fls :: Tm Z
fls = Lam $ \x -> Lam $ \y -> Var y
app :: Tm Z
app = Lam $ \f -> Lam $ \x -> App (Var f) (Var x)
omega :: Tm Z
omega = App delta delta where
delta = Lam $ \x -> App (Var x) (Var x)
-- We can convert them to a de Bruijn-indexed
-- representation easily
-- >>> cvt tru
-- (λ. (λ. 1))
-- >>> cvt fls
-- (λ. (λ. 0))
-- >>> cvt app
-- (λ. (λ. (1 0)))
-- >>> cvt omega
-- ((λ. (0 0)) (λ. (0 0)))
-- These terms are elements of the following datatype
-- that uses a form of "weak higher-order abstract syntax"
-- for variable binding. A type class constraint in the variable
-- constructor constructs the appropriate de Bruijn index.
data Tm (a :: Nat) where
Var :: (b ⊆ a) => Proxy b -> Tm a
App :: Tm a -> Tm a -> Tm a
Lam :: (Proxy (S a) -> Tm (S a)) -> Tm a
instance Cvt Tm LC.Exp where
cvt :: Tm m -> LC.Exp m
cvt (Var x) = LC.Var (cvtVar x)
cvt (App a b) = LC.App (cvt a) (cvt b)
cvt (Lam f) = LC.Lam (cvtBind f)
------------------------------------------------------------
-- The rest of this file is independent of the language that we are using
-- and can be called reusable "library" code
-- It depends on overlapping instances
-- Conversion type class
class Cvt t u | t -> u where
cvt :: t m -> u m
class (b :: Nat) ⊆ (a :: Nat) where
inj :: Fin b -> Fin a
instance {-# OVERLAPPING #-} n ⊆ n where inj = id
instance {-# OVERLAPPING #-} (o ~ S n, m ⊆ n) => m ⊆ o where inj = FS . inj
-- Note: you don't actually need overlapping instances for this example:
--- instance n ⊆ n where inj = id
--- instance n ⊆ S n where inj = FS
-- would work just as well
newtype Proxy b = P (Fin b)
zeroVar :: Proxy (S b)
zeroVar = P FZ
cvtVar :: (b ⊆ a) => Proxy b -> Fin a
cvtVar (P x) = inj x
cvtBind :: (Subst v u, Cvt t u) => (Proxy (S a) -> t (S a)) -> Bind v u a
cvtBind f = bind (cvt (f zeroVar))