Created
February 6, 2015 04:53
-
-
Save jozefg/5bf7a4fce126a032d93b to your computer and use it in GitHub Desktop.
A tiny tottering implementation of abstract binding trees
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE DeriveFunctor, GeneralizedNewtypeDeriving #-} | |
{-# LANGUAGE FlexibleContexts, LambdaCase #-} | |
-- | A humane representation of binding. | |
module ABT ( FreeVar (..) | |
, Operator (..) | |
, ABT | |
, View (..) | |
, view | |
, fold | |
, subst ) where | |
import Control.Monad.Gen | |
import Control.Applicative | |
data Operator = Operator { operArity :: Int | |
, operName :: String } | |
deriving(Eq) | |
newtype FreeVar = Free Int | |
deriving(Eq, Ord, Enum) | |
-- | Internal representation of an ABT. This is locally nameless and | |
-- somewhat painful to work with. Happily with this library we write | |
-- this code once and rely on a nicer interface in the future. This | |
-- does easily admit alpha-equivalence though. | |
data ABT = FreeVar FreeVar | |
| BoundVar Int | |
| BindIn ABT | |
| App Operator [ABT] | |
deriving (Eq) | |
-- | The humane view of an ABT. | |
data View t = Var FreeVar -- ^ A variable | |
| Binding FreeVar t -- ^ A binding. Binds a FreeVar in t | |
| Oper Operator [t] -- ^ Applying some operator to some ts. | |
deriving Functor | |
-- | Given a freevar, replace the outmost DB variable with it. | |
unbind :: FreeVar -> ABT -> ABT | |
unbind var = go 0 | |
where go i = \case | |
BoundVar j -> if i == j then FreeVar var else BoundVar j | |
FreeVar v -> FreeVar v | |
BindIn a -> BindIn (go (i + 1) a) | |
App oper abts -> App oper (map (go i) abts) | |
-- | Add a new binder which binds a particular free variable. This | |
-- replaces all occurences of this var with a DB var pointing to this | |
-- outermost binder. | |
bind :: FreeVar -> ABT -> ABT | |
bind var = go 0 | |
where go i = \case | |
FreeVar var' -> if var == var' then BoundVar i else FreeVar var' | |
BoundVar j -> BoundVar j | |
BindIn a -> BindIn (go (i + 1) a) | |
App oper abts -> App oper (map (go i) abts) | |
-- | Given an ABT unfold it partially and give a view of the | |
-- structure. This can potentially create new fresh variables so we | |
-- need to have effects here. | |
view :: (Functor m, MonadGen FreeVar m) => ABT -> m (View ABT) | |
view = \case | |
FreeVar v -> return $ Var v | |
BoundVar _ -> error "Impossible" | |
App oper abts -> return $ Oper oper abts | |
BindIn a -> (Binding <*> flip unbind a) <$> gen | |
-- | Given a view, we can smush it back into the efficient, locally | |
-- nameless representation. | |
fold :: View ABT -> ABT | |
fold = \case | |
Var v -> FreeVar v | |
Binding v a -> bind v a | |
Oper oper abts -> App oper abts | |
-- | A priveleged subst. This way avoids unneeded calls to gen. | |
subst :: ABT -> FreeVar -> ABT -> ABT | |
subst new var = go | |
where go = \case | |
FreeVar var' -> if var == var' then new else FreeVar var' | |
BoundVar i -> BoundVar i | |
BindIn a -> BindIn (go a) | |
App oper abts -> App oper (map go abts) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment