Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AST with unbound + recussion schemes for transformations #43

Open
csabahruska opened this issue Aug 5, 2017 · 5 comments
Open

AST with unbound + recussion schemes for transformations #43

csabahruska opened this issue Aug 5, 2017 · 5 comments

Comments

@csabahruska
Copy link

Does unbound semantics allow to create the shape functor for the AST?
It would help a lot to implement the AST transformations using recursion schemes.
I.e. in this short closure conversion tutorial
It would be useful example to have the same thing but with unbound.

@sweirich
Copy link
Owner

RepLib/Unbound does not have the facility to do this now. The only way to define new datatypes based on existing ones (i.e. the shape functor) is using TemplateHaskell. RepLib already uses TH to construct representation types; it would be possible to extend this to also construct shape functors too.

However, you might be able to replace your uses of cata with datatype-generic functions. For example, Unbound defines a generic version of the free variable function.

@csabahruska
Copy link
Author

csabahruska commented Aug 15, 2017

Is it possible to create the shape functor manually for a recursive data type that uses unbound? Or would it break unbound semantics?
I.e. to write

data Term a = Var (Name a)
          | App a a
          | Lam (Bind (Name a) a)

instead of

data Term = Var (Name Term)
          | App Term Term
          | Lam (Bind (Name Term) Term)

@sweirich
Copy link
Owner

So, this doesn't work directly. It's fine to define the shape functor

type Var = Name Term
data TermF a = VarF Var
          | AppF a a
          | LamF (Bind [Var] a)

But the problem is that TermF is not actually a functor. In the LamFcase, we need to decompose the binding, but to do that we need a and b to be in the Alpha class. Furthermore, to make sure that we have a suitably fresh variable, this should also be in the context of a freshness monad.

instance Functor TermF where
    fmap :: (a -> b) -> TermF a -> TermF b
    fmap f (VarF n)     = VarF n
    fmap f (AppF a1 a2) = AppF (f a1) (f a2)
    fmap f (LamF bnd)   = undefined
      -- can't do anything here

Instead, we need to work we new recursion schemes that include the monadic traversal of the term.

So imagine that we had these definitions

fmapM :: (Alpha a, Alpha b, LFresh m) => (a -> m b) -> TermF a -> m (TermF b)
fmapM f (VarF n)     = return $ VarF n
fmapM f (AppF a1 a2) = AppF <$> (f a1) <*> (f a2)
fmapM f (LamF bnd)   = lunbind bnd $ \ (x,a1) -> f a1 >>= \ b1 -> return $ LamF (bind x b1)

cataM :: forall a m.  (Alpha a, LFresh m) => (TermF a -> m a) -> Term -> m a
cataM f x = fmapM (cataM f) (project x) >>= f  where

Then your closure conversion function can be defined using cataM

closConv :: (LFresh m) => [Var] -> Term -> m Term
closConv globals t = avoid (map AnyName globals) $ cataM folder t
  where folder (LamF bnd) =
            let vars = fv (LamF bnd) `without` globals in
            lunbind bnd $ \(vs,a) -> return $ (Lam (bind (vars ++ vs) a)) `applyTo` vars
        folder e = return $ embed e

@csabahruska
Copy link
Author

fmapM :: (Alpha a, Alpha b, LFresh m) => (a -> m b) -> TermF a -> m (TermF b)

is pretty similar to mapM

class (Functor t, Foldable t) => Traversable t where
    mapM :: Monad m => (a -> m b) -> t a -> m (t b)

Is TermF a Traversable (but not a Functor)?
If so why should Traversable t must be Functor t also if TermF works just fine?

@sweirich
Copy link
Owner

sweirich commented Aug 17, 2017 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants