|
| 1 | +{- |
| 2 | +
|
| 3 | +Embedding of a simple first-order language with the fix-point operator. |
| 4 | +
|
| 5 | +-} |
| 6 | +{-# OPTIONS_GHC -Wno-missing-export-lists #-} |
| 7 | +{-# LANGUAGE DataKinds #-} |
| 8 | +{-# LANGUAGE DerivingStrategies #-} |
| 9 | +{-# LANGUAGE FlexibleContexts #-} |
| 10 | +{-# LANGUAGE FlexibleInstances #-} |
| 11 | +{-# LANGUAGE FunctionalDependencies #-} |
| 12 | +{-# LANGUAGE GADTs #-} |
| 13 | +{-# LANGUAGE PolyKinds #-} |
| 14 | +{-# LANGUAGE RankNTypes #-} |
| 15 | +{-# LANGUAGE ScopedTypeVariables #-} |
| 16 | +{-# LANGUAGE StandaloneDeriving #-} |
| 17 | +{-# LANGUAGE TypeFamilies #-} |
| 18 | +{-# LANGUAGE TypeOperators #-} |
| 19 | + |
| 20 | +module Unembedding.Examples.FirstOrder where |
| 21 | +import Data.Proxy (Proxy (..)) |
| 22 | +import qualified Unembedding as UE |
| 23 | +import qualified Unembedding.Env as UE |
| 24 | +import Unembedding.Env (Env (..)) |
| 25 | + |
| 26 | +data ValType = VInt | VBool |
| 27 | +data FunType = I ValType | ValType :~> FunType |
| 28 | + |
| 29 | +infixr 0 :~> |
| 30 | + |
| 31 | +-- First-order de Bruijn representation for a first-order language with fix. |
| 32 | +data FOLang fenv venv a where |
| 33 | + FLam :: FOLang fenv (a : venv) b -> FOLang fenv venv (a :~> b) |
| 34 | + FApp :: FOLang fenv venv (a :~> b) -> FOLang fenv venv (I a) -> FOLang fenv venv b |
| 35 | + FVar :: UE.Ix venv a -> FOLang fenv venv (I a) |
| 36 | + FFun :: UE.Ix fenv f -> FOLang fenv venv f |
| 37 | + FFix :: FOLang (f : fenv) venv f -> FOLang fenv venv f |
| 38 | + |
| 39 | +deriving stock instance Show (FOLang fenv venv a) |
| 40 | + |
| 41 | +weakenVar :: Proxy b -> Proxy venv -> UE.Env Proxy as -> UE.Ix (UE.Append as venv) a -> UE.Ix (UE.Append as (b : venv)) a |
| 42 | +weakenVar _ _ (_ :. _) UE.IxZ = UE.IxZ |
| 43 | +weakenVar pb pvenv (_ :. as) (UE.IxS x) = UE.IxS (weakenVar pb pvenv as x) |
| 44 | +weakenVar _ _ ENil ix = UE.IxS ix |
| 45 | + |
| 46 | +weaken2 :: Proxy b -> Proxy venv -> UE.Env Proxy as -> FOLang fenv (UE.Append as venv) a -> FOLang fenv (UE.Append as (b : venv)) a |
| 47 | +weaken2 pb pvenv as (FLam e) = FLam $ weaken2 pb pvenv (Proxy :. as) e |
| 48 | +weaken2 pb pvenv as (FApp e1 e2) = FApp (weaken2 pb pvenv as e1) (weaken2 pb pvenv as e2) |
| 49 | +weaken2 pb pvenv as (FVar ix) = FVar (weakenVar pb pvenv as ix) |
| 50 | +weaken2 _ _ _ (FFun ix) = FFun ix |
| 51 | +weaken2 pb pvenv as (FFix e) = FFix $ weaken2 pb pvenv as e |
| 52 | + |
| 53 | + |
| 54 | + |
| 55 | +-- Its HOAS representation (in tagless-final style) |
| 56 | +-- |
| 57 | +-- Observe that it involves two semantic domains. Also, `lam` and `fix` bind |
| 58 | +-- variables from different domains. |
| 59 | +class HFOLang i e | e -> i where |
| 60 | + lam :: (i a -> e b) -> e (a :~> b) |
| 61 | + var :: i a -> e (I a) |
| 62 | + app :: e (a :~> b) -> e (I a) -> e b |
| 63 | + fix :: (e a -> e a) -> e a |
| 64 | + |
| 65 | +-- Intermediate type class |
| 66 | +class HFOLang' e where |
| 67 | + lam' :: e (a : venv) b -> e venv (a :~> b) |
| 68 | + app' :: e venv (a :~> b) -> e venv (I a) -> e venv b |
| 69 | + var' :: UE.Ix venv a -> e venv (I a) |
| 70 | + fix' :: (e venv a -> e venv a) -> e venv a |
| 71 | + |
| 72 | +-- newtype wrapper for the above e |
| 73 | +newtype E e (env :: [ValType]) (a :: FunType) = E { unE :: e env a } |
| 74 | + |
| 75 | +instance UE.Weakenable e => UE.Weakenable (E e) where |
| 76 | + weaken (E x) = E (UE.weaken x) |
| 77 | + |
| 78 | +instance (HFOLang' e, UE.Weakenable e) => HFOLang (UE.EnvI UE.Ix) (UE.EnvI (E e)) where |
| 79 | + lam = UE.liftSOn' (UE.ol1 :. ENil) Proxy $ \(E e) -> E (lam' e) |
| 80 | + app = UE.liftFO2 $ \(E e1) (E e2) -> E (app' e1 e2) |
| 81 | + fix = UE.liftSOnGen (UE.DimNested (UE.K UE.E) :. ENil) (Proxy :: Proxy UE.Ix) $ \f -> E $ fix' (unE . f . E) |
| 82 | + var = UE.liftFO1' $ E . var' |
| 83 | + |
| 84 | +-- type family Fst (a :: (k , k')) :: k where |
| 85 | +-- Fst '(a , b) = a |
| 86 | + |
| 87 | +-- type family Snd (a :: (k , k')) :: k' where |
| 88 | +-- Snd '(a , b) = b |
| 89 | + |
| 90 | +-- newtype WrapL fenv venva = WrapL (FOLang fenv (Fst venva) (Snd venva)) |
| 91 | + |
| 92 | + |
| 93 | +-- newtype SwFOLang venv fenv a = SwFOLang (FOLang fenv venv a) |
| 94 | +newtype Sw f venv fenv a = Sw (f fenv venv a) |
| 95 | + |
| 96 | +newtype Wrap e venv a = Wrap { unWrap :: UE.EnvI (Sw e venv) a } |
| 97 | + |
| 98 | +instance UE.LiftVariables (Sw FOLang venv) where |
| 99 | + type Var (Sw FOLang venv) = UE.Ix |
| 100 | + liftVar ix = Sw $ FFun ix |
| 101 | + |
| 102 | +instance HFOLang' (Wrap FOLang) where |
| 103 | + lam' (Wrap ex) = Wrap $ UE.liftFO1' (\(Sw e) -> Sw $ FLam e) ex |
| 104 | + app' (Wrap ex1) (Wrap ex2) = Wrap $ UE.liftFO2 (\(Sw e1) (Sw e2) -> Sw (FApp e1 e2)) ex1 ex2 |
| 105 | + var' ix = Wrap $ UE.liftFO0 $ Sw $ FVar ix |
| 106 | + fix' f = Wrap $ UE.liftSOn (UE.ol1 :. ENil) fixSem (unWrap . f . Wrap) |
| 107 | + where |
| 108 | + fixSem (Sw e) = Sw $ FFix e |
| 109 | + |
| 110 | +instance UE.Weakenable (Wrap FOLang) where |
| 111 | + weaken (Wrap w) = Wrap $ UE.liftFO1' (\(Sw e) -> Sw (weaken2 Proxy Proxy ENil e)) w |
| 112 | + |
| 113 | +convert :: forall a. (forall i e. HFOLang i e => e a) -> FOLang '[] '[] a |
| 114 | +convert h = |
| 115 | + let h' :: forall e. (UE.Weakenable e, HFOLang' e) => e '[] a |
| 116 | + h' = unE $ UE.runClose h |
| 117 | + Sw r = UE.runClose (unWrap h') -- TODO: Is it really safe? |
| 118 | + in r |
| 119 | + |
| 120 | +-- >>> convert $ fix (\_ -> lam $ \x -> lam $ \y -> var x) |
| 121 | +-- FFix (FLam (FLam (FVar (IxS IxZ)))) |
| 122 | + |
| 123 | +-- >>> convert $ lam $ \x -> lam $ \y -> fix (\_ -> lam $ \x -> lam $ \y -> var x) `app` var x `app` var y |
| 124 | +-- FLam (FLam (FApp (FApp (FFix (FLam (FLam (FVar (IxS IxZ))))) (FVar (IxS IxZ))) (FVar IxZ))) |
| 125 | + |
| 126 | +-- >>> convert $ fix (\f -> lam $ \x -> app f (var x)) |
| 127 | +-- FFix (FLam (FApp (FFun IxZ) (FVar IxZ))) |
0 commit comments