{-# LANGUAGE GADTs, DeriveDataTypeable, Rank2Types, ViewPatterns #-} -- | -- Module : Data.Binding.Hobbits.Internal.Name -- Copyright : (c) 2014 Edwin Westbrook, Nicolas Frisby, and Paul Brauner -- -- License : BSD3 -- -- Maintainer : westbrook@kestrel.edu -- Stability : experimental -- Portability : GHC -- -- This module defines the type @'Mb' ctx a@. In order to ensure -- adequacy of the Hobbits name-binding approach, this representation -- is hidden, and so this file should never be imported directly by -- the user. -- module Data.Binding.Hobbits.Internal.Mb where import Data.Typeable import Data.Proxy import Data.Type.Equality import Data.Type.RList import Data.Binding.Hobbits.Internal.Name {-| An @Mb ctx b@ is a multi-binding that binds one name for each type in @ctx@, where @ctx@ has the form @'RNil' ':>' t1 ':>' ... ':>' tn@. Internally, multi-bindings are represented either as "fresh functions", which are functions that quantify over all fresh names that have not been used before and map them to the body of the binding, or as "fresh pairs", which are pairs of a list of names that are guaranteed to be fresh along with a body. Note that fresh pairs must come with an 'MbTypeRepr' for the body type, to ensure that the names given in the pair can be relaced by fresher names. -} data Mb ctx b = MkMbFun (MapRList Proxy ctx) (MapRList Name ctx -> b) | MkMbPair (MbTypeRepr b) (MapRList Name ctx) b deriving Typeable {-| This type states that it is possible to replace free names with fresh names in an object of type @a@. This type essentially just captures a representation of the type a as either a Name type, a multi-binding, a function type, or a (G)ADT. In order to be sure that only the "right" proofs are used for (G)ADTs, however, this type is hidden from the user, and can only be constructed with 'mkNuMatching'. -} data MbTypeRepr a where MbTypeReprName :: MbTypeRepr (Name a) MbTypeReprMb :: MbTypeRepr a -> MbTypeRepr (Mb ctx a) MbTypeReprFun :: MbTypeRepr a -> MbTypeRepr b -> MbTypeRepr (a -> b) MbTypeReprData :: MbTypeReprData a -> MbTypeRepr a data MbTypeReprData a = MkMbTypeReprData (forall ctx. MapRList Name ctx -> MapRList Name ctx -> a -> a) {-| The call @mapNamesPf data ns ns' a@ replaces each occurrence of a free name in @a@ which is listed in @ns@ by the corresponding name listed in @ns'@. This is similar to the name-swapping of Nominal Logic, except that the swapping does not go both ways. -} mapNamesPf :: MbTypeRepr a -> MapRList Name ctx -> MapRList Name ctx -> a -> a mapNamesPf MbTypeReprName MNil MNil n = n mapNamesPf MbTypeReprName (names :>: m) (names' :>: m') n = case cmpName m n of Just Refl -> m' Nothing -> mapNamesPf MbTypeReprName names names' n mapNamesPf MbTypeReprName _ _ _ = error "Should not be possible! (in mapNamesPf)" mapNamesPf (MbTypeReprMb tRepr) names1 names2 (ensureFreshFun -> (proxies, f)) = -- README: the fresh function created below is *guaranteed* to not -- be passed elements of either names1 or names2, since it should -- only ever be passed fresh names MkMbFun proxies (\ns -> mapNamesPf tRepr names1 names2 (f ns)) mapNamesPf (MbTypeReprFun tReprIn tReprOut) names names' f = (mapNamesPf tReprOut names names') . f . (mapNamesPf tReprIn names' names) mapNamesPf (MbTypeReprData (MkMbTypeReprData mapFun)) names names' x = mapFun names names' x -- | Ensures a multi-binding is in "fresh function" form ensureFreshFun :: Mb ctx a -> (MapRList Proxy ctx, MapRList Name ctx -> a) ensureFreshFun (MkMbFun proxies f) = (proxies, f) ensureFreshFun (MkMbPair tRepr ns body) = (mapMapRList (\_ -> Proxy) ns, \ns' -> mapNamesPf tRepr ns ns' body) -- | Ensures a multi-binding is in "fresh pair" form ensureFreshPair :: Mb ctx a -> (MapRList Name ctx, a) ensureFreshPair (MkMbPair _ ns body) = (ns, body) ensureFreshPair (MkMbFun proxies f) = let ns = mapMapRList (MkName . fresh_name) proxies in (ns, f ns)