packages feed

binder-0.2: src/Data/Binder.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

{-|
Module      : Data.Binder
Description : Variable binding for abstract syntax tree
Copyright   : (c) 2023 Keito Kajitani
License     : MIT
Maintainer  : Keito Kajitani <ijaketak@gmail.com>

binder is purely functional implementation of Ocaml's bindlib.
It follows the style of higher-order abstract syntax,
and offers the representation of abstract syntax tree.
-}
module Data.Binder
-- * Preliminaries
  ( MonadNumbering(..)
-- * Variable and Box
  , Var
  , Box
-- ** Variable
  , var'Key
  , var'Name
  , var'mkFree
  , var'Box
  , nameOf
  , boxVar
  , newVar
  , isClosed
  , occur
-- ** Box
  , unbox
  , box
  , apBox
  , boxApply
  , boxApply2
  , boxApply3
  , boxApply4
  , boxPair
  , boxTriple
  , boxT
  , boxList
  , boxJoin
-- * Variable binding
  , Binder
  , binder'Name
  , binder'mkFree
  , binder'Body
  , subst
  , buildBinder
  , bind
  , unbind
  , eqBinder
  , boxBinder
  , bindApply
-- * List
-- * Variable list
  , VarList
  , varList'Keys
  , varList'Names
  , varList'Boxes
  , namesOf
  , boxVarList
  , newVarList
-- * Binder for list
  , BinderList
  , binderList'Names
  , binderList'Body
  , binderList'mkFree
  , binderList'Arity
  , substList
  , eqBinderList
  , bindList
  , unbindList
  , boxBinderList
  , bindListApply
  ) where

import Control.Lens
import Control.Monad (join)
import Data.Kind (Type)
import qualified Data.Map.Lazy as M
import Data.Maybe (fromJust)
import Data.Text (Text)
import Unsafe.Coerce

-- | Numbering monad.
class (Monad m, Ord (Numbering m)) => MonadNumbering m where
  type Numbering m :: Type
  numbering :: m (Numbering m)

-- | Representation of variable
--   for abstract syntax tree type @a@
--   with base numbering monad @m@.
data Var m a = Var
  { _var'Key :: !(Numbering m)
  , _var'Body :: VarBody m a
  }
data VarBody m a = VarBody
  { _varBody'Name :: Text
  , _varBody'mkFree :: Var m a -> m a
  , _varBody'Box :: Box m a
  }
-- | Representation of under-construction things
--   having type @a@ and containing variables.
data Box m a
  = Box'Closed a
  | Box'Env (EnvVar m) (Closure m a)

data AnyVar m = forall a. AnyVar (Var m a)
type EnvVar m = M.Map (Numbering m) (AnyVar m)
data AnyOne = forall a. AnyOne a
type EnvOne m = M.Map (Numbering m) AnyOne
newtype Closure m a = Closure { unClosure :: (EnvOne m) -> m a }

instance Functor m => Functor (Closure m) where
  fmap f cla = Closure $ fmap f . unClosure cla

instance Applicative m => Applicative (Closure m) where
  pure a = Closure $ const $ pure a
  clf <*> cla = Closure $ \env -> unClosure clf env <*> unClosure cla env

closureJoin :: Monad m => Closure m (m a) -> Closure m a
closureJoin cl = Closure $ \env -> join $ unClosure cl env

instance MonadNumbering m => Eq (Var m a) where
  Var x _ == Var y _ = x == y

instance MonadNumbering m => Ord (Var m a) where
  Var x _ `compare` Var y _ = x `compare` y

$(makeLenses ''Var)
$(makeLenses ''VarBody)


var'Name :: Lens' (Var m a) Text
var'Name = var'Body . varBody'Name
var'mkFree :: Lens' (Var m a) (Var m a -> m a)
var'mkFree = var'Body . varBody'mkFree
var'Box :: Lens' (Var m a) (Box m a)
var'Box = var'Body . varBody'Box

instance Show (Var m a) where
  showsPrec n x = showsPrec n $ x ^. var'Name
instance Show (VarBody m a) where
  showsPrec n x = showsPrec n $ x ^. varBody'Name
instance Show (AnyVar m) where
  showsPrec n (AnyVar x) = showsPrec n $ x ^. var'Name

-- | The name of variable.
nameOf :: Var m a -> Text
nameOf x = x ^. var'Name

-- | Smart constructor for 'Box'.
boxVar :: Var m a -> Box m a
boxVar x = x ^. var'Box

-- | Create a new variable with given name.
newVar :: forall m a. MonadNumbering m => Text -> (Var m a -> m a) -> m (Var m a)
newVar name mkFree = do
  i <- numbering
  let x = let b = Box'Env
                (M.singleton i $ AnyVar x)
                (Closure $ \env ->
                  let f (AnyOne y) = pure $ unsafeCoerce y
                   in f $ fromJust $ M.lookup i env)
           in Var i $ VarBody name mkFree b
  return x


-- | 'Box' is closed if it exposes no free variables.
isClosed :: Box m a -> Bool
isClosed Box'Closed{} = True
isClosed Box'Env{} = False

-- | Check if the variable occurs in the box.
occur :: MonadNumbering m => Var m a -> Box m b -> Bool
occur _ (Box'Closed _) = False
occur v (Box'Env vs _) = M.member (v ^. var'Key) vs


instance Functor m => Functor (Box m) where
  fmap f (Box'Closed a) = Box'Closed (f a)
  fmap f (Box'Env vs ta) = Box'Env vs (f <$> ta)

instance MonadNumbering m => Applicative (Box m) where
  pure = Box'Closed
  Box'Closed f <*> Box'Closed a = Box'Closed (f a)
  Box'Closed f <*> Box'Env va ta = Box'Env va (f <$> ta)
  Box'Env vf tf <*> Box'Closed a = Box'Env vf (appClosure tf a)
   where
    appClosure clf x = Closure $ \env -> unClosure clf env <*> pure x
  Box'Env vf tf <*> Box'Env va ta = Box'Env (M.union vf va) (tf <*> ta)

-- | Pick out and complete the construction of @a@.
unbox :: forall m a. Monad m => Box m a -> m a
unbox (Box'Closed t) = pure t
unbox (Box'Env env cl) = unClosure cl =<< traverse f env
 where
  f (AnyVar x) = fmap AnyOne $ x ^. var'mkFree $ x

box :: MonadNumbering m => a -> Box m a
box = pure
apBox :: MonadNumbering m => Box m (a -> b) -> Box m a -> Box m b
apBox = (<*>)
boxApply :: Functor m => (a -> b) -> Box m a -> Box m b
boxApply = fmap
boxApply2 :: MonadNumbering m => (a -> b -> c) -> Box m a -> Box m b -> Box m c
boxApply2 f ta tb = f <$> ta <*> tb
boxApply3 :: MonadNumbering m => (a -> b -> c -> d) -> Box m a -> Box m b -> Box m c -> Box m d
boxApply3 f ta tb tc = f <$> ta <*> tb <*> tc
boxApply4 :: MonadNumbering m => (a -> b -> c -> d -> e) -> Box m a -> Box m b -> Box m c -> Box m d -> Box m e
boxApply4 f ta tb tc td = f <$> ta <*> tb <*> tc <*> td
boxPair :: MonadNumbering m => Box m a -> Box m b -> Box m (a, b)
boxPair = boxApply2 (,)
boxTriple :: MonadNumbering m => Box m a -> Box m b -> Box m c -> Box m (a, b, c)
boxTriple = boxApply3 (,,)
boxT :: (MonadNumbering m, Traversable t) => t (Box m a) -> Box m (t a)
boxT = sequenceA
boxList :: MonadNumbering m => [Box m a] -> Box m [a]
boxList = sequenceA
boxJoin :: MonadNumbering m => Box m (m a) -> m (Box m a)
boxJoin (Box'Closed ma) = return . Box'Closed =<< ma
boxJoin (Box'Env env cl) = return $ Box'Env env $ closureJoin cl


-- | Variable binding.
--   Essentially, @Binder a m b@ means @a -> m b@.
data Binder a m b = Binder
  { _binder'Name :: Text
  , _binder'mkFree :: Var m a -> m a
  , _binder'Body :: a -> m b
  }

$(makeLenses ''Binder)

-- | Variable substitution.
subst :: Binder a m b -> a -> m b
subst b = b ^. binder'Body

-- | unbinding
unbind :: MonadNumbering m => Binder a m b -> m (Var m a, b)
unbind b = do
  let mkFree = b ^. binder'mkFree
  x <- newVar (b ^. binder'Name) mkFree
  y <- subst b =<< mkFree x
  return (x, y)

unbind2 :: MonadNumbering m
        => Binder a m b1 -> Binder a m b2 -> m (Var m a, b1, b2)
unbind2 b1 b2 = do
  let mkFree = b1 ^. binder'mkFree
  x <- newVar (b1 ^. binder'Name) mkFree
  v <- mkFree x
  y1 <- subst b1 v
  y2 <- subst b2 v
  return (x, y1, y2)

-- | Check if two bindings are equal.
eqBinder :: MonadNumbering m
         => (b -> b -> m Bool) -> Binder a m b -> Binder a m b -> m Bool
eqBinder eq f g = do
  (_, t, u) <- unbind2 f g
  eq t u


-- | Smart constructor for 'Binder'.
buildBinder :: Var m a -> (a -> m b) -> Binder a m b
buildBinder x body = Binder (x ^. var'Name) (x ^. var'mkFree) body

-- | binding
bind :: MonadNumbering m => Var m a -> Box m b -> Box m (Binder a m b)
bind x (Box'Closed t) = Box'Closed $ buildBinder x $ const $ return t
bind x (Box'Env vs t) =
  let vs' = M.delete (x ^. var'Key) vs in if length vs' == 0
    then Box'Closed $ buildBinder x $
      \arg -> unClosure t $ M.singleton (x ^. var'Key) (AnyOne arg)
    else Box'Env vs' $ Closure $
      \ms -> return $ buildBinder x $
      \arg -> unClosure t $ M.insert (x ^. var'Key) (AnyOne arg) ms

boxBinder :: MonadNumbering m
          => (b -> m (Box m b)) -> Binder a m b -> m (Box m (Binder a m b))
boxBinder f b = do
  (x, t) <- unbind b
  ft <- f t
  return $ bind x ft

bindApply :: MonadNumbering m => Box m (Binder a m b) -> Box m a -> m (Box m b)
bindApply b arg = boxJoin $ subst <$> b <*> arg


type VarList m a = [Var m a]

varList'Keys :: Getter (VarList m a) [Numbering m]
varList'Keys = to $ fmap $ view var'Key
varList'Names :: Getter (VarList m a) [Text]
varList'Names = to $ fmap $ view var'Name
varList'Boxes :: Getter (VarList m a) [Box m a]
varList'Boxes = to $ fmap $ view var'Box

-- | The names of variables.
namesOf :: VarList m a -> [Text]
namesOf = fmap $ view var'Name

-- | Smart constructor for a list of 'Box'.
boxVarList :: VarList m a -> [Box m a]
boxVarList = fmap $ view var'Box

-- | Create new variables with given names.
newVarList :: MonadNumbering m => [Text] -> (Var m a -> m a) -> m (VarList m a)
newVarList names mkFree = sequence $ flip fmap names $ \name -> newVar name mkFree


-- | Essentially, @BinderList a m b@ means @[a] -> m b@.
data BinderList a m b = BinderList
  { _binderList'Names :: [Text]
  , _binderList'mkFree :: Var m a -> m a
  , _binderList'Body :: [a] -> m b
  }

$(makeLenses ''BinderList)

binderList'Arity :: Getter (BinderList a m b) Int
binderList'Arity = binderList'Names . to length

-- | Variable substitution.
substList :: BinderList a m b -> [a] -> m b
substList ba = ba ^. binderList'Body

-- | unbinding
unbindList :: MonadNumbering m => BinderList a m b -> m (VarList m a, b)
unbindList ba = do
  let mkFree = ba ^. binderList'mkFree
  xs <- newVarList (ba ^. binderList'Names) mkFree
  y <- substList ba =<< traverse mkFree xs
  return (xs, y)

unbind2List :: MonadNumbering m
             => BinderList a m b1 -> BinderList a m b2
             -> m (VarList m a, b1, b2)
unbind2List ba1 ba2 = do
  let mkFree = ba1 ^. binderList'mkFree
  xs <- newVarList (ba1 ^. binderList'Names) mkFree
  vs <- traverse mkFree xs
  y1 <- substList ba1 vs
  y2 <- substList ba2 vs
  return (xs, y1, y2)

-- | Check if two bindings are equal.
eqBinderList :: MonadNumbering m
              => (b -> b -> m Bool)
              -> BinderList a m b -> BinderList a m b -> m Bool
eqBinderList eq f g =
  if f ^. binderList'Arity /= g ^. binderList'Arity
    then return False
    else do
      (_, t, u) <- unbind2List f g
      eq t u

-- | Smart constructor for 'BinderList.
buildBinderList :: VarList m a -> ([a] -> m b) -> BinderList a m b
buildBinderList xs body =
  BinderList (xs ^. varList'Names) (head xs ^. var'mkFree) body

deleteList :: Ord k => [k] -> M.Map k a -> M.Map k a
deleteList = flip $ foldl $ \m k -> M.delete k m
insertList :: Ord k => [k] -> [a] -> M.Map k a -> M.Map k a
insertList ks xs m = foldl f m $ zip ks xs
 where
  f n (k, x) = M.insert k x n
zipList :: Ord k => [k] -> [a] -> M.Map k a
zipList ks xs = insertList ks xs M.empty

-- | binding
bindList :: MonadNumbering m
          => VarList m a -> Box m b -> Box m (BinderList a m b)
bindList xs (Box'Closed t) = Box'Closed $ buildBinderList xs $ const $ return t
bindList xs (Box'Env vs t) =
  let vs' = deleteList (xs ^. varList'Keys) vs in if length vs' == 0
    then Box'Closed $ buildBinderList xs $
      \args -> unClosure t $
      zipList (xs ^. varList'Keys) (AnyOne <$> args)
    else Box'Env vs' $ Closure $
      \ms -> return $ buildBinderList xs $
      \args -> unClosure t $
      insertList (xs ^. varList'Keys) (AnyOne <$> args) ms

boxBinderList :: MonadNumbering m
               => (b -> m (Box m b)) -> BinderList a m b
               -> m (Box m (BinderList a m b))
boxBinderList f b = do
  (xs, t) <- unbindList b
  ft <- f t
  return $ bindList xs ft

bindListApply :: MonadNumbering m
              => Box m (BinderList a m b) -> Box m [a] -> m (Box m b)
bindListApply b args = boxJoin $ substList <$> b <*> args