I want to lift a Haskell function into in a higher-order lambda calculus encoding. This is taken almost verbatim from Oleg's Typed Tagless Final encoding.
class Lam r where
emb :: a -> r a
(^) :: r (r a -> r a) -> (r a -> r a)
lam :: (r a -> r a) -> r (r a -> r a)
instance Lam Identity where
emb = Identity
f ^ x = f >>= ($ x)
lam f = return (f . return =<<) -- call-by-value
eval = runIdentity
I can embed arbitrary Haskell types into Lam
using emb
, but I can't use (^)
for application then. Further, the lifted functions would behave lazily. Instead, I have to lift them application by application.
emb1 :: ( Applicative r, Lam r )
=> (a -> b) -> r (r a -> r b)
emb1 f = lam $ \ra -> f <$> ra
emb2 :: ( Applicative r, Lam r )
=> (a -> b -> c) -> r (r a -> r (r b -> r c))
emb2 f = lam $ \ra -> lam $ \rb -> f <$> ra <*> rb
emb3 :: ( Applicative r, Lam r )
=> (a -> b -> c -> d)
-> r (r a -> r (r b -> r (r c -> r d)))
emb3 f = lam $ \ra -> lam $ \rb -> lam $ \rc -> f <$> ra <*> rb <*> rc
>>> eval $ emb2 (+) ^ emb 1 ^ emb 2
3
That's a lot of boilerplate, though. I'd like to create a generic lifting function that would work for any arity function. I feel like it'd be possible using something akin to Printf
's PrintfType
or fixed-vector
's Cont
types. I can specify what I want using type functions
type family Low h o
type instance Low () o = o
type instance Low (a, h) o = a -> Low h o
type family Lift r h o
type instance Lift r () o = o
type instance Lift r (a, h) o = r a -> r (Lift r h o)
class Emb r h o where
embed :: Low h o -> r (Lift r h o)
instance ( Lam r ) => Emb r () o where
embed = emb
instance ( Lam r, Applicative r, Emb r h o ) => Emb r (a, h) o where
embed = ?
But I get very stuck via this method, usually due to injectivity issues. I was able to resolve injectivity with a truly hideous combination of newtype wrappers and scoped type variables, but it never actually type checked.
Is this possible to express in Haskell?