General Question
I have a pair of datatypes that are two different ways of representing the same thing, one records the variable name in String, while the other one records the variable name in Int. Currently, they're both defined. However, I would like to describe just first representation, and the second one would be generated by some relation.
Specific Example
Concretely, the first one is the user defined version of a STLC term universe, while the second one is the de Bruijn–indexed version of the same thing:
{-# LANGUAGE RankNTypes, GADTs, DataKinds, TypeOperators, KindSignatures, PolyKinds, TypeFamilies, UndecidableInstances, FunctionalDependencies, FlexibleInstances #-}
-- * Universe of Terms * --
data Term :: Type -> * where
Var :: Id -> Term a
Lam :: Id -> Type -> Term b -> Term (a :-> b)
App :: Term (a :-> b) -> Term a -> Term b
Let :: Id -> Term a -> Term b -> Term b
Tup :: Term a -> Term b -> Term (a :*: b)
Lft :: Term a -> Term (a :+: b)
Rgt :: Term b -> Term (a :+: b)
Tru :: Term Boolean
Fls :: Term Boolean
Uni :: Term Unit
data TermIn :: Type -> * where
VarI :: Idx -> Info -> TermIn a
LamI :: Type -> TermIn b -> Info -> TermIn (a :-> b)
AppI :: TermIn (a :-> b) -> TermIn a -> TermIn b
LetI :: TermIn a -> TermIn b -> Info -> TermIn b
TupI :: TermIn a -> TermIn b -> TermIn (a :*: b)
TruI :: TermIn Boolean
FlsI :: TermIn Boolean
UniI :: TermIn Unit
-- * Universe of Types * --
data Type
= Type :-> Type
| Type :*: Type
| Type :+: Type
| Boolean
| Unit
| Void
-- * Synonyms * --
type Id = String -- * variable name
type Idx = Int -- * de-brujin's index
data Info = Rec Id String -- * store original name and extra info
There is already a relationship defined over Term
and TermIn
:
class DeBruijnPair a b | a -> b, b -> a where
decode :: b -> a
encode :: a -> b
instance DeBruijnPair (Term a) (TermIn a) where
decode = undefined
encode = undefined
Please note since the original name of Term
is stored in TermIn
, there's a one-to-one mapping of Term
to TermIn
, and back.
Restate the Question
Now you can see how much boiler plate is involved, which I'd like to get rid of using some elegant abstraction where I define Term
and some function over types outputs TermIn
. Just to provide even more context, I'm creating many pairs of such external and de Bruijn representations for different lambda-calculus formulations, and the one-to-one relation holds for all of them.