unbound 0.2.3 → 0.2.4
raw patch · 17 files changed
+401/−1481 lines, 17 files
Files
- CHANGES +9/−0
- Unbound/LocallyNameless.hs +2/−2
- Unbound/LocallyNameless/Alpha.hs +56/−52
- Unbound/LocallyNameless/Fresh.hs +4/−4
- Unbound/LocallyNameless/Name.hs +1/−0
- Unbound/LocallyNameless/Ops.hs +1/−1
- Unbound/LocallyNameless/Subst.hs +6/−6
- Unbound/LocallyNameless/Test.hs +0/−153
- Unbound/LocallyNameless/Types.hs +2/−2
- Unbound/Nominal.hs +0/−70
- Unbound/Nominal/Internal.hs +0/−977
- Unbound/Nominal/Name.hs +0/−130
- Unbound/PermM.hs +58/−69
- examples/Basic.hs +9/−9
- test/PermTest.hs +93/−0
- test/Test.hs +153/−0
- unbound.cabal +7/−6
CHANGES view
@@ -19,3 +19,12 @@ - examples/STLC.hs Thanks to Ki Yung Ahn for the reports.++Version 0.2.4: 13 July 2011++ * Fix bug in mkPerm which caused unbind2 to unexpectedly fail when+ dealing with binders with non-disjoint sets of names.++ Thanks to Sean Leather for the report.++ * Clean up some compiler warnings.
Unbound/LocallyNameless.hs view
@@ -37,8 +37,8 @@ -- of the library source package: @cabal unpack unbound@) and the -- examples in the @example@ directory. -- --- See also: Stephanie Weirich, Brent Yorgey, and Tim Sheard. --- /Binders Unbound/. Submitted, March 2011. <http://www.cis.upenn.edu/~byorgey/papers/binders-unbound.pdf>. +-- See also: Stephanie Weirich, Brent A. Yorgey, and Tim Sheard. +-- /Binders Unbound/. To appear in ICFP'11, September 2011, Tokyo, Japan. <http://www.cis.upenn.edu/~byorgey/papers/binders-unbound.pdf>. ---------------------------------------------------------------------- module Unbound.LocallyNameless
Unbound/LocallyNameless/Alpha.hs view
@@ -463,20 +463,20 @@ findpatR1 :: R1 AlphaD b -> b -> AnyName -> FindResult -findpatR1 (Data1 dt cons) = \ d n -> +findpatR1 (Data1 _dt cons) = \ d n -> case findCon cons d of - Val c rec kids -> findpatL rec kids n -findpatR1 _ = \ x n -> mempty + Val _c rec kids -> findpatL rec kids n +findpatR1 _ = \ _ _ -> mempty findpatL :: MTup AlphaD l -> l -> AnyName -> FindResult -findpatL MNil Nil n = mempty +findpatL MNil Nil _ = mempty findpatL (r :+: rs) (t :*: ts) n = findpatD r t n <> findpatL rs ts n nthpatR1 :: R1 AlphaD b -> b -> NthCont -nthpatR1 (Data1 dt cons) = \ d -> +nthpatR1 (Data1 _dt cons) = \ d -> case findCon cons d of - Val c rec kids -> nthpatL rec kids -nthpatR1 _ = \ x -> mempty + Val _c rec kids -> nthpatL rec kids +nthpatR1 _ = \ _ -> mempty nthpatL :: MTup AlphaD l -> l -> NthCont nthpatL MNil Nil = mempty @@ -488,43 +488,39 @@ combine _ _ = Nothing isPatR1 :: R1 AlphaD b -> b -> Maybe [AnyName] -isPatR1 (Data1 dt cons) = \ d -> +isPatR1 (Data1 _dt cons) = \ d -> case findCon cons d of - Val c rec kids -> + Val _c rec kids -> foldl_l (\ c b a -> combine (isPatD c a) b) (Just []) rec kids -isPatR1 _ = \ d -> Just [] +isPatR1 _ = \ _ -> Just [] isTermR1 :: R1 AlphaD b -> b -> Bool -isTermR1 (Data1 dt cons) = \ d -> +isTermR1 (Data1 _dt cons) = \ d -> case findCon cons d of - Val c rec kids -> foldl_l (\ c b a -> isTermD c a && b) True rec kids -isTermR1 _ = \ d -> True + Val _c rec kids -> foldl_l (\ c b a -> isTermD c a && b) True rec kids +isTermR1 _ = \ _ -> True -- Exactly like the generic Ord instance defined in Generics.RepLib.PreludeLib, -- except that the comparison operation takes an AlphaCtx acompareR1 :: R1 AlphaD a -> AlphaCtx -> a -> a -> Ordering -acompareR1 Int1 c = \x y -> compare x y -acompareR1 Char1 c = \x y -> compare x y -acompareR1 (Data1 str cons) c = \x y -> +acompareR1 Int1 _ = \x y -> compare x y +acompareR1 Char1 _ = \x y -> compare x y +acompareR1 (Data1 _ cons) c = \x y -> let loop (Con emb rec : rest) = case (from emb x, from emb y) of (Just t1, Just t2) -> compareTupM rec c t1 t2 - (Just t1, Nothing) -> LT - (Nothing, Just t2) -> GT + (Just _ , Nothing) -> LT + (Nothing, Just _ ) -> GT (Nothing, Nothing) -> loop rest + loop [] = error "acompareR1 found no constructors! Please report this as a bug." in loop cons -acompareR1 r1 c = error ("compareR1 not supported for " ++ show r1) - -lexord :: Ordering -> Ordering -> Ordering -lexord LT ord = LT -lexord EQ ord = ord -lexord GT ord = GT +acompareR1 r1 _ = error ("compareR1 not supported for " ++ show r1) compareTupM :: MTup AlphaD l -> AlphaCtx -> l -> l -> Ordering -compareTupM MNil c Nil Nil = EQ +compareTupM MNil _ Nil Nil = EQ compareTupM (x :+: xs) c (y :*: ys) (z :*: zs) = - lexord (acompareD x c y z) (compareTupM xs c ys zs) + mappend (acompareD x c y z) (compareTupM xs c ys zs) ------------------------------------------------------------ @@ -553,12 +549,12 @@ case gcastR (getR y) (getR x) y of Just y' -> y' Nothing -> error "Internal error in swaps': sort mismatch" - swaps' c p x | mode c == Pat = x + swaps' _ _ x = x - aeq' _ x y | x == y = True - aeq' c n1 n2 | mode c == Term = False - aeq' c _ _ | mode c == Pat = True + aeq' c x y | mode c == Term = x == y + aeq' _ _ _ = True + {- match' _ x y | x == y = Just empty match' c n1 n2 | mode c == Term = Just $ single (AnyName n1) (AnyName n2) @@ -567,8 +563,9 @@ freshen' c nm | mode c == Pat = do x <- fresh nm return (x, single (AnyName nm) (AnyName x)) - freshen' c nm | mode c == Term = error "freshen' on Name in Term mode" + freshen' _ _ = error "freshen' on Name in Term mode" + lfreshen' c nm f = case mode c of Pat -> do x <- lfresh nm avoid [AnyName x] $ f x (single (AnyName nm) (AnyName x)) @@ -581,7 +578,7 @@ Nothing -> error "Internal error in open: sort mismatch" open _ _ n = n - close c a nm@(Nm r n) | mode c == Term = + close c a nm@(Nm r _) | mode c == Term = case findpat a (AnyName nm) of Just x -> Bn r (level c) x Nothing -> nm @@ -595,15 +592,16 @@ nthpatrec = nthName . AnyName acompare' c (Nm r1 n1) (Nm r2 n2) - | mode c == Term = lexord (compare r1 r2) (compare n1 n2) + | mode c == Term = mconcat [compare r1 r2, compare n1 n2] acompare' c (Bn r1 m1 n1) (Bn r2 m2 n2) - | mode c == Term = lexord (compare r1 r2) (lexord (compare m1 m2) (compare n1 n2)) + | mode c == Term = mconcat [compare r1 r2, compare m1 m2, compare n1 n2] acompare' c (Nm _ _) (Bn _ _ _) | mode c == Term = LT acompare' c (Bn _ _ _) (Nm _ _) | mode c == Term = GT - acompare' c _ _ | mode c == Pat = EQ + acompare' _ _ _ = EQ + instance Alpha AnyName where isTerm _ = True @@ -620,8 +618,9 @@ aeq' _ x y | x == y = True aeq' c _ _ | mode c == Term = False - aeq' c _ _ | mode c == Pat = True + aeq' _ _ _ = True + {- match' _ x y | x == y = Just empty match' c (AnyName n1) (AnyName n2) @@ -639,9 +638,8 @@ EQ -> case gcastR (getR n1) (getR n2) n1 of Just n1' -> acompare' c n1' n2 Nothing -> error "impossible" - otherwise -> otherwise - acompare' c _ _ | mode c == Pat = EQ - + neq -> neq + acompare' _ _ _ = EQ freshen' c (AnyName nm) = case mode c of Pat -> do x <- fresh nm @@ -656,7 +654,7 @@ open c a (AnyName (Bn _ j x)) | level c == j = nthpat a x open _ _ n = n - close c a nm@(AnyName (Nm r n)) = + close c a nm@(AnyName (Nm r _)) = case findpat a nm of Just x -> AnyName (Bn r (level c) x) Nothing -> nm @@ -706,7 +704,7 @@ -- Comparing two binding terms. acompare' c (B p1 t1) (B p2 t2) = - lexord (acompare' (pat c) p1 p2) (acompare' (incr c) t1 t2) + mappend (acompare' (pat c) p1 p2) (acompare' (incr c) t1 t2) findpatrec _ b = error $ "Binding " ++ show b ++ " used as a pattern" nthpatrec b = error $ "Binding " ++ show b ++ " used as a pattern" @@ -744,7 +742,7 @@ -} acompare' c (R a1 a2) (R b1 b2) = - lexord (acompare' c a1 b1) (acompare' (incr c) a2 b2) + mappend (acompare' c a1 b1) (acompare' (incr c) a2 b2) open c a (R p q) = R (open c a p) (open (incr c) a q) @@ -778,14 +776,18 @@ -- and we generally should treat the annots as constants instance Alpha t => Alpha (Embed t) where isPat (Embed t) = if (isTerm t) then Just [] else Nothing - isTerm t = False + isTerm _ = False isEmbed (Embed t) = isTerm t - swaps' c pm (Embed t) | mode c == Pat = Embed (swaps' (term c) pm t) - swaps' c pm (Embed t) | mode c == Term = Embed t + swaps' c pm (Embed t) = + case mode c of + Pat -> Embed (swaps' (term c) pm t) + Term -> Embed t - fv' c (Embed t) | mode c == Pat = fv' (term c) t - fv' c _ | mode c == Term = emptyC + fv' c (Embed t) = + case mode c of + Pat -> fv' (term c) t + Term -> emptyC freshen' c p | mode c == Term = error "freshen' called on a term" | otherwise = return (p, empty) @@ -805,11 +807,13 @@ else Nothing -} - close c b (Embed x) | mode c == Pat = Embed (close (term c) b x) - | mode c == Term = error "close on Embed" + close c b (Embed x) = case mode c of + Pat -> Embed (close (term c) b x) + Term -> error "close on Embed" - open c b (Embed x) | mode c == Pat = Embed (open (term c) b x) - | mode c == Term = error "open on Embed" + open c b (Embed x) = case mode c of + Pat -> Embed (open (term c) b x) + Term -> error "open on Embed" findpatrec _ _ = mempty nthpatrec _ = mempty @@ -824,7 +828,7 @@ -- The contents of Shift may only be an Embed or another Shift. isPat (Shift a) = if (isEmbed a) then Just [] else Nothing - isTerm a = False + isTerm _ = False isEmbed (Shift a) = isEmbed a close c b (Shift x) = Shift (close (decr c) b x)
Unbound/LocallyNameless/Fresh.hs view
@@ -25,14 +25,14 @@ Fresh(..), - FreshM(..), runFreshM, contFreshM, + FreshM, runFreshM, contFreshM, FreshMT(..), runFreshMT, contFreshMT, -- * The 'LFresh' class LFresh(..), - LFreshM(..), runLFreshM, contLFreshM, getAvoids, + LFreshM, runLFreshM, contLFreshM, getAvoids, LFreshMT(..), runLFreshMT, contLFreshMT ) where @@ -55,7 +55,6 @@ import Control.Monad.Trans.Identity import Control.Monad.Trans.List import Control.Monad.Trans.Maybe -import Control.Monad.Trans.Reader (ReaderT) import Control.Monad.Trans.State.Lazy as Lazy import Control.Monad.Trans.State.Strict as Strict import Control.Monad.Trans.Writer.Lazy as Lazy @@ -65,7 +64,6 @@ import qualified Control.Monad.Error.Class as EC import qualified Control.Monad.State.Class as StC import qualified Control.Monad.Reader.Class as RC -import qualified Control.Monad.IO.Class as IC ------------------------------------------------------------ -- Fresh @@ -98,6 +96,8 @@ n <- St.get St.put (n+1) return $ Nm r (s,n) + + fresh (Bn {}) = error "fresh encountered bound name! Please report this as a bug." -- | A convenient monad which is an instance of 'Fresh'. It keeps -- track of a global index used for generating fresh names, which is
Unbound/LocallyNameless/Name.hs view
@@ -7,6 +7,7 @@ , MultiParamTypeClasses , ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} ---------------------------------------------------------------------- -- | -- Module : Unbound.LocallyNameless.Name
Unbound/LocallyNameless/Ops.hs view
@@ -1,4 +1,4 @@-+{-# OPTIONS_GHC -fno-warn-orphans #-} ---------------------------------------------------------------------- -- | -- Module : Unbound.LocallyNameless.Ops
Unbound/LocallyNameless/Subst.hs view
@@ -42,7 +42,7 @@ -- non-variable arguments. The default implementation always -- returns 'Nothing'. isvar :: a -> Maybe (SubstName a b)- isvar x = Nothing+ isvar _ = Nothing -- | @'subst' nm sub tm@ substitutes @sub@ for @nm@ in @tm@. It has -- a default generic implementation in terms of @isvar@.@@ -51,7 +51,7 @@ case (isvar x :: Maybe (SubstName a b)) of Just (SubstName m) -> if m == n then u else x Nothing -> substR1 rep1 n u x- subst m u x = error $ "Cannot substitute for bound variable " ++ show m+ subst m _ _ = error $ "Cannot substitute for bound variable " ++ show m -- | Perform several simultaneous substitutions. This method also -- has a default generic implementation in terms of @isvar@.@@ -81,20 +81,20 @@ substDefault = substR1 rep1 substR1 :: R1 (SubstD b) a -> Name b -> b -> a -> a-substR1 (Data1 dt cons) = \ x y d ->+substR1 (Data1 _dt cons) = \ x y d -> case (findCon cons d) of Val c rec kids -> let z = map_l (\ w -> substD w x y) rec kids in (to c z)-substR1 r = \ x y c -> c+substR1 _ = \ _ _ c -> c substsR1 :: R1 (SubstD b) a -> [(Name b, b)] -> a -> a-substsR1 (Data1 dt cons) = \ s d ->+substsR1 (Data1 _dt cons) = \ s d -> case (findCon cons d) of Val c rec kids -> let z = map_l (\ w -> substsD w s) rec kids in (to c z)-substsR1 r = \ s c -> c+substsR1 _ = \ _ c -> c instance Subst b Int instance Subst b Bool
− Unbound/LocallyNameless/Test.hs
@@ -1,153 +0,0 @@-{-# LANGUAGE TemplateHaskell- , MultiParamTypeClasses- , FlexibleInstances- , FlexibleContexts- , ScopedTypeVariables- , UndecidableInstances- #-}--module Unbound.LocallyNameless.Test where--import qualified Data.Set as S--import Unbound.LocallyNameless hiding (GT)-import Unbound.LocallyNameless.Alpha-import Unbound.LocallyNameless.Name-import Unbound.PermM---------------------- TESTING CODE ---------------------------------data Exp = V (Name Exp)- | A Exp Exp- | L (Bind (Name Exp) Exp) deriving (Show)--$(derive [''Exp])--instance Alpha Exp-instance Subst Exp Exp where- isvar (V n) = Just (SubstName n)- isvar _ = Nothing--nameA, nameB, nameC :: Name Exp-nameA = integer2Name 1-nameB = integer2Name 2-nameC = integer2Name 3--assert :: String -> Bool -> IO ()-assert s True = return ()-assert s False = print ("Assertion " ++ s ++ " failed")--do_tests :: IO ()-do_tests = do- tests_aeq- tests_fv- tests_big- tests_nth- tests_acompare--perm = single nameA nameB--naeq x y = not (aeq x y)--tests_aeq = do- assert "a1" $ (bind nameA nameA) `naeq` (bind nameA nameB)- assert "a2" $ (bind nameA nameA) `aeq` (bind nameA nameA)- assert "a3" $ (bind nameA nameA) `aeq` (bind nameB nameB)- assert "a4" $ (bind nameA nameB) `naeq` (bind nameB nameA)- assert "a5" $ (bind (nameA, Embed nameB) nameA) `naeq`- (bind (nameA, Embed nameC) nameA)- assert "a6" $ (bind (nameA, Embed nameB) nameA) `aeq`- (bind (nameA, Embed nameB) nameA)- assert "a7" $ (bind (nameA, Embed nameB) nameA) `aeq`- (bind (nameB, Embed nameB) nameB)- assert "a8" $ rebind nameA nameB `naeq` rebind nameB nameB- assert "a9" $ rebind nameA nameA `naeq` rebind nameB nameB- assert "a9" $ (bind (rebind nameA (Embed nameA)) nameA) `aeq`- (bind (rebind nameB (Embed nameB)) nameB)- assert "a10" $ bind (rebind (nameA, Embed nameA) ()) nameA `aeq`- bind (rebind (nameB, Embed nameA) ()) nameB- assert "a11" $ bind (rebind (nameA, Embed nameA) ()) nameA `naeq`- bind (rebind (nameB, Embed nameB) ()) nameB- assert "a12" $ bind (Embed nameA) () `naeq` bind (Embed nameB) ()- assert "a13" $ bind (Embed nameA) () `aeq` bind (Embed nameA) ()- assert "a14" $ bind (rebind (Embed nameA) ()) () `naeq`- bind (rebind (Embed nameB) ()) ()- assert "a15" $ (rebind (nameA, Embed nameA) ()) `naeq`- (rebind (nameA, Embed nameC) ())- assert "a16" $ bind (nameA, nameB) nameA `naeq` bind (nameB, nameA) nameA- assert "a17" $ bind (nameA, nameB) nameA `naeq` bind (nameA, nameB) nameB- assert "a18" $ (nameA, nameA) `naeq` (nameA, nameB)--- assert "a19" $ match (nameA, nameA) (nameB, nameC) == Nothing--emptyNE :: S.Set (Name Exp)-emptyNE = S.empty--tests_fv = do- assert "f1" $ fv (bind nameA nameA) == emptyNE- assert "f2" $ fv' (pat initial) (bind nameA nameA) == S.empty- assert "f4" $ fv (bind nameA nameB) == S.singleton nameB- assert "f5" $ fv (bind (nameA, Embed nameB) nameA) == S.singleton nameB- assert "f7" $ fv (bind (nameB, Embed nameB) nameB) == S.singleton nameB- assert "f8" $ fv (rebind nameA nameB) == S.fromList [nameA, nameB]- assert "f9" $ fv' (pat initial) (rebind nameA nameA) == S.empty- assert "f3" $ fv (bind (rebind nameA (Embed nameA)) nameA) == emptyNE- assert "f10" $ fv (rebind (nameA, Embed nameA) ()) == S.singleton nameA- assert "f11" $ fv' (pat initial) (rebind (nameA, Embed nameA) ()) == S.singleton (AnyName nameA)- assert "f12" $ fv (bind (Embed nameA) ()) == S.singleton nameA- assert "f14" $ fv (rebind (Embed nameA) ()) == emptyNE--mkbig :: [Name Exp] -> Exp -> Exp-mkbig (n : names) body =- L (bind n (mkbig names (A (V n) body)))-mkbig [] body = body--big1 = mkbig (map integer2Name (take 100 [1 ..])) (V nameA)-big2 = mkbig (map integer2Name (take 101 [1 ..])) (V nameA)---tests_nth = do- assert "n1" $ nthpat ([nameA],nameB) 0 == AnyName nameA- assert "n2" $ nthpat ([nameA],nameB) 1 == AnyName nameB- assert "n3" $ nthpat (nameA, nameB) 0 == AnyName nameA- assert "p1" $ findpat ([nameA],nameB) (AnyName nameA) == Just 0- assert "p2" $ findpat ([nameA],nameB) (AnyName nameB) == Just 1- assert "p3" $ findpat ([nameA],nameB) (AnyName nameC) == Nothing--tests_big = do- assert "b1" $ big1 `naeq` big2- assert "b2" $ fv big1 == emptyNE- assert "b3" $ big1 `aeq` subst nameA (V nameA) big1--tests_acompare = do- -- Names compare in the obvious way.- assert "ac1" $ acompare nameA nameB == LT- assert "ac2" $ acompare nameB nameB == EQ- assert "ac3" $ acompare nameB nameA == GT- -- structured date compares lexicographically- assert "ac4" $ acompare (A (V nameA) (V nameA)) (A (V nameA) (V nameA)) == EQ- assert "ac5" $ acompare (A (V nameA) (V nameA)) (A (V nameA) (V nameB)) == LT- assert "ac6" $ acompare (A (V nameA) (V nameB)) (A (V nameA) (V nameA)) == GT- assert "ac7" $ acompare (A (V nameA) (V nameA)) (A (V nameB) (V nameA)) == LT- assert "ac8" $ acompare (A (V nameB) (V nameA)) (A (V nameA) (V nameA)) == GT- assert "ac9" $ acompare (A (V nameB) (V nameA)) (A (V nameA) (V nameB)) == GT- -- comparison goes under binders, alpha-respectingly.- assert "ac10" $ acompare (bind nameA (A (V nameA) (V nameA))) (bind nameA (A (V nameA) (V nameA))) == EQ- assert "ac11" $ acompare (bind nameA (A (V nameA) (V nameA))) (bind nameA (A (V nameA) (V nameB))) == GT- assert "ac12" $ acompare (bind nameC (A (V nameC) (V nameA))) (bind nameA (A (V nameA) (V nameB))) == LT- -- non-matching binders handled alpha-respectingly.- assert "ac13" $ acompare (bind [nameA] nameA) (bind [nameA,nameB] nameA)- == acompare (bind [nameC] nameC) (bind [nameA,nameB] nameA)- assert "ac14" $ acompare (bind [nameA,nameB] nameA) (bind [nameA] nameA)- == acompare (bind [nameC,nameB] nameC) (bind [nameA] nameA)- -- non-binding stuff in patterns gets compared- assert "ac15" $ acompare (Embed nameA) (Embed nameB) == LT- assert "ac16" $ acompare (bind (nameC, Embed nameA) (A (V nameC) (V nameC)))- (bind (nameC, Embed nameB) (A (V nameC) (V nameC))) == LT- assert "ac17" $ acompare (bind (nameC, Embed nameA) (A (V nameB) (V nameB)))- (bind (nameC, Embed nameB) (A (V nameA) (V nameA))) == LT- -- TODO: do we need anything special for rebind? For AnyName?---- properties--- if match t1 t2 = Some p then swaps p t1 = t2--main :: IO ()-main = do_tests
Unbound/LocallyNameless/Types.hs view
@@ -120,7 +120,7 @@ newtype Embed t = Embed t deriving Eq instance Show a => Show (Embed a) where- showsPrec p (Embed a) = showString "{" . showsPrec 0 a . showString "}"+ showsPrec _ (Embed a) = showString "{" . showsPrec 0 a . showString "}" -- Shift --------------------------------------------------@@ -129,7 +129,7 @@ newtype Shift p = Shift p deriving Eq instance Show a => Show (Shift a) where- showsPrec p (Shift a) = showString "{" . showsPrec 0 a . showString "}"+ showsPrec _ (Shift a) = showString "{" . showsPrec 0 a . showString "}" -- Pay no attention...
− Unbound/Nominal.hs
@@ -1,70 +0,0 @@----------------------------------------------------------------------- --- | --- Module : Unbound.Nominal --- License : BSD-like (see LICENSE) --- Maintainer : Stephanie Weirich <sweirich@cis.upenn.edu> --- Stability : experimental --- Portability : non-portable (-XKitchenSink) --- --- A generic implementation of standard functions dealing with names --- and binding structure (alpha equivalence, free variable --- calculation, capture-avoiding substitution, name permutation, ...) --- using a nominal representation. --- --- DISCLAIMER: this module almost certainly contains bugs and may be --- slower than "Unbound.LocallyNameless". The documentation is also --- sparse and likely out of date. At this point we recommend it only --- for the curious or intrepid. We are actively working on bringing --- it up to speed as a viable alternative to --- "Unbound.LocallyNameless". --- --------------------------------------------------------------------------- -module Unbound.Nominal - (-- * Basic types - Name, AnyName(..), Bind, Embed(..), Rebind, Rec, Shift, - - -- ** Utilities - integer2Name, string2Name, name2Integer, name2String, makeName, - name1,name2,name3,name4,name5,name6,name7,name8,name9,name10, - translate, - - -- * The 'Alpha' class - Alpha(..), - swaps, -- is a bit wonky - match, - binders, patfv, fv, - aeq, - - -- * Binding operations - bind, unsafeUnbind, - - -- * The 'Fresh' class - Fresh(..), freshen, - unbind, unbind2, unbind3, - - -- * The 'LFresh' class - HasNext(..), LFresh(..), - lfreshen, - lunbind, lunbind2, lunbind3, - - -- * Rebinding operations - rebind, reopen, - - -- * Rec operations - rec, unrec, - - -- * Substitution - Subst(..), - - -- * Advanced - AlphaCtx, matchR1, - - -- * Pay no attention to the man behind the curtain - - -- | These type representation objects are exported so they can be - -- referenced by auto-generated code. Please pretend they do not - -- exist. - rName, rBind, rRebind, rEmbed, rRec, rShift) where - -import Unbound.Nominal.Name -import Unbound.Nominal.Internal
− Unbound/Nominal/Internal.hs
@@ -1,977 +0,0 @@-{-# LANGUAGE FlexibleInstances, UndecidableInstances, FlexibleContexts, MultiParamTypeClasses, TemplateHaskell, TypeOperators, ScopedTypeVariables, TypeSynonymInstances, RankNTypes, GADTs, EmptyDataDecls, StandaloneDeriving #-} - ----------------------------------------------------------------------- --- | --- Module : Unbound.Nominal.Internal --- License : BSD-like (see LICENSE) --- --------------------------------------------------------------------------- -module Unbound.Nominal.Internal where - -import Generics.RepLib -import Unbound.Nominal.Name -import Unbound.PermM - -import qualified Data.List as List -import qualified Text.Read as R -import Data.Set (Set) -import Data.Maybe -import qualified Data.Set as S -import Prelude hiding (or) -import Data.Monoid -import qualified Control.Monad as Monad -import Control.Monad.Reader (Reader,ask,local,runReader) -import System.IO.Unsafe (unsafePerformIO) - -(<>) :: Monoid m => m -> m -> m -(<>) = mappend - ---------------------------------------------------- - -$(derive_abstract [''R]) --- The above only works with GHC 7. - - --- | Type of a binding. Morally, the type a should be in the --- class 'Pattern' and the type b should be in the class 'Alpha'. --- The Pattern class contains the constructor and a safe --- destructor for these types. --- We can Bind an "a" object in a "b" object if we --- can create "fresh" a objects, and Names can be --- swapped in "b" objects. Often "a" is Name --- but that need not be the case. -data Bind a b = B a b - --- | An annotation is a 'hole' in a pattern where variables --- can be used, but not bound. For example patterns may include --- type annotations, and those annotations can reference variables --- without binding them. --- Annotations do nothing special when they appear elsewhere in terms -newtype Embed a = Embed a deriving (Read, Eq) - --- | Shift the scope of an embedded term one level outwards. -newtype Shift a = Shift a deriving Eq - --- | Rebinding is for telescopes --- i.e. to support patterns that --- also bind variables that appear later -data Rebind a b = R a (Bind [AnyName] b) - --- | 'Rec' supports recursive patterns --- that is, patterns where --- any variables anywhere in the pattern are bound in the pattern --- itself. Useful for lectrec (and Agda's dot notation). -data Rec a = Rec a - -$(derive [''Bind, ''Name, ''Embed, ''Rebind, ''Rec, ''Shift]) - ----------------------------------------------------------- --- Binding operations & instances ----------------------------------------------------------- - --- | Smart constructor for binders -bind :: (Alpha b,Alpha c) => b -> c -> Bind b c -bind a b = B a b - --- | A destructor for binders that does not guarantee fresh --- names for the binders. -unsafeUnbind :: Bind a b -> (a,b) -unsafeUnbind (B a b) = (a,b) - -instance (Show a, Show b) => Show (Bind a b) where - showsPrec p (B a b) = showParen (p>0) - (showString "<" . showsPrec p a . showString "> " . showsPrec 0 b) - -instance (Alpha a, Alpha b, Read a, Read b) => Read (Bind a b) where - readPrec = R.parens $ (R.prec app_prec $ do - R.Ident "B" <- R.lexP - m1 <- R.step R.readPrec - m2 <- R.step R.readPrec - return (bind m1 m2)) - where app_prec = 10 - - readListPrec = R.readListPrecDefault - ----------------------------------------------------------- --- Rebinding operations ----------------------------------------------------------- - --- | Constructor for binding in patterns -rebind :: (Alpha a, Alpha b) => a -> b -> Rebind a b -rebind a b = R a (bind (binders a) b) - -instance (Alpha a, Show a, Show b) => Show (Rebind a b) where - showsPrec p (R a (B a' b)) = showParen (p>0) - (showString "<<" . showsPrec p a . sa' . showString ">> " . showsPrec 0 b) - where sa' = if binders' initial a == a' then showString "" - else showString "/" . showsPrec p a' - --- | destructor for binding patterns, the external names --- should have already --- been freshen'ed. We swap the internal names so that they use the --- external names -reopen :: (Alpha a, Alpha b) => Rebind a b -> (a, b) -reopen (R a1 (B names b)) = (a1, swaps p b) where - p = fromJust $ Monad.foldM join empty - (zipWith single (binders' initial a1) names) - ----------------------------------------------------------- --- Rec operations ----------------------------------------------------------- - -rec :: (Alpha a) => a -> Rec a -rec a = Rec a where - -unrec :: (Alpha a) => Rec a -> a -unrec (Rec a) = a - -instance Show a => Show (Rec a) where - showsPrec p (Rec a) = showString "[" . showsPrec 0 a . showString "]" - ----------------------------------------------------------- --- Embed ----------------------------------------------------------- -instance (Show a) => Show (Embed a) where - showsPrec p (Embed a) = - (showString "{" . showsPrec 0 a . showString "}") - ----------------------------------------------------------- --- Wrappers for operations in the Alpha class ----------------------------------------------------------- - -aeq :: Alpha a => a -> a -> Bool -aeq t1 t2 = -- aeq' initial t1 t2 - case match t1 t2 of - Just p -> isid p - _ -> False - - --- | calculate the free variables of the term -fv :: (Rep b, Alpha a) => a -> Set (Name b) -fv = S.map fromJust . S.filter isJust . S.map toSortedName . fv' initial - --- | List the binding variables in a pattern -binders :: (Rep b, Alpha b) => b -> [AnyName] -binders = binders' initial - --- | Set of variables that occur freely in annotations (not binding) -patfv :: (Rep a, Alpha b) => b -> Set (Name a) -patfv = S.map fromJust . S.filter isJust . S.map toSortedName . fv' (pat initial) - --- | The method "swaps" applys a permutation to all free variables --- in the term. -swaps :: Alpha a => Perm AnyName -> a -> a -swaps = swaps' initial - --- | Apply a permutation to the binding variables in a pattern. --- Embedded terms are left alone by the permutation. -swapsBinders :: Alpha a => Perm AnyName -> a -> a -swapsBinders = swaps' initial - --- | Apply a permutation to the annotations in a pattern. Binding --- names are left alone by the permutation. -swapsEmbeds :: Alpha a => Perm AnyName -> a -> a -swapsEmbeds = swaps' (pat initial) - - --- | "Locally" freshen an object -lfreshen :: Alpha a => LFresh m => a -> (a -> Perm AnyName -> m b) -> m b -lfreshen = lfreshen' initial - --- | A pattern of type "b" can be freshened if a new --- copy of "b" can be produced where all old *binding* Names --- in "b" are replaced with new fresh Names, and the --- permutation reports which Names were swapped by others. -freshen :: (Fresh m, Alpha a) => a -> m (a, Perm AnyName) -freshen = freshen' initial - --- | Match compares two data structures and produces a permutation --- of their Names that will make them alpha-equivalent to --- eachother. (Names that appear in annotations must match exactly.) --- Also note that two terms are alpha-equivalent when the empty --- permutation is returned. -match :: Alpha a => a -> a -> Maybe (Perm AnyName) -match = match' initial - - --- | Compare two patterns, ignoring the names of binders, and produce --- a permutation of their annotations to make them alpha-equivalent --- to eachother. Return 'Nothing' if no such renaming is possible. -matchEmbeds :: Alpha a => a -> a -> Maybe (Perm AnyName) -matchEmbeds = match' (pat initial) - --- | Compare two patterns for equality and produce a permutation of --- their binding 'Names' to make them alpha-equivalent to each other --- ('Name's that appear in annotations must match exactly). Return --- 'Nothing' if no such renaming is possible. -matchBinders :: Alpha a => a -> a -> Maybe (Perm AnyName) -matchBinders = match' initial - ---------------------------------------------------------------- --- | Many of the operations in the 'Alpha' class take an 'AlphaCtx': --- stored information about the iteration as it progresses. This type --- is abstract, as classes that override these operations should just pass --- the context on. -data AlphaCtx = Term | Pat deriving (Show, Eq, Read) - -initial :: AlphaCtx -initial = Term - -pat :: AlphaCtx -> AlphaCtx -pat c = Pat - -term :: AlphaCtx -> AlphaCtx -term c = Term - -mode :: AlphaCtx -> AlphaCtx -mode = id - ------------------------------------------------------------- --- The Alpha class ------------------------------------------------------------- --- | The Alpha class is for all terms that may contain binders --- The 'Rep1' class constraint means that we can only --- make instances of this class for types that have --- generic representations. (Derive these using TH and --- RepLib.) - -class (Rep1 (AlphaD) a) => Alpha a where - - aeq' :: AlphaCtx -> a -> a -> Bool - aeq' = aeqR1 rep1 - - -- | swap everything, including bound and free variables, - -- parts in annots, etc. - swapall' :: AlphaCtx -> Perm AnyName -> a -> a - swapall' = swapallR1 rep1 - - -- | The method "swaps'" applys a compound permutation - swaps' :: AlphaCtx -> Perm AnyName -> a -> a - swaps' = swapsR1 rep1 - - -- | calculate the free variables (aka support) - fv' :: AlphaCtx -> a -> Set AnyName - fv' = fvR1 rep1 - - -- | list the binding variables in a pattern, in order - binders' :: AlphaCtx -> a -> [AnyName] - binders' = bindersR1 rep1 - - -- | Match' compares two data structures and produces a - -- permutation of their free variables that will make them - -- alpha-equivalent to eachother. - match' :: AlphaCtx -> a -> a -> Maybe (Perm AnyName) - match' = matchR1 rep1 - - -- | An object of type "a" can be freshened if a new - -- copy of "a" can be produced where all old Names - -- in "a" are replaced with new fresh Names, and the - -- permutation reports which names were swapped by others. - freshen' :: Fresh m => AlphaCtx -> a -> m (a,Perm AnyName) - freshen' = freshenR1 rep1 - - -- | See 'lfreshen' - lfreshen' :: LFresh m => AlphaCtx -> a -> (a -> Perm AnyName -> m b) -> m b - lfreshen' = lfreshenR1 rep1 - - --- class constraint hackery to allow us to override the --- default definitions for certain classes -data AlphaD a = AlphaD { - aeqD :: AlphaCtx -> a -> a -> Bool, - swapallD :: AlphaCtx -> (Perm AnyName) -> a -> a, - swapsD :: AlphaCtx -> (Perm AnyName) -> a -> a, - fvD :: AlphaCtx -> a -> Set AnyName, - bindersD :: AlphaCtx -> a -> [AnyName], - - matchD :: AlphaCtx -> a -> a -> Maybe (Perm AnyName), - freshenD :: forall m. Fresh m => AlphaCtx -> a -> m (a,Perm AnyName), - lfreshenD :: forall b m. LFresh m => AlphaCtx -> a -> (a -> Perm AnyName -> m b) -> m b - } - -instance Alpha a => Sat (AlphaD a) where - dict = AlphaD aeq' swapall' swaps' fv' binders' match' freshen' lfreshen' - --- Generic definitions of the class functions. --- (All functions that take representations end --- in 'R1') -aeqR1 :: R1 AlphaD a -> AlphaCtx -> a -> a -> Bool -aeqR1 (Data1 _ cons) = loop cons where - loop (Con emb reps : rest) p x y = - case (from emb x, from emb y) of - (Just p1, Just p2) -> aeq1 reps p p1 p2 - (Nothing, Nothing) -> loop rest p x y - (_,_) -> False - loop [] _ _ _ = error "Impossible" -aeqR1 Int1 = \ _ x y -> x == y -aeqR1 Integer1 = \ _ x y -> x == y -aeqR1 Char1 = \ _ x y -> x == y -aeqR1 _ = \ _ _ _ -> error "Cannot aeq this type" - -aeq1 :: MTup (AlphaD) l -> AlphaCtx -> l -> l -> Bool -aeq1 MNil _ Nil Nil = True -aeq1 (r :+: rs) c (p1 :*: t1) (p2 :*: t2) = - aeqD r c p1 p2 && aeq1 rs c t1 t2 - -swapsR1 :: R1 (AlphaD) a -> AlphaCtx -> (Perm AnyName) -> a -> a -swapsR1 Char1 = \ _ _ c -> c -swapsR1 Int1 = \ _ _ c -> c -swapsR1 Float1 = \ _ _ c -> c -swapsR1 Integer1 = \ _ _ c -> c -swapsR1 (Data1 _ cons) = \ p x d -> - case (findCon cons d) of - Val c rec kids -> to c (map_l (\z -> swapsD z p x) rec kids) -swapsR1 r = error ("Cannot swap type " ++ (show r)) - - -swapallR1 :: R1 (AlphaD) a -> AlphaCtx -> (Perm AnyName) -> a -> a -swapallR1 Char1 = \ _ _ c -> c -swapallR1 Int1 = \ _ _ c -> c -swapallR1 Float1 = \ _ _ c -> c -swapallR1 Integer1 = \ _ _ c -> c -swapallR1 (Data1 _ cons) = \ p x d -> - case (findCon cons d) of - Val c rec kids -> to c (map_l (\z -> swapallD z p x) rec kids) -swapallR1 r = error ("Cannot swap type " ++ (show r)) - -fvR1 :: R1 (AlphaD) a -> AlphaCtx -> a -> Set AnyName -fvR1 (Data1 _ cons) = \ p d -> - case (findCon cons d) of - Val _ rec kids -> fv1 rec p kids -fvR1 _ = \ _ _ -> S.empty - -fv1 :: MTup (AlphaD) l -> AlphaCtx -> l -> Set AnyName -fv1 MNil _ Nil = S.empty -fv1 (r :+: rs) p (p1 :*: t1) = - fvD r p p1 `S.union` fv1 rs p t1 - -bindersR1 :: R1 (AlphaD) a -> AlphaCtx -> a -> [AnyName] -bindersR1 (Data1 _ cons) = \ p d -> - case (findCon cons d) of - Val _ rec kids -> binders1 rec p kids -bindersR1 _ = \ _ _ -> [] - -binders1 :: MTup (AlphaD) l -> AlphaCtx -> l -> [AnyName] -binders1 MNil _ Nil = [] -binders1 (r :+: rs) p (p1 :*: t1) = - bindersD r p p1 ++ binders1 rs p t1 - - -matchR1 :: R1 (AlphaD) a -> AlphaCtx -> a -> a -> Maybe (Perm AnyName) -matchR1 (Data1 _ cons) = loop cons where - loop (Con emb reps : rest) p x y = - case (from emb x, from emb y) of - (Just p1, Just p2) -> match1 reps p p1 p2 - (Nothing, Nothing) -> loop rest p x y - (_,_) -> Nothing - loop [] _ _ _ = error "Impossible" -matchR1 Int1 = \ _ x y -> if x == y then Just empty else Nothing -matchR1 Integer1 = \ _ x y -> if x == y then Just empty else Nothing -matchR1 Char1 = \ _ x y -> if x == y then Just empty else Nothing -matchR1 _ = \ _ _ _ -> Nothing - -match1 :: MTup (AlphaD) l -> AlphaCtx -> l -> l -> Maybe (Perm AnyName) -match1 MNil _ Nil Nil = Just empty -match1 (r :+: rs) c (p1 :*: t1) (p2 :*: t2) = do - l1 <- matchD r c p1 p2 - l2 <- match1 rs c t1 t2 - (l1 `join` l2) - - -freshenR1 :: R1 (AlphaD) a -> Fresh m => AlphaCtx -> a -> m (a,Perm AnyName) -freshenR1 (Data1 _ cons) = \ p d -> - case findCon cons d of - Val c rec kids -> do - (l, p') <- freshenL rec p kids - return (to c l, p') -freshenR1 _ = \ _ n -> return (n, empty) - -freshenL :: Fresh m => MTup (AlphaD) l -> AlphaCtx -> l -> m (l, Perm AnyName) -freshenL MNil _ Nil = return (Nil, empty) -freshenL (r :+: rs) p (t :*: ts) = do - (xs, p2) <- freshenL rs p ts - (x, p1) <- freshenD r p (swapsD r p p2 t) - return ( x :*: xs, p1 <> p2) - -lfreshenR1 :: LFresh m => R1 AlphaD a -> AlphaCtx -> a -> - (a -> Perm AnyName -> m b) -> m b -lfreshenR1 (Data1 _ cons) = \p d f -> - case findCon cons d of - Val c rec kids -> lfreshenL rec p kids (\ l p' -> f (to c l) p') -lfreshenR1 _ = \ _ n f -> f n empty - -lfreshenL :: LFresh m => MTup (AlphaD) l -> AlphaCtx -> l -> - (l -> Perm AnyName -> m b) -> m b -lfreshenL MNil _ Nil f = f Nil empty -lfreshenL (r :+: rs) p (t :*: ts) f = - lfreshenL rs p ts ( \ y p2 -> - lfreshenD r p (swapsD r p p2 t) ( \ x p1 -> - f (x :*: y) (p1 <> p2))) - - -instance (Rep a) => Alpha (Name a) where - fv' c n@(Nm _ _) | mode c == Term = S.singleton (AnyName n) - fv' c n | mode c == Pat = S.empty - - binders' c n@(Nm _ _) | mode c == Term = [AnyName n] - binders' c n | mode c == Pat = [] - - swapall' c p x = - case apply p (AnyName x) of - AnyName y -> - case cast y of - Just y' -> y' - Nothing -> error "Internal error in swaps': sort mismatch" - - swaps' c p x | mode c == Term = - case apply p (AnyName x) of - AnyName y -> - case cast y of - Just y' -> y' - Nothing -> error "Internal error in swaps': sort mismatch" - swaps' c p x | mode c == Pat = x - - aeq' c x y = x == y - - match' c x y | x == y = Just empty - match' c x y | mode c == Term = - Just $ single (AnyName x) (AnyName y) - match' c _ _ | mode c == Pat = Just empty - - freshen' c nm = case mode c of - Term -> do x <- fresh nm - return (x, single (AnyName nm) (AnyName x)) - Pat -> return (nm, empty) - - lfreshen' c nm f = case mode c of - Term -> do x <- lfresh nm - avoid [AnyName x] $ - f x (single (AnyName nm) (AnyName x)) - Pat -> f nm empty - - -instance Alpha AnyName where - - fv' Term n = S.singleton n - fv' Pat n = S.empty - - binders' Term n = [n] - binders' Pat n = [] - - swapall' c p x = apply p x - - swaps' Term p x = apply p x - swaps' Pat perm x = x - - aeq' c x y = x == y - - match' Term x y = if x == y then Just empty else Just (single x y) - match' Pat x y = Just empty - - freshen' Term (AnyName nm) = do { x <- fresh nm; return(AnyName x, - (single (AnyName nm) (AnyName x))) } - freshen' Pat nm = return (nm, empty) - - lfreshen' c (AnyName nm) f = case mode c of - Term -> do { x <- lfresh nm; avoid [AnyName x] $ f (AnyName x) - (single (AnyName nm) (AnyName x)) } - Pat -> f (AnyName nm) empty - -instance (Alpha a, Alpha b) => Alpha (Bind a b) where - -- default definition of swapall - - -- to swap in a binder, swap the free variables in the - -- pattern, then remove the binders from the permutation - -- and swap in the body - -- ? why don't we just swap everywhere? we need to be - -- able to freshen patterns with annotations - swaps' p pm (B x y) = - B (swaps' (pat p) pm x) (swaps' p pm' y) where - pm' = restrict pm (binders x) - - -- free variables of a binder are the free variables in - -- the annotations in the pattern plus the free variables - -- of the body, minus the binders. - fv' p (B x y) = fv' Pat x `S.union` (fv' p y S.\\ fv' Term x) - - binders' p (B x y) = binders' Pat x ++ - (binders' p y List.\\ binders' Term x) - - -- default definition of freshen' -{- - freshen' p (B x y) = do --- (x', p1) <- freshen' (all p) x -- freshen the binders & annots - (y', p3) <- freshen' p (swaps' p p1 y) -- freshen body - return (B x' y', p1 <> p3) --} - - lfreshen' c (B x y) f = - avoid (S.elems $ fv' c x) $ - lfreshen' (pat c) x (\ x' pm1 -> - lfreshen' c (swaps' c pm1 y) (\ y' pm2 -> - f (B x' y') (pm1 <> pm2))) - - -- this version of aeq seems to work - aeq' p (B x1 y1) (B x2 y2) - -- if the binders match, compare the patterns & bodies - | bx1 == bx2 = aeq' p x1 x2 && aeq' p y1 y2 - -- if any binders of the first appear freely in the - -- second body, not aeq - | (S.fromList bx1) `S.intersection` - (fv' Term y2 S.\\ fv' Term y1) /= S.empty = False - -- otherwise swap the binding variables in the pattern - -- and *all* of the variables in the body - | True = aeq' p x1 (swaps' Term pm x2) && - aeq' p y1 (swapall' Term pm y2) - where bx1 = binders' Term x1 - bx2 = binders' Term x2 - pm = foldl (<>) empty (zipWith single bx1 bx2) - - - -- basic idea of match - -- if binders x1 == binders x2 then - --- match the annots in x1 and x2 and match the bodies y1 y2 - -- if binders x1 /= binders x2 then - -- make sure binders of x1 are not free in the body of y2 - -- swap (x1,x2) in y2 - -- match the annots & match the bodies - -- make sure none of the binders escapes in the resulting match - -- ingredients: - -- match the binders, ignoring the annots - -- match the annots, ignoring the binders - -- list the binding variables - match' p (B x1 y1) (B x2 y2) - | bx1 == bx2 = do - pm1 <- match' Pat x1 x2 - pm2 <- match' p y1 y2 - pm1 `join` pm2 - | (S.fromList bx1) `S.intersection` - (fv' Term y2 S.\\ fv' Term y1) - /= S.empty = Nothing - | True = do - -- make sure that we can match up the binders - -- this will fail if there are repeated vars - pm <- Monad.foldM join empty (zipWith single bx1 bx2) - let x2' = swaps' initial pm x2 - let y2' = swapall' initial pm y2 - pm1 <- match' Pat x1 x2' - pm2 <- match' p y1 y2' - -- note pm2 should not permute binders - -- (see a16) Dropping would give us "set binding" - if S.fromList bx1 `S.intersection` - S.fromList (support pm2) /= S.empty - then Nothing - else pm1 `join` pm2 - where bx1 = binders' Term x1 - bx2 = binders' Term x2 - -instance (Alpha a, Alpha b) => Alpha (Rebind a b) where - - -- free variables of the external binder - -- plus free vars of the annots in the binder - -- plus free vars of the body minus any - -- binding vars of internal binder - fv' p (R x (B ns y)) = fv' p x `S.union` - (fv' p y S.\\ S.fromList ns) - - binders' p (R x (B ns y)) = binders' p x ++ - (binders' p y List.\\ ns) - - - swaps' Term pm (R x (B ns y)) = - R (swaps' Term pm x) (B ns (swaps' Term pm' y)) where - pm' = restrict pm ns - - match' p (R x1 (B n1 y1)) (R x2 (B n2 y2)) = do - px <- match' p x1 x2 -- external names - pb <- match' p (B n1 y1) (B n2 y2) - px `join` pb - - freshen' p (R x (B x1 y)) = do - (x', pm1) <- freshen' p x - (y', pm2) <- freshen' p (swaps' p pm1 y) - return (R x (B x1 y'), pm1 <> pm2) - - -instance (Eq a, Alpha a) => Alpha (Embed a) where - - swaps' Pat pm (Embed t) = Embed (swaps' Term pm t) - swaps' Term pm (Embed t) = Embed t - - fv' Pat (Embed t) = fv' Term t - fv' Term _ = S.empty - - binders' Pat (Embed t) = binders' Term t - binders' Term _ = [] - - - freshen' Pat (Embed t) = do - (t', p) <- freshen' Term t - return (Embed t', p) - freshen' Term a = return (a, empty) - - match' Pat (Embed x) (Embed y) = match' Term x y - match' Term (Embed x) (Embed y) = if x `aeq` y - then Just empty - else Nothing - --- Instances for other types (mostly) use the default definitions. -instance Alpha Bool where -instance Alpha Float where -instance Alpha () where -instance Alpha a => Alpha [a] where -instance Alpha Int where -instance Alpha Integer where -instance Alpha Double where -instance Alpha Char where -instance Alpha a => Alpha (Maybe a) where -instance (Alpha a,Alpha b) => Alpha (Either a b) where -instance (Alpha a,Alpha b) => Alpha (a,b) where -instance (Alpha a,Alpha b,Alpha c) => Alpha (a,b,c) where -instance (Alpha a, Alpha b,Alpha c, Alpha d) => Alpha (a,b,c,d) -instance (Alpha a, Alpha b,Alpha c, Alpha d, Alpha e) => - Alpha (a,b,c,d,e) -instance (Rep a) => Alpha (R a) where - - --- Definitions of the class members for abstract types. -{- -abs_swaps' :: Alpha a => AlphaCtx -> Perm Name -> a -> a -abs_swaps' _ p s = s -abs_fv' :: Alpha a => AlphaCtx -> a -> [Name] -abs_fv' _ s = [] -abs_freshen' :: (Fresh m, Alpha a) => AlphaCtx -> a -> m (a, Perm Name) -abs_freshen' _ b = return (b, empty) -abs_match' :: (Eq a, Alpha a) => AlphaCtx -> a -> a -> Maybe (Perm Name) -abs_match' _ x1 x2 = if x1 == x2 then Just empty else Nothing --} - -------------------------------------------------------- --- | A monad "m" supports the nextInteger operation if it --- can generate new fresh integers - -class Monad m => HasNext m where - nextInteger :: m Integer - resetNext :: Integer -> m () - --- | A monad "m" supports the "fresh" operation if it --- can generate a new unique names. - -class (Monad m, HasNext m) => Fresh m where - fresh :: Name a -> m (Name a) - fresh (Nm r (s,j)) = do { i <- nextInteger; return (Nm r (s,i)) } - -instance HasNext m => Fresh m where - fresh (Nm r (s,j)) = do { n <- nextInteger; return (Nm r (s,n)) } - --- | Unbind is the destructor of a binding. It ensures that --- the names in the binding b are fresh. -unbind :: (Alpha b,Fresh m,Alpha c) => Bind b c -> m (b,c) -unbind (B x y) = do - (x',perm) <- freshen x - return(x', swaps perm y) - --- | Destruct two bindings simultanously, if they match, using the --- same list of fresh names -unbind2 :: (Fresh m,Alpha b,Alpha c, Alpha d) => - Bind b c -> Bind b d -> m (Maybe (b,c,d)) -unbind2 (B x1 y1) (B x2 y2) = do - (x1', perm1) <- freshen x1 - case match x1' x2 of - (Just perm2) -> - return $ Just (x1', swaps perm1 y1, swaps perm2 y2) - Nothing -> return Nothing - -unbind3 :: (Fresh m,Alpha b,Alpha c, Alpha d, Alpha e) => - Bind b c -> Bind b d -> Bind b e -> m (Maybe (b,c,d,e)) -unbind3 (B x1 y1) (B x2 y2) (B x3 y3) = do - (x1', perm1) <- freshen x1 - case (match x1' x2, match x1' x3) of - (Just perm2, Just perm3) -> - return $ Just (x1', swaps perm1 y1, swaps perm2 y2, swaps perm3 y3) - _ -> return Nothing - ------------------------------------------------------------------ --- | Locally fresh monad --- This is the class of --- monads that support freshness in an (implicit) local scope. Names --- drawn are fresh for this particular scope, but not globally fresh. --- This class has a basic instance based on the reader monad. -class Monad m => LFresh m where - -- | pick a new name that is fresh for the current (implicit) scope. - lfresh :: Rep a => Name a -> m (Name a) - -- | avoid these names when freshening in the subcomputation. - avoid :: [AnyName] -> m a -> m a - --- | Reader monad instance for local freshness class. -instance LFresh (Reader Integer) where - lfresh (Nm r (s,j)) = do { n <- ask; return (Nm r (s, max j (n+1))) } - avoid [] = id - avoid names = local (max k) where - k = maximum (map anyName2Integer names) - --- | Destruct a binding in the LFresh monad. -lunbind :: (LFresh m, Alpha a, Alpha b) => Bind a b -> m (a, b) -lunbind (B a b) = - avoid (S.elems $ fv' initial b) $ error "UNIMP" - - -lunbind2 :: (LFresh m, Alpha b, Alpha c, Alpha d) => - Bind b c -> Bind b d -> m (Maybe (b,c,d)) -lunbind2 (B b1 c) (B b2 d) = do - case match b1 b2 of - Just _ -> do - (b', c') <- lunbind (B b1 c) - return $ error "UNIMP" - Nothing -> return Nothing - -lunbind3 :: (LFresh m, Alpha b, Alpha c, Alpha d, Alpha e) => - Bind b c -> Bind b d -> Bind b e -> m (Maybe (b,c,d,e)) -lunbind3 (B b1 c) (B b2 d) (B b3 e) = do - case (match b1 b2, match b1 b3) of - (Just _, Just _) -> do - (b', c') <- lunbind (B b1 c) - return $ error "UNIMP" - _ -> return Nothing - - ---------------------------------------------------------------- - --- | Capture-avoiding substitution, in a monad so that we can rename --- variables at binding locations and avoid capture - -subst :: (Alpha a, Alpha b, Subst b a) => Name b -> b -> a -> a -subst n u x = - runReader (avoid ([AnyName n] ++ (S.elems $ fv' initial u)++(S.elems $ fv' initial x)) $ lsubst n u x) (0 :: Integer) - -substs :: (Alpha a, Alpha b, Subst b a) => [Name b] -> [b] -> a -> a -substs ns us x = - runReader (avoid ((map AnyName ns) ++ (concatMap (S.elems . (fv' initial)) us)++(S.elems $ fv' initial x)) $ lsubsts ns us x) - (0 :: Integer) - - -class (Rep1 (SubstD b) a) => Subst b a where - isvar :: a -> Maybe (Name b, b -> a) - isvar x = Nothing - - lsubst :: LFresh m => Name b -> b -> a -> m a - lsubst n u x = - case isvar x of - Just (m, f) | m == n -> return (f u) - Just (_, _) -> return x - Nothing -> substR1 rep1 n u x - - - lsubsts :: LFresh m => [Name b] -> [b] -> a -> m a - lsubsts ns us x = - case isvar x of - Just (m, f) -> case m `List.elemIndex` ns of - Just i -> return (f (us !! i)) - Nothing -> return x - Nothing -> substsR1 rep1 ns us x - - -data SubstD b a = SubstD { - substD :: LFresh m => Name b -> b -> a -> m a - ,substsD :: LFresh m => [Name b] -> [b] -> a -> m a -} - -instance Subst b a => Sat (SubstD b a) where - dict = SubstD lsubst lsubsts - -substR1 :: LFresh m => R1 (SubstD b) a -> Name b -> b -> a -> m a -substR1 (Data1 _ cons) = \ x y d -> - case (findCon cons d) of - Val c rec kids -> do - w <- mapM_l (\z -> substD z x y) rec kids - return (to c w) -substR1 _ = \ _ _ c -> return c - -substsR1 :: LFresh m => R1 (SubstD b) a -> [Name b] -> [b] -> a -> m a -substsR1 (Data1 _ cons) = \ x y d -> - case (findCon cons d) of - Val c rec kids -> do - z <- mapM_l (\ w -> substsD w x y) rec kids - return (to c z) -substsR1 _ = \ _ _ c -> return c - -instance Subst c AnyName where - -instance Subst b Int where -instance Subst b Bool where -instance Subst b () where -instance Subst b Char where -instance Subst b Integer where -instance Subst b Float where -instance Subst b Double where -instance (Subst c a, Subst c b) => Subst c (a,b) where -instance (Subst c a, Subst c b, Subst c d) => Subst c (a,b,d) where -instance (Subst c a, Subst c b, Subst c d, Subst c e) => Subst c (a,b,d,e) where -instance (Subst c a, Subst c b, Subst c d, Subst c e, Subst c f) => Subst c (a,b,d,e,f) where - -instance (Subst c a) => Subst c [a] where -instance (Subst c a) => Subst c (Maybe a) where -instance (Subst c a, Subst c b) => Subst c (Either a b) where - -instance Rep a => Subst b (R a) where -instance Rep a => Subst b (Name a) where - - -instance (Subst c a, Alpha a, Subst c b, Alpha b) => - Subst c (Bind a b) where - lsubst n u (B a b) = - lfreshen' Term a ( \ a' p -> do - let b' = swapall' Term p b - a'' <- lsubst n u a' - b'' <- lsubst n u b' - return (B a'' b'')) - - lsubsts n u (B a b) = - lfreshen' Term a ( \ a' p -> do - a'' <- lsubsts n u a' - let b' = swaps' Pat p (swaps' Term p b) - b'' <- lsubsts n u b' - return (B a'' b'')) - -instance (Subst c b, Subst c a, Alpha a, Alpha b) => - Subst c (Rebind a b) where -instance (Subst c a) => Subst c (Embed a) where - - --------------------- TESTING CODE -------------------------------- -data Exp = V (Name Exp) - | A Exp Exp - | L (Bind (Name Exp) Exp) deriving (Show) - -$(derive [''Exp]) - -instance Alpha Exp -instance Subst Exp Exp where - isvar (V n) = Just (n, id) - isvar _ = Nothing - --- deriving instance Eq Exp --- deriving instance Ord Exp - -nameA, nameB, nameC :: Name Exp -nameA = string2Name "A" -nameB = string2Name "B" -nameC = string2Name "C" - -assert :: String -> Bool -> IO () -assert s True = return () -assert s False = print ("Assertion " ++ s ++ " failed") - -do_tests :: () -do_tests = - unsafePerformIO $ do - tests_aeq - tests_fv - tests_big - tests_subst - -perm = single (AnyName nameA)(AnyName nameB) - -naeq x y = not (aeq x y) - -a10a = bind (rebind (nameA, Embed nameC) ()) nameA -a10b = bind (rebind (nameB, Embed nameC) ()) nameB - -a10c = bind (rebind (nameA, Embed nameA) ()) nameA -a10d = bind (rebind (nameB, Embed nameA) ()) nameB - -tests_aeq = do - assert "a1" $ (bind nameA nameA) `naeq` (bind nameA nameB) - assert "a2" $ (bind nameA nameA) `aeq` (bind nameA nameA) - assert "a3" $ (bind nameA nameA) `aeq` (bind nameB nameB) - assert "a4" $ (bind nameA nameB) `naeq` (bind nameB nameA) - assert "a5" $ (bind (nameA, Embed nameB) nameA) `naeq` - (bind (nameA, Embed nameC) nameA) - assert "a6" $ (bind (nameA, Embed nameB) nameA) `aeq` - (bind (nameA, Embed nameB) nameA) - assert "a7" $ (bind (nameA, Embed nameB) nameA) `aeq` - (bind (nameB, Embed nameB) nameB) - assert "a8" $ rebind nameA nameB `naeq` rebind nameB nameB - assert "a9" $ rebind nameA nameA `naeq` rebind nameB nameB - assert "a9a" $ (bind (rebind nameA (Embed nameA)) nameA) `aeq` - (bind (rebind nameB (Embed nameB)) nameB) - assert "a10" $ bind (rebind (nameA, Embed nameA) ()) nameA `aeq` - bind (rebind (nameB, Embed nameA) ()) nameB - assert "a10a" $ a10a `aeq` a10b - assert "a11" $ bind (rebind (nameA, Embed nameA) ()) nameA `naeq` - bind (rebind (nameB, Embed nameB) ()) nameB - assert "a12" $ bind (Embed nameA) () `naeq` bind (Embed nameB) () - assert "a13" $ bind (Embed nameA) () `aeq` bind (Embed nameA) () - assert "a14" $ bind (rebind (Embed nameA) ()) () `naeq` - bind (rebind (Embed nameB) ()) () - assert "a15" $ (rebind (nameA, Embed nameA) ()) `naeq` - (rebind (name4, Embed nameC) ()) - assert "a16" $ bind (nameA, nameB) nameA `naeq` - bind (nameB, nameA) nameA - assert "a17" $ bind (nameA, nameB) nameA `naeq` bind (nameA, nameB) nameB - assert "a18" $ (nameA, nameA) `naeq` (nameA, nameB) - assert "a19" $ match (nameA, nameA) (nameB, nameC) == Nothing - assert "a20" $ (L (bind name2 (L (bind name3 (L (bind name4 (A (V name2) (A (V name3) (V name4))))))))) `aeq` (L (bind name1 (L (bind name2 (L (bind name3 (A (V name1) (A (V name2) (V name3))))))))) - -emptyNE :: Set (Name Exp) -emptyNE = S.empty - -tests_fv = do - assert "f1" $ fv (bind nameA nameA) == emptyNE - assert "f2" $ fv' Pat (bind nameA nameA) == S.empty - assert "f3" $ fv (bind (rebind nameA (Embed nameA)) nameA) == emptyNE - assert "f4" $ fv (bind nameA nameB) == S.singleton nameB - assert "f5" $ fv (bind (nameA, Embed nameB) nameA) == S.singleton nameB - assert "f7" $ fv (bind (nameB, Embed nameB) nameB) == S.singleton nameB - assert "f8" $ fv (rebind nameA nameB) == S.fromList [nameA, nameB] - assert "f9" $ fv' Pat (rebind nameA nameA) == S.empty - assert "f9a" $ fv (rebind nameA (Embed nameA)) == S.singleton nameA - assert "f9b" $ fv' Pat (rebind nameA (Embed nameA)) == S.empty - assert "f10" $ fv (rebind (nameA, Embed nameA) ()) == S.singleton nameA - assert "f11" $ fv' Pat (rebind (nameA, Embed nameA) ()) == S.singleton (AnyName nameA) - assert "f10a" $ fv (rebind (nameA, Embed nameB) ()) == S.singleton nameA - assert "f11a" $ fv' Pat (rebind (nameA, Embed nameB) ()) == S.singleton (AnyName nameB) - - - assert "f12" $ fv (bind (Embed nameA) ()) == S.singleton nameA - assert "f12a" $ fv' Pat (bind (Embed nameA) ()) == S.singleton (AnyName nameA) - - assert "f14" $ fv (rebind (Embed nameA) ()) == emptyNE - assert "f14a" $ fv' Pat (rebind (Embed nameA) ()) == S.singleton (AnyName nameA) - -tests_subst = do - assert "s1" $ subst nameA (V nameB) (V nameA) `aeq` (V nameB) - assert "s2" $ subst nameA (V nameB) (V nameC) `aeq` (V nameC) - assert "s3" $ subst nameA (V nameB) (L (bind nameA (V nameA))) `aeq` - (L (bind nameA (V nameA))) - - assert "s4" $ subst nameA (V nameB) (L (bind nameB (V nameB))) `aeq` - (L (bind nameA (V nameA))) - - assert "s5" $ subst nameA (V nameB) (L (bind nameC (V nameA))) `aeq` - (L (bind nameC (V nameB))) - - assert "s6" $ subst nameA (V nameA) (L (bind nameC (V nameA))) `aeq` - (L (bind nameC (V nameA))) - - assert "s7" $ subst nameA (V nameA) (L (bind nameA (V nameB))) `aeq` - (L (bind nameA (V nameB))) - assert "s9" $ subst name1 (V name1) - (L (bind name1 (L (bind name2 (L (bind name3 - (A (V name1) (A (V name2) (V name3))))))))) `aeq` - (L (bind name1 (L (bind name2 (L (bind name3 - (A (V name1) (A (V name2) (V name3))))))))) - - -mkbig :: [Name Exp] -> Exp -> Exp -mkbig (n : names) body = - L (bind n (mkbig names (A (V n) body))) -mkbig [] body = body - -big1 = mkbig (map integer2Name (take 100 [1 ..])) (V name11) -big2 = mkbig (map integer2Name (take 101 [1 ..])) (V name11) - -tests_big = do - assert "b1" $ big1 `naeq` big2 - assert "b2" $ fv big1 == emptyNE - assert "b3" $ big1 `aeq` subst name11 (V name11) big1 - -
− Unbound/Nominal/Name.hs
@@ -1,130 +0,0 @@-{-# LANGUAGE RankNTypes - , TemplateHaskell - , GADTs - , FlexibleInstances - , MultiParamTypeClasses - #-} ----------------------------------------------------------------------- --- | --- Module : Unbound.Nominal.Name --- License : BSD-like (see LICENSE) --- --- Maintainer : Stephanie Weirich <sweirich@cis.upenn.edu> --- Stability : experimental --- Portability : XXX --- --- XXX write me ----------------------------------------------------------------------- - -module Unbound.Nominal.Name where --- XXX todo make explicit export list - -import Generics.RepLib - --- | 'Name's are things that get bound. This type is intentionally --- abstract; to create a 'Name' you can use 'string2Name' or --- 'integer2Name'. The type parameter is a tag, or /sort/, which tells --- us what sorts of things this name may stand for. The sort must --- be an instance of the 'Rep' type class. -data Name a - = Nm (R a) (String, Integer) -- free names - deriving (Eq, Ord) - --- | A name with a hidden (existentially quantified) sort. -data AnyName = forall a. Rep a => AnyName (Name a) - --- AnyName has an existential in it, so we cannot create a complete --- representation for it, unfortunately. - -$(derive_abstract [''AnyName]) - -instance Show AnyName where - show (AnyName n1) = show n1 - -instance Eq AnyName where - (AnyName n1) == (AnyName n2) = - case gcastR (getR n1) (getR n2) n1 of - Just n1' -> n1' == n2 - Nothing -> False - -instance Ord AnyName where - compare (AnyName n1) (AnyName n2) = - case compareR (getR n1) (getR n2) of - EQ -> case gcastR (getR n1) (getR n2) n1 of - Just n1' -> compare n1' n2 - Nothing -> error "Panic: equal types are not equal in Ord AnyName instance!" - ord -> ord - ------------------------------------------------------------- --- Utilities ------------------------------------------------------------- - --- some convenient names for testing -name1, name2, name3, name4, name5, name6, name7, name8, name9, name10, name11 - :: Rep a => Name a -name1 = integer2Name 1 -name2 = integer2Name 2 -name3 = integer2Name 3 -name4 = integer2Name 4 -name5 = integer2Name 5 -name6 = integer2Name 6 -name7 = integer2Name 7 -name8 = integer2Name 8 -name9 = integer2Name 9 -name10 = integer2Name 10 -name11 = integer2Name 11 - ---instance Read Name where --- read s = error "FIXME" - -instance Show (Name a) where - show (Nm _ ("",n)) = "_" ++ (show n) - show (Nm _ (x,0)) = x - show (Nm _ (x,n)) = x ++ (show n) - --- | Get the integer index of a 'Name'. -name2Integer :: Name a -> Integer -name2Integer (Nm _ (_,x)) = x - - --- | Get the string part of a 'Name'. -name2String :: Name a -> String -name2String (Nm _ (s,_)) = s - - --- | Get the integer index of an 'AnyName'. -anyName2Integer :: AnyName -> Integer -anyName2Integer (AnyName nm) = name2Integer nm - --- | Get the string part of an 'AnyName'. -anyName2String :: AnyName -> String -anyName2String (AnyName nm) = name2String nm - -toSortedName :: Rep a => AnyName -> Maybe (Name a) -toSortedName (AnyName n) = gcastR (getR n) rep n - --- | Create a 'Name' from an 'Integer'. -integer2Name :: Rep a => Integer -> Name a -integer2Name n = makeName "" n - --- | Create a 'Name' from a 'String'. -string2Name :: Rep a => String -> Name a -string2Name s = makeName s 0 - --- | Convenient synonym for 'string2Name'. -s2n :: Rep a => String -> Name a -s2n = string2Name - --- | Create a 'Name' from a @String@ and an @Integer@ index. -makeName :: Rep a => String -> Integer -> Name a -makeName s i = Nm rep (s,i) - --- | Determine the sort of a 'Name'. -getR :: Name a -> R a -getR (Nm r _) = r - - --- | Change the sort of a name -translate :: (Rep b) => Name a -> Name b -translate (Nm _ x) = Nm rep x -
Unbound/PermM.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE PatternGuards #-} ---------------------------------------------------------------------- -- | -- Module : Unbound.Perm@@ -10,17 +11,17 @@ ---------------------------------------------------------------------- module Unbound.PermM (- Perm, single, compose, apply, support, isid, join, empty, restrict, mkPerm+ Perm(..), permValid, single, compose, apply, support, isid, join, empty, restrict, mkPerm ) where import Data.Monoid import Data.List import Data.Map (Map)-import qualified Data.Map as Map-import System.IO.Unsafe--(<>) :: Monoid m => m -> m -> m-(<>) = mappend+import Data.Function (on)+import qualified Data.Map as M+import qualified Data.Set as S+import Control.Arrow ((&&&))+import Control.Monad ((>=>)) -- | A /permutation/ is a bijective function from names to names -- which is the identity on all but a finite set of names. They@@ -28,33 +29,39 @@ -- also be useful in general. newtype Perm a = Perm (Map a a) +permValid :: Ord a => Perm a -> Bool+permValid (Perm p) = all (\(_,v) -> M.member v p) (M.assocs p)+ -- a Map sends every key uniquely to a value by construction. So if+ -- every value is also a key, the sizes of the domain and range must+ -- be equal and hence the mapping is a bijection.+ instance Ord a => Eq (Perm a) where (Perm p1) == (Perm p2) =- all (\x -> Map.findWithDefault x x p1 == Map.findWithDefault x x p2) (Map.keys p1) &&- all (\x -> Map.findWithDefault x x p1 == Map.findWithDefault x x p2) (Map.keys p2)+ all (\x -> M.findWithDefault x x p1 == M.findWithDefault x x p2) (M.keys p1) &&+ all (\x -> M.findWithDefault x x p1 == M.findWithDefault x x p2) (M.keys p2) instance Show a => Show (Perm a) where show (Perm p) = show p -- | Apply a permutation to an element of the domain. apply :: Ord a => Perm a -> a -> a-apply (Perm p) x = Map.findWithDefault x x p+apply (Perm p) x = M.findWithDefault x x p -- | Create a permutation which swaps two elements. single :: Ord a => a -> a -> Perm a-single x y = if x == y then Perm Map.empty else- Perm (Map.insert x y (Map.insert y x Map.empty))+single x y = if x == y then Perm M.empty else+ Perm (M.insert x y (M.insert y x M.empty)) -- | The empty (identity) permutation. empty :: Perm a-empty = Perm Map.empty+empty = Perm M.empty -- | Compose two permutations. The right-hand permutation will be -- applied first. compose :: Ord a => Perm a -> Perm a -> Perm a compose (Perm b) (Perm a) =- Perm (Map.fromList ([ (x,Map.findWithDefault y y b) | (x,y) <- Map.toList a]- ++ [ (x, Map.findWithDefault x x b) | x <- Map.keys b, Map.notMember x a]))+ Perm (M.fromList ([ (x,M.findWithDefault y y b) | (x,y) <- M.toList a]+ ++ [ (x, M.findWithDefault x x b) | x <- M.keys b, M.notMember x a])) -- | Permutations form a monoid under composition. instance Ord a => Monoid (Perm a) where@@ -64,79 +71,61 @@ -- | Is this the identity permutation? isid :: Ord a => Perm a -> Bool isid (Perm p) =- Map.foldrWithKey (\ a b r -> r && a == b) True p+ M.foldrWithKey (\ a b r -> r && a == b) True p -- | /Join/ two permutations by taking the union of their relation -- graphs. Fail if they are inconsistent, i.e. map the same element -- to two different elements. join :: Ord a => Perm a -> Perm a -> Maybe (Perm a) join (Perm p1) (Perm p2) =- let overlap = Map.intersectionWith (==) p1 p2 in- if Map.fold (&&) True overlap then- Just (Perm (Map.union p1 p2))+ let overlap = M.intersectionWith (==) p1 p2 in+ if M.fold (&&) True overlap then+ Just (Perm (M.union p1 p2)) else Nothing -- | The /support/ of a permutation is the set of elements which are -- not fixed. support :: Ord a => Perm a -> [a]-support (Perm p) = [ x | x <- Map.keys p, Map.findWithDefault x x p /= x]+support (Perm p) = [ x | x <- M.keys p, M.findWithDefault x x p /= x] -- | Restrict a permutation to a certain domain. restrict :: Ord a => Perm a -> [a] -> Perm a-restrict (Perm p) l = Perm (foldl' (\p' k -> Map.delete k p') p l)+restrict (Perm p) l = Perm (foldl' (\p' k -> M.delete k p') p l) +-- | A partial permutation consists of two maps, one in each direction+-- (inputs -> outputs and outputs -> inputs).+data PartialPerm a = PP (M.Map a a) (M.Map a a)+ deriving Show++emptyPP :: PartialPerm a+emptyPP = PP M.empty M.empty++extendPP :: Ord a => a -> a -> PartialPerm a -> Maybe (PartialPerm a)+extendPP x y pp@(PP mfwd mrev)+ | Just y' <- M.lookup x mfwd = if y == y' then Just pp+ else Nothing+ | Just x' <- M.lookup y mrev = if x == x' then Just pp+ else Nothing+ | otherwise = Just $ PP (M.insert x y mfwd) (M.insert y x mrev)++-- | Convert a partial permutation into a full permutation by closing+-- off any remaining open chains into a cycles.+ppToPerm :: Ord a => PartialPerm a -> Perm a+ppToPerm (PP mfwd mrev) = Perm $ foldr (uncurry M.insert) mfwd+ (map (findEnd &&& id) chainStarts)+ -- beginnings of open chains are elements which map to+ -- something in the forward direction but have no ancestor.+ where chainStarts = S.toList (M.keysSet mfwd `S.difference` M.keysSet mrev)+ findEnd x = case M.lookup x mfwd of+ Nothing -> x+ Just x' -> findEnd x'+ -- | @mkPerm l1 l2@ creates a permutation that sends @l1@ to @l2@. -- Fail if there is no such permutation, either because the lists -- have different lengths or because they are inconsistent (which -- can only happen if @l1@ or @l2@ have repeated elements). mkPerm :: Ord a => [a] -> [a] -> Maybe (Perm a) mkPerm xs ys- | length xs == length ys = foldl' (\mp p -> mp >>= join p)- (Just empty)- (zipWith single xs ys)- | otherwise = Nothing------------------------------------------------------------------------seteq :: Ord a => [a] -> [a] -> Bool-seteq x y = nub (sort x) == nub (sort y)---assert :: String -> Bool -> IO ()-assert s True = return ()-assert s False = print ("Assertion " ++ s ++ " failed")--do_tests :: ()-do_tests =- unsafePerformIO $ do- tests_apply- tests_isid- tests_support- tests_join--tests_join = do- assert "j1" $ join empty (empty :: Perm Int) == Just empty- assert "j2" $ join (single 1 2) empty == Just (single 1 2)- assert "j3" $ join (single 1 2) (single 2 1) == Just (single 1 2)- assert "j4" $ join (single 1 2) (single 1 3) == Nothing--tests_apply = do- assert "a1" $ apply empty 1 == 1- assert "a2" $ apply (single 1 2) 1 == 2- assert "a3" $ apply (single 2 1) 1 == 2- assert "a4" $ apply ((single 1 2) <> (single 2 1)) 1 == 1--tests_isid = do- assert "i1" $ isid (empty :: Perm Int) == True- assert "i2" $ isid (single 1 2) == False- assert "i3" $ isid (single 1 1) == True- assert "i4" $ isid ((single 1 2) <> (single 1 2)) == True- assert "i5" $ isid ((single 1 2) <> (single 2 1)) == True- assert "i6" $ isid ((single 1 2) <> (single 3 2)) == False--tests_support = do- assert "s1" $ support (empty :: Perm Int) `seteq` []- assert "s2" $ support (single 1 2) `seteq` [1,2]- assert "s3" $ support (single 1 1) `seteq` []- assert "s4" $ support ((single 1 2) <> (single 1 2)) `seteq` []- assert "s5" $ support ((single 1 2) <> (single 2 1)) `seteq` []- assert "s6" $ support ((single 1 2) <> (single 3 2)) `seteq` [1,2,3]+ | length xs /= length ys = Nothing+ | otherwise =+ fmap ppToPerm . ($emptyPP) . foldr (>=>) return $ zipWith extendPP xs ys
examples/Basic.hs view
@@ -147,11 +147,11 @@ -- -- finally, we define generalized versions of fold left and -- fold right for the Tree type constructor.-instance Fold Tree where- foldRight op = rreduceR1 (rTree1 (RreduceD { rreduceD = op })- (RreduceD { rreduceD = foldRight op}))- foldLeft op = lreduceR1 (rTree1 (LreduceD { lreduceD = op })- (LreduceD { lreduceD = foldLeft op }))+-- instance Fold Tree where+-- foldRight op = rreduceR1 (rTree1 ( RreduceD { rreduceD = op }+-- , RreduceD { rreduceD = foldRight op}))+-- foldLeft op = lreduceR1 (rTree1 ( LreduceD { lreduceD = op }+-- , LreduceD { lreduceD = foldLeft op })) assert :: String -> Bool -> IO () assert s True = return ()@@ -172,13 +172,13 @@ -- assert "s1" (subtrees t1 == [Node (Leaf 3) (Leaf 4),Node (Leaf 5) (Leaf 6)])- assert "s2" (gsum t1 == 18)- assert "s3" (gsum t2 == 27)+-- assert "s2" (gsum t1 == 18)+-- assert "s3" (gsum t2 == 27) -- assert "i1" (increase 0.1 s1 == C [D "Types" (M (E (P "Stephanie") (S 1100.0))) [PU (E (P "Michael") (S 55.0)),PU (E (P "Samuel") (S 55.0)),PU (E (P "Theodore") (S 55.0))],D "Terms" (M (E (P "Stephanie") (S 220.0))) [DU (D "Shipping" (M (E (P "Alice") (S 3300.0))) [])]]) assert "i2" (s1 < (increase 0.2 s1)) --- assert "f1" (gproduct t1 == 360)- assert "f2" (count t1 == 4)+-- assert "f1" (gproduct t1 == 360)+-- assert "f2" (count t1 == 4)
+ test/PermTest.hs view
@@ -0,0 +1,93 @@+import Unbound.PermM++import Data.Maybe (isJust)+import Data.Monoid+import Data.List (nub, sort)+import qualified Data.Map as M+import System.IO.Unsafe++import Test.QuickCheck++instance (Ord a, Arbitrary a) => Arbitrary (Perm a) where+ arbitrary = (mconcat . map (uncurry single)) `fmap` arbitrary++---------------------------------------------------------------------+(<>) :: Monoid m => m -> m -> m+(<>) = mappend++seteq :: Ord a => [a] -> [a] -> Bool+seteq x y = nub (sort x) == nub (sort y)+++assert :: String -> Bool -> IO ()+assert s True = return ()+assert s False = print ("Assertion " ++ s ++ " failed")++do_tests :: ()+do_tests =+ unsafePerformIO $ do+ tests_apply+ tests_isid+ tests_support+ tests_join++tests_join = do+ assert "j1" $ join empty (empty :: Perm Int) == Just empty+ assert "j2" $ join (single 1 2) empty == Just (single 1 2)+ assert "j3" $ join (single 1 2) (single 2 1) == Just (single 1 2)+ assert "j4" $ join (single 1 2) (single 1 3) == Nothing++tests_apply = do+ assert "a1" $ apply empty 1 == 1+ assert "a2" $ apply (single 1 2) 1 == 2+ assert "a3" $ apply (single 2 1) 1 == 2+ assert "a4" $ apply ((single 1 2) <> (single 2 1)) 1 == 1++tests_isid = do+ assert "i1" $ isid (empty :: Perm Int) == True+ assert "i2" $ isid (single 1 2) == False+ assert "i3" $ isid (single 1 1) == True+ assert "i4" $ isid ((single 1 2) <> (single 1 2)) == True+ assert "i5" $ isid ((single 1 2) <> (single 2 1)) == True+ assert "i6" $ isid ((single 1 2) <> (single 3 2)) == False++prop_isid :: Perm Int -> Bool+prop_isid p = isid p == (all (\x -> apply p x == x) $ support p)++tests_support = do+ assert "s1" $ support (empty :: Perm Int) `seteq` []+ assert "s2" $ support (single 1 2) `seteq` [1,2]+ assert "s3" $ support (single 1 1) `seteq` []+ assert "s4" $ support ((single 1 2) <> (single 1 2)) `seteq` []+ assert "s5" $ support ((single 1 2) <> (single 2 1)) `seteq` []+ assert "s6" $ support ((single 1 2) <> (single 3 2)) `seteq` [1,2,3]++prop_support :: Perm Int -> Int -> Bool+prop_support p i = (apply p i /= i) == (i `elem` support p)++------------------------------------------------------------++prop_support_empty :: Bool+prop_support_empty = support (empty :: Perm Int) == []++prop_compose_empty_l :: Perm Int -> Int -> Bool+prop_compose_empty_l p i = apply p i == apply (compose empty p) i++prop_compose_empty_r :: Perm Int -> Int -> Bool+prop_compose_empty_r p i = apply p i == apply (compose p empty) i++prop_compose :: Perm Int -> Perm Int -> Int -> Bool+prop_compose p1 p2 i = apply (compose p1 p2) i == apply p1 (apply p2 i)++prop_mkPerm_perm :: [Int] -> [Int] -> Bool+prop_mkPerm_perm xs ys | Just p <- mkPerm xs ys = map (apply p) xs == ys+ | otherwise = True++prop_mkPerm_fail :: [Int] -> [Int] -> Property+prop_mkPerm_fail xs ys = length xs == length ys && nub xs == xs && nub ys == ys ==> isJust (mkPerm xs ys)++prop_mkPerm_valid :: [Int] -> [Int] -> Bool+prop_mkPerm_valid xs ys = maybe True permValid (mkPerm xs ys)++prop_valid :: Perm Int -> Bool+prop_valid = permValid
+ test/Test.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE TemplateHaskell+ , MultiParamTypeClasses+ , FlexibleInstances+ , FlexibleContexts+ , ScopedTypeVariables+ , UndecidableInstances+ #-}++module Unbound.LocallyNameless.Test where++import qualified Data.Set as S++import Unbound.LocallyNameless hiding (GT)+import Unbound.LocallyNameless.Alpha+import Unbound.LocallyNameless.Name+import Unbound.PermM++-------------------- TESTING CODE --------------------------------+data Exp = V (Name Exp)+ | A Exp Exp+ | L (Bind (Name Exp) Exp) deriving (Show)++$(derive [''Exp])++instance Alpha Exp+instance Subst Exp Exp where+ isvar (V n) = Just (SubstName n)+ isvar _ = Nothing++nameA, nameB, nameC :: Name Exp+nameA = integer2Name 1+nameB = integer2Name 2+nameC = integer2Name 3++assert :: String -> Bool -> IO ()+assert s True = return ()+assert s False = print ("Assertion " ++ s ++ " failed")++do_tests :: IO ()+do_tests = do+ tests_aeq+ tests_fv+ tests_big+ tests_nth+ tests_acompare++perm = single nameA nameB++naeq x y = not (aeq x y)++tests_aeq = do+ assert "a1" $ (bind nameA nameA) `naeq` (bind nameA nameB)+ assert "a2" $ (bind nameA nameA) `aeq` (bind nameA nameA)+ assert "a3" $ (bind nameA nameA) `aeq` (bind nameB nameB)+ assert "a4" $ (bind nameA nameB) `naeq` (bind nameB nameA)+ assert "a5" $ (bind (nameA, Embed nameB) nameA) `naeq`+ (bind (nameA, Embed nameC) nameA)+ assert "a6" $ (bind (nameA, Embed nameB) nameA) `aeq`+ (bind (nameA, Embed nameB) nameA)+ assert "a7" $ (bind (nameA, Embed nameB) nameA) `aeq`+ (bind (nameB, Embed nameB) nameB)+ assert "a8" $ rebind nameA nameB `naeq` rebind nameB nameB+ assert "a9" $ rebind nameA nameA `naeq` rebind nameB nameB+ assert "a9" $ (bind (rebind nameA (Embed nameA)) nameA) `aeq`+ (bind (rebind nameB (Embed nameB)) nameB)+ assert "a10" $ bind (rebind (nameA, Embed nameA) ()) nameA `aeq`+ bind (rebind (nameB, Embed nameA) ()) nameB+ assert "a11" $ bind (rebind (nameA, Embed nameA) ()) nameA `naeq`+ bind (rebind (nameB, Embed nameB) ()) nameB+ assert "a12" $ bind (Embed nameA) () `naeq` bind (Embed nameB) ()+ assert "a13" $ bind (Embed nameA) () `aeq` bind (Embed nameA) ()+ assert "a14" $ bind (rebind (Embed nameA) ()) () `naeq`+ bind (rebind (Embed nameB) ()) ()+ assert "a15" $ (rebind (nameA, Embed nameA) ()) `naeq`+ (rebind (nameA, Embed nameC) ())+ assert "a16" $ bind (nameA, nameB) nameA `naeq` bind (nameB, nameA) nameA+ assert "a17" $ bind (nameA, nameB) nameA `naeq` bind (nameA, nameB) nameB+ assert "a18" $ (nameA, nameA) `naeq` (nameA, nameB)+-- assert "a19" $ match (nameA, nameA) (nameB, nameC) == Nothing++emptyNE :: S.Set (Name Exp)+emptyNE = S.empty++tests_fv = do+ assert "f1" $ fv (bind nameA nameA) == emptyNE+ assert "f2" $ fv' (pat initial) (bind nameA nameA) == S.empty+ assert "f4" $ fv (bind nameA nameB) == S.singleton nameB+ assert "f5" $ fv (bind (nameA, Embed nameB) nameA) == S.singleton nameB+ assert "f7" $ fv (bind (nameB, Embed nameB) nameB) == S.singleton nameB+ assert "f8" $ fv (rebind nameA nameB) == S.fromList [nameA, nameB]+ assert "f9" $ fv' (pat initial) (rebind nameA nameA) == S.empty+ assert "f3" $ fv (bind (rebind nameA (Embed nameA)) nameA) == emptyNE+ assert "f10" $ fv (rebind (nameA, Embed nameA) ()) == S.singleton nameA+ assert "f11" $ fv' (pat initial) (rebind (nameA, Embed nameA) ()) == S.singleton (AnyName nameA)+ assert "f12" $ fv (bind (Embed nameA) ()) == S.singleton nameA+ assert "f14" $ fv (rebind (Embed nameA) ()) == emptyNE++mkbig :: [Name Exp] -> Exp -> Exp+mkbig (n : names) body =+ L (bind n (mkbig names (A (V n) body)))+mkbig [] body = body++big1 = mkbig (map integer2Name (take 100 [1 ..])) (V nameA)+big2 = mkbig (map integer2Name (take 101 [1 ..])) (V nameA)+++tests_nth = do+ assert "n1" $ nthpat ([nameA],nameB) 0 == AnyName nameA+ assert "n2" $ nthpat ([nameA],nameB) 1 == AnyName nameB+ assert "n3" $ nthpat (nameA, nameB) 0 == AnyName nameA+ assert "p1" $ findpat ([nameA],nameB) (AnyName nameA) == Just 0+ assert "p2" $ findpat ([nameA],nameB) (AnyName nameB) == Just 1+ assert "p3" $ findpat ([nameA],nameB) (AnyName nameC) == Nothing++tests_big = do+ assert "b1" $ big1 `naeq` big2+ assert "b2" $ fv big1 == emptyNE+ assert "b3" $ big1 `aeq` subst nameA (V nameA) big1++tests_acompare = do+ -- Names compare in the obvious way.+ assert "ac1" $ acompare nameA nameB == LT+ assert "ac2" $ acompare nameB nameB == EQ+ assert "ac3" $ acompare nameB nameA == GT+ -- structured date compares lexicographically+ assert "ac4" $ acompare (A (V nameA) (V nameA)) (A (V nameA) (V nameA)) == EQ+ assert "ac5" $ acompare (A (V nameA) (V nameA)) (A (V nameA) (V nameB)) == LT+ assert "ac6" $ acompare (A (V nameA) (V nameB)) (A (V nameA) (V nameA)) == GT+ assert "ac7" $ acompare (A (V nameA) (V nameA)) (A (V nameB) (V nameA)) == LT+ assert "ac8" $ acompare (A (V nameB) (V nameA)) (A (V nameA) (V nameA)) == GT+ assert "ac9" $ acompare (A (V nameB) (V nameA)) (A (V nameA) (V nameB)) == GT+ -- comparison goes under binders, alpha-respectingly.+ assert "ac10" $ acompare (bind nameA (A (V nameA) (V nameA))) (bind nameA (A (V nameA) (V nameA))) == EQ+ assert "ac11" $ acompare (bind nameA (A (V nameA) (V nameA))) (bind nameA (A (V nameA) (V nameB))) == GT+ assert "ac12" $ acompare (bind nameC (A (V nameC) (V nameA))) (bind nameA (A (V nameA) (V nameB))) == LT+ -- non-matching binders handled alpha-respectingly.+ assert "ac13" $ acompare (bind [nameA] nameA) (bind [nameA,nameB] nameA)+ == acompare (bind [nameC] nameC) (bind [nameA,nameB] nameA)+ assert "ac14" $ acompare (bind [nameA,nameB] nameA) (bind [nameA] nameA)+ == acompare (bind [nameC,nameB] nameC) (bind [nameA] nameA)+ -- non-binding stuff in patterns gets compared+ assert "ac15" $ acompare (Embed nameA) (Embed nameB) == LT+ assert "ac16" $ acompare (bind (nameC, Embed nameA) (A (V nameC) (V nameC)))+ (bind (nameC, Embed nameB) (A (V nameC) (V nameC))) == LT+ assert "ac17" $ acompare (bind (nameC, Embed nameA) (A (V nameB) (V nameB)))+ (bind (nameC, Embed nameB) (A (V nameA) (V nameA))) == LT+ -- TODO: do we need anything special for rebind? For AnyName?++-- properties+-- if match t1 t2 = Some p then swaps p t1 = t2++main :: IO ()+main = do_tests
unbound.cabal view
@@ -1,10 +1,10 @@ name: unbound-version: 0.2.3+version: 0.2.4 license: BSD3 license-file: LICENSE build-type: Simple cabal-version: >= 1.6-tested-with: GHC == 7.0.1+tested-with: GHC == 7.0.1, GHC == 7.0.3 author: Stephanie Weirich maintainer: Brent Yorgey <byorgey@cis.upenn.edu> Stephanie Weirich <sweirich@cis.upenn.edu>@@ -15,7 +15,7 @@ examples/*.hs, tutorial/Makefile, tutorial/Tutorial.lhs,- Unbound/LocallyNameless/Test.hs+ test/*.hs synopsis: Generic support for programming with names and binders description: Specify the binding structure of your data type with an@@ -24,6 +24,10 @@ alpha-equivalence, free variable calculation, capture-avoiding substitution, and more. See "Unbound.LocallyNameless" to get started.+source-repository head+ type: svn+ location: https://replib.googlecode.com/svn/trunk/+ Library build-depends: base >= 4.3 && < 5, RepLib >= 0.4.0,@@ -37,8 +41,5 @@ Unbound.LocallyNameless.Alpha, Unbound.LocallyNameless.Subst, Unbound.LocallyNameless.Ops,- Unbound.Nominal,- Unbound.Nominal.Name,- Unbound.Nominal.Internal, Unbound.PermM, Unbound.Util