## HOAS Based Interpreter
The preceding deBruijn index based interpreter is impressive, and a great example of the expressive power of the typed tagless-final approach. However, using deBruijn notation can be quite cumbersome, so I will now show how to make an interpreter for a HOAS based version of LLC, where there are named variables which are represented by actual Haskell variables.
```haskell
-- show Standard typed HOAS lambda calculus signature
lam :: (repr a -> repr b) -> repr (a -> b)
app :: repr (a -> b) -> repr a -> repr b
```
A complete example of such an interpreter can be found [here](http://okmij.org/ftp/tagless-final/course/TTF.hs).
The basic idea for interpreting a HOAS LLC is to semantically separate the linear context into two components: one for storing variables and their types, which acts the same as, and can be represented by, the Haskell context (linearity does not affect substitution); and another for enforcing linearity, which must be explicitly represented and manipulated since the Haskell context does not enforce linearity. My abstract linear contexts will be type level lists of `Maybe Nat`; the actual type of each variable is unnecssary for checking linearity. Thus, I will label each variable as it is introduced and add that label to the abstract linear context to enforce linearity. To accomplish this, I will have the representation type carry around a `Nat` to use for the next linear variable:
```haskell
repr (vid::Nat) (hi::[Maybe Nat]) (ho::[Maybe Nat]) (a::*)
```
where `vid` represents the number of linear variables in scope (and also gives the next linear variable "name" to use), `hi` and `ho` again represent abstractions of the input and output linear contexts; the length of `hi` and `ho` should equal `vid`.
Since I am pushing all of the work of checking linearity into the type level, I will try to use type families and promoted data kinds for this code. I will also introduce a linear function type, `:-<>:`, in anticipation of adding regular lambdas in the next section. Here is the heart of the HOAS interpreter:
```haskell
newtype a :-<>: b = Lolli {unLolli :: a -> b}
class Lin (repr :: Nat -> [Maybe Nat] -> [Maybe Nat] -> * -> *) where
llam :: (LinVar repr vid a -> repr (S vid) (Just vid ': hi) (Nothing ': ho) b) ->
repr vid hi ho (a :-<>: b)
(<^>) :: repr vid hi h (a :-<>: b) -> repr vid h ho a -> repr vid hi ho b
```
The semantics (i.e. type) of linear application, `<^>`, are a direct transcription of the `-<>E` typing rule; and identical to the previous first-order version. The semantics of the linear lambda directly mirror the `-<>I` rule except that they add a representation of the variable, with its type, (i.e. `LinVar repr vid a` which I will explain below) to the Haskell context, and they specify, via the `Nothing` in the output context, that this variable, labelled `vid`, must be used in the body of the lambda.
The Haskell type checker needs to enforce that each variable of type `LinVar repr vid a` is only used once; which translates into checking that `Just vid` is in the input linear context and placing `Nothing`, in its place, in the output linear context. To do this, I rely on the following type families:
```haskell
-- show Type level Nat equality
type family EQ (x::Nat) (y::Nat) :: Bool
type instance EQ Z Z = True
type instance EQ Z (S y) = False
type instance EQ (S x) Z = False
type instance EQ (S x) (S y) = EQ x y
-- show Type level machinery for consuming a variable in a list of variables.
type family Consume (vid::Nat) (i::[Maybe Nat]) :: [Maybe Nat]
type instance Consume vid (Nothing ': vs) = (Nothing ': Consume vid vs)
type instance Consume vid (Just v ': vs) = Consume1 (EQ vid v) vid v vs
type family Consume1 (b::Bool) (vid::Nat) (v::Nat) (vs::[Maybe Nat]) :: [Maybe Nat]
type instance Consume1 True vid v vs = Nothing ': vs
type instance Consume1 False vid v vs = Just v ': Consume vid vs
```
With the above, I can write `LinVar` as follows:
```haskell
type LinVar (repr :: Nat -> [Maybe Nat] -> [Maybe Nat] -> * -> *) vid a =
forall v h . repr v h (Consume vid h) a
```
Note the explicit use of `forall` which is necessary to allow this type to work no matter what context variable `vid` occurs in.
I can now put everything together and try to type check some terms:
```active haskell
-- /show
{-# LANGUAGE
DataKinds,
FlexibleInstances,
KindSignatures,
MultiParamTypeClasses,
NoMonomorphismRestriction,
RankNTypes,
TypeFamilies,
TypeOperators,
UndecidableInstances
#-}
--
-- Type level Nats
--
data Nat = Z | S Nat
--
-- Type level Nat equality
--
type family EQ (x::Nat) (y::Nat) :: Bool
type instance EQ Z Z = True
type instance EQ Z (S y) = False
type instance EQ (S x) Z = False
type instance EQ (S x) (S y) = EQ x y
--
-- Type level machinery for consuming a variable in a list of variables.
--
type family Consume (vid::Nat) (i::[Maybe Nat]) :: [Maybe Nat]
type family Consume1 (b::Bool) (vid::Nat) (v::Nat) (vs::[Maybe Nat]) :: [Maybe Nat]
type instance Consume vid (Nothing ': vs) = (Nothing ': Consume vid vs)
type instance Consume vid (Just v ': vs) = Consume1 (EQ vid v) vid v vs
type instance Consume1 True vid v vs = Nothing ': vs
type instance Consume1 False vid v vs = Just v ': Consume vid vs
-- show
type LinVar (repr :: Nat -> [Maybe Nat] -> [Maybe Nat] -> * -> *) vid a =
forall v i . repr v i (Consume vid i) a
-- to distinguish linear arrow from regular arrow
newtype a :-<>: b = Lolli {unLolli :: a -> b}
class Lin (repr :: Nat -> [Maybe Nat] -> [Maybe Nat] -> * -> *) where
llam :: (LinVar repr vid a -> repr (S vid) (Just vid ': hi) (Nothing ': ho) b) ->
repr vid hi ho (a :-<>: b)
(<^>) :: repr vid hi h (a :-<>: b) -> repr vid h ho a -> repr vid hi ho b
good = llam $ \f -> llam $ \x -> f <^> x
bad = llam $ \f -> llam $ \x -> f <^> x <^> x
main = putStrLn "ok"
```
Unfortunately, both `good` and `bad` above type. The generated type for `good` (something like):
```haskell
llam $ \f -> llam $ \x -> f <^> x
:: (Lin repr,
Consume
('S vid)
(Consume1
{-hi-}(EQ vid ('S vid)){-/hi-}
vid
('S vid)
((':) (Maybe Nat) ('Just Nat vid) hi))
~ (':)
(Maybe Nat) ('Nothing Nat) ((':) (Maybe Nat) ('Nothing Nat) ho)) =>
repr vid hi ho ((a :-<>: b) :-<>: (a :-<>: b))
```
shows that the type checker didn't really do anything; it just generated a big constraint involving the `Consume` type family. In order to make the type checker do some work, I'll have to force a specific `vid` (e.g. `Z`) and possibly also `hi` and `ho`; or I'll have to teach the type checker how to deal with the more general types. I will opt for the latter since I'll want to be able to use my definitions in any context.
Looking closely at the above constraint shows that the problem lies in `EQ`-- `EQ vid (S vid)` did not rewrite to `False`. Of course the specification of `EQ` doesn't explain what to do with open variables, but it seems reasonable that `EQ vid (S vid)` should rewrite to `False`. In order to get this behavior, I really need `EQ` to check whether its arguments can be unified; since there will only be one uninstantiated `Nat` type variable unification will really be sufficient to check `Nat` equality. Fortunately, there is a way to determine whether two types, containing at most 1 type variable, unify, though I can't do it with type families:
```haskell
-- show An EQ which tests unifiability
class EQ (x::Nat) (y::Nat) (b::Bool) | x y -> b
instance EQ x x True
instance (b ~ False) => EQ x y b
```
By making the second instance strictly more general than the first, GHC will try the first instance and, if the two arguments unify, succeed with `True` or fail and succeed on the second instance with `False` (this of course requires overlapping instances and a few other extensions; this should also be possible with type families when overlapping family instances are allowed).
Now I have to turn my `Consume` function into a class:
```haskell
class Consume (v::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]) | v i -> o
class Consume1 (b::Bool) (v::Nat) (x::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]) | b v x i -> o
instance (Consume v i o) => Consume v (Nothing ': i) (Nothing ': o)
instance (EQ v x b, Consume1 b v x i o) => Consume v (Just x ': i) o
instance Consume1 True v x i (Nothing ': i)
instance (Consume v i o) => Consume1 False v x i (Just x ': o)
```
I can now create a type synonym for my definitions and, in order to show that GHC can actually infer my linear types, a convenience function to set the type of an LLC term:
```active haskell
-- /show
{-# LANGUAGE
DataKinds,
FlexibleContexts,
FlexibleInstances,
FunctionalDependencies,
KindSignatures,
MultiParamTypeClasses,
NoMonomorphismRestriction,
OverlappingInstances,
RankNTypes,
TypeFamilies,
TypeOperators,
UndecidableInstances
#-}
--
-- Type level Nats
--
data Nat = Z | S Nat
--
-- Type level Nat equality
--
class EQ (x::Nat) (y::Nat) (b::Bool) | x y -> b
instance EQ x x True
instance (b ~ False) => EQ x y b
--
-- Type level machinery for consuming a variable in a list of variables.
--
class Consume (v::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]) | v i -> o
class Consume1 (b::Bool) (v::Nat) (x::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]) | b v x i -> o
instance (Consume v i o) => Consume v (Nothing ': i) (Nothing ': o)
instance (EQ v x b, Consume1 b v x i o) => Consume v (Just x ': i) o
instance Consume1 True v x i (Nothing ': i)
instance (Consume v i o) => Consume1 False v x i (Just x ': o)
type LinVar repr (vid::Nat) a = forall (v::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]). (Consume vid i o) => repr v i o a
-- to distinguish linear arrow from regular arrow
newtype a :-<>: b = Lolli {unLolli :: a -> b}
class Lin (repr :: Nat -> [Maybe Nat] -> [Maybe Nat] -> * -> *) where
llam :: (LinVar repr vid a -> repr (S vid) (Just vid ': hi) (Nothing ': ho) b) ->
repr vid hi ho (a :-<>: b)
(<^>) :: repr vid hi h (a :-<>: b) -> repr vid h ho a -> repr vid hi ho b
-- show
type Defn a = forall repr v h . (Lin repr) => repr v h h a
defn :: Defn a -> Defn a
defn x = x
good :: Defn ((a :-<>: b) :-<>: (a :-<>: b))
good = llam $ \f -> llam $ \x -> f <^> x
-- GHC can actually infer the linear types.
-- Main*> :t defn $ llam $ \f -> llam $ \x -> f <^> x
-- defn $ llam $ \f -> llam $ \x -> f <^> x
-- :: (Lin repr) => repr vid h h ((a :-<>: b) :-<>: (a :-<>: b))
--
good' = defn $ llam $ \f -> llam $ \x -> f <^> x
--bad = defn $ llam $ \f -> llam $ \x -> f <^> x <^> x
main = putStrLn "ok"
```
The above code correctly fails to typecheck `bad`. Also, as in the deBruijn index based interpreter, all of the linearity checking is done by the type checker, i.e. I have not yet given any instance for `Lin`. Since I've used HOAS, a type to realize evaluation of LLC terms and an eval function are trivial.
```active haskell
-- /show
{-# LANGUAGE
DataKinds,
FlexibleContexts,
FlexibleInstances,
FunctionalDependencies,
KindSignatures,
MultiParamTypeClasses,
NoMonomorphismRestriction,
OverlappingInstances,
RankNTypes,
TypeFamilies,
TypeOperators,
UndecidableInstances
#-}
--
-- Type level Nats
--
data Nat = Z | S Nat
--
-- Type level Nat equality
--
class EQ (x::Nat) (y::Nat) (b::Bool) | x y -> b
instance EQ x x True
instance (b ~ False) => EQ x y b
--
-- Type level machinery for consuming a variable in a list of variables.
--
class Consume (v::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]) | v i -> o
class Consume1 (b::Bool) (v::Nat) (x::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]) | b v x i -> o
instance (Consume v i o) => Consume v (Nothing ': i) (Nothing ': o)
instance (EQ v x b, Consume1 b v x i o) => Consume v (Just x ': i) o
instance Consume1 True v x i (Nothing ': i)
instance (Consume v i o) => Consume1 False v x i (Just x ': o)
type LinVar repr (vid::Nat) a = forall (v::Nat) (i::[Maybe Nat]) (o::[Maybe Nat]). (Consume vid i o) => repr v i o a
-- to distinguish linear arrow from regular arrow
newtype a :-<>: b = Lolli {unLolli :: a -> b}
class Lin (repr :: Nat -> [Maybe Nat] -> [Maybe Nat] -> * -> *) where
llam :: (LinVar repr vid a -> repr (S vid) (Just vid ': hi) (Nothing ': ho) b) ->
repr vid hi ho (a :-<>: b)
(<^>) :: repr vid hi h (a :-<>: b) -> repr vid h ho a -> repr vid hi ho b
type Defn a = forall repr v h . (Lin repr) => repr v h h a
defn :: Defn a -> Defn a
defn x = x
-- show
newtype R (vid::Nat) (hi::[Maybe Nat]) (ho::[Maybe Nat]) a = R {unR :: a}
instance Lin R where
llam f = R $ Lolli $ \x -> unR $ f $ R x
f <^> x = R $ unLolli (unR f) (unR x)
eval :: R Z '[] '[] a -> a
eval = unR
good = defn $ llam $ \f -> llam $ \x -> f <^> x
main = do
putStrLn $ unLolli (eval $ good <^> llam (\x -> x)) "I was passed to a real function."
putStrLn "ok"
```
One interesting thing to note is that the `llam` rule needs to be eta-expanded (as written above) and cannot be written as
```haskell
llam f = R $ Lolli $ unR . f . R
```