Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

more doctests and documentation

  • Loading branch information...
commit c235b48e78fccabc3719d20ab513b68c917df191 1 parent 24ae1f9
Edward Kmett authored September 13, 2012
4  bound.cabal
@@ -21,6 +21,10 @@ description:
21 21
    An untyped lambda calculus:
22 22
    .
23 23
    > import Bound
  24
+   > import Control.Applicative
  25
+   > import Control.Monad (ap)
  26
+   > import Data.Foldable
  27
+   > import Data.Traversable
24 28
    > import Prelude.Extras
25 29
    .
26 30
    > infixl 9 :@
65  src/Bound.hs
@@ -9,40 +9,57 @@
9 9
 -- Portability :  portable
10 10
 --
11 11
 -- We represent the target language itself as an ideal monad supplied by the
12  
--- user, and provide a 'Scope' monad transformer for introducing bound 
  12
+-- user, and provide a 'Scope' monad transformer for introducing bound
13 13
 -- variables in user supplied terms. Users supply a 'Monad' and 'Traversable'
14 14
 -- instance, and we traverse to find free variables, and use the 'Monad' to
15 15
 -- perform substitution that avoids bound variables.
16 16
 --
17 17
 -- An untyped lambda calculus:
18 18
 --
19  
--- > import Bound
20  
--- > import Prelude.Extras
  19
+-- @
  20
+-- {-\# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable \#-}
  21
+-- import Bound
  22
+-- import Control.Applicative
  23
+-- import Control.Monad ('Control.Monad.ap')
  24
+-- import Prelude.Extras
  25
+-- import Data.Foldable
  26
+-- import Data.Traverable
  27
+-- @
21 28
 --
22  
--- > infixl 9 :@
23  
--- > data Exp a = V a | Exp a :@ Exp a | Lam (Scope () Exp a)
24  
--- >  deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable)
  29
+-- @
  30
+-- infixl 9 :\@
  31
+-- data Exp a = V a | Exp a :\@ Exp a | Lam ('Scope' () Exp a)
  32
+--   deriving ('Eq','Ord','Show','Read','Functor','Data.Foldable.Foldable','Data.Foldable.Traversable')
  33
+-- @
25 34
 --
26  
--- > instance Eq1 Exp   where (==#)      = (==)
27  
--- > instance Ord1 Exp  where compare1   = compare
28  
--- > instance Show1 Exp where showsPrec1 = showsPrec
29  
--- > instance Read1 Exp where readsPrec1 = readsPrec
30  
--- > instance Applicative Exp where pure = V; (<*>) = ap
  35
+-- @
  36
+-- instance 'Prelude.Extras.Eq1' Exp   where ('Prelude.Extras.==#')      = ('==')
  37
+-- instance 'Prelude.Extras.Ord1' Exp  where 'Prelude.Extras.compare1'   = 'compare'
  38
+-- instance 'Prelude.Extras.Show1' Exp where 'Prelude.Extras.showsPrec1' = 'showsPrec'
  39
+-- instance 'Prelude.Extras.Read1' Exp where 'Prelude.Extras.readsPrec1' = 'readsPrec'
  40
+-- instance 'Control.Applicative.Applicative' Exp where 'Control.Applicative.pure' = V; ('<*>') = 'Control.Monad.ap'
  41
+-- @
31 42
 --
32  
--- > instance Monad Exp where
33  
--- >   return = V
34  
--- >   V a      >>= f = f a
35  
--- >   (x :@ y) >>= f = (x >>= f) :@ (y >>= f)
36  
--- >   Lam e    >>= f = Lam (e >>>= f)
37  
--- >
38  
--- > lam :: Eq a => a -> Exp a -> Exp a
39  
--- > lam v b = Lam (abstract1 v b)
  43
+-- @
  44
+-- instance 'Monad' Exp where
  45
+--   'return' = V
  46
+--   V a      '>>=' f = f a
  47
+--   (x :\@ y) '>>=' f = (x '>>=' f) :\@ (y >>= f)
  48
+--   Lam e    '>>=' f = Lam (e '>>>=' f)
  49
+-- @
40 50
 --
41  
--- > whnf :: Exp a -> Exp a
42  
--- > whnf (f :@ a) = case whnf f of
43  
--- >   Lam b -> whnf (instantiate1 a b)
44  
--- >   f'    -> f' :@ a
45  
--- > whnf e = e
  51
+-- @
  52
+-- lam :: 'Eq' a => a -> 'Exp' a -> 'Exp' a
  53
+-- lam v b = Lam ('abstract1' v b)
  54
+-- @
  55
+--
  56
+-- @
  57
+-- whnf :: 'Exp' a -> 'Exp' a
  58
+-- whnf (f :\@ a) = case whnf f of
  59
+--   Lam b -> whnf ('instantiate1' a b)
  60
+--   f'    -> f' :\@ a
  61
+-- whnf e = e
  62
+-- @
46 63
 --
47 64
 -- More exotic combinators for manipulating a 'Scope' can be imported from
48 65
 -- "Bound.Scope".
20  src/Bound/Class.hs
@@ -12,6 +12,8 @@
12 12
 -- Stability   :  experimental
13 13
 -- Portability :  portable
14 14
 --
  15
+-- This module provides the 'Bound' class, for performing substitution into
  16
+-- things that are not necessarily full monad transformers.
15 17
 ----------------------------------------------------------------------------
16 18
 module Bound.Class
17 19
   ( Bound(..)
@@ -24,15 +26,22 @@ import Control.Monad.Trans.Class
24 26
 
25 27
 infixl 1 >>>=
26 28
 
27  
--- | Instances may or may not be monad transformers.
  29
+-- | Instances of 'Bound' may or may not be monad transformers.
28 30
 --
29  
--- If they are, then you can use @m >>>= f = m >>= lift . f@
  31
+-- If they are, then @m '>>>=' f ≡ m '>>=' 'lift' '.' f@ is required to hold, and is
  32
+-- in fact the default definition. If it is not a 'MonadTrans' instance, then
  33
+-- you have greater flexibility in how to implement this class.
30 34
 --
31 35
 -- This is useful for types like expression lists, case alternatives,
32 36
 -- schemas, etc. that may not be expressions in their own right, but often
33  
--- contain one.
34  
-
  37
+-- contain expressions.
35 38
 class Bound t where
  39
+  -- | Perform substitution
  40
+  --
  41
+  -- If @t@ is an instance of @MonadTrans@ and you are compiling on GHC >= 7.4, then this
  42
+  -- gets the default definition:
  43
+  --
  44
+  -- @m '>>>=' f = m '>>=' 'lift' '.' f@
36 45
   (>>>=) :: Monad f => t f a -> (a -> f c) -> t f c
37 46
 #if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 704
38 47
   default (>>>=) :: (MonadTrans t, Monad f, Monad (t f)) =>
@@ -41,5 +50,8 @@ class Bound t where
41 50
 #endif
42 51
 
43 52
 infixr 1 =<<<
  53
+-- | A flipped version of ('>>>=').
  54
+--
  55
+-- @('=<<<') = 'flip' ('>>>=')@
44 56
 (=<<<) :: (Bound t, Monad f) => (a -> f c) -> t f a -> t f c
45 57
 (=<<<) = flip (>>>=)
21  src/Bound/Scope.hs
@@ -8,6 +8,10 @@
8 8
 -- Stability   :  experimental
9 9
 -- Portability :  portable
10 10
 --
  11
+-- This is the work-horse of the @bound@ library.
  12
+--
  13
+-- 'Scope' provides a single generalized de Bruijn level
  14
+-- and is often used inside of the definition of binders.
11 15
 ----------------------------------------------------------------------------
12 16
 module Bound.Scope
13 17
   ( Scope(..)
@@ -171,21 +175,21 @@ toScope :: Monad f => f (Var b a) -> Scope b f a
171 175
 toScope e = Scope (liftM (fmap return) e)
172 176
 {-# INLINE toScope #-}
173 177
 
174  
--- | Perform substitution on both bound and free variables in a 'Scope'
  178
+-- | Perform substitution on both bound and free variables in a 'Scope'.
175 179
 splat :: Monad f => (a -> f c) -> (b -> f c) -> Scope b f a -> f c
176 180
 splat f unbind s = unscope s >>= \v -> case v of
177 181
   B b -> unbind b
178 182
   F ea -> ea >>= f
179 183
 {-# INLINE splat #-}
180 184
 
181  
--- Return a list of occurences of the variables bound by this scope
  185
+-- | Return a list of occurences of the variables bound by this 'Scope'.
182 186
 bindings :: Foldable f => Scope b f a -> [b]
183 187
 bindings (Scope s) = foldr f [] s where
184 188
   f (B v) vs = v : vs
185 189
   f _ vs     = vs
186 190
 {-# INLINE bindings #-}
187 191
 
188  
--- | Perform a change of variables on bound variables
  192
+-- | Perform a change of variables on bound variables.
189 193
 mapBound :: Functor f => (b -> b') -> Scope b f a -> Scope b' f a
190 194
 mapBound f (Scope s) = Scope (fmap f' s) where
191 195
   f' (B b) = B (f b)
@@ -226,6 +230,7 @@ foldMapScope :: (Foldable f, Monoid r) =>
226 230
 foldMapScope f g (Scope s) = foldMap (bifoldMap f (foldMap g)) s
227 231
 {-# INLINE foldMapScope #-}
228 232
 
  233
+-- | 'traverse_' the bound variables in a 'Scope'.
229 234
 traverseBound_ :: (Applicative g, Foldable f) =>
230 235
                   (b -> g d) -> Scope b f a -> g ()
231 236
 traverseBound_ f (Scope s) = traverse_ f' s
@@ -233,7 +238,7 @@ traverseBound_ f (Scope s) = traverse_ f' s
233 238
         f' _     = pure ()
234 239
 {-# INLINE traverseBound_ #-}
235 240
 
236  
---- | Traverse both the variables bound by this scope and any free variables.
  241
+-- | 'traverse' both the variables bound by this scope and any free variables.
237 242
 traverseScope_ :: (Applicative g, Foldable f) =>
238 243
                   (b -> g d) -> (a -> g c) -> Scope b f a -> g ()
239 244
 traverseScope_ f g (Scope s) = traverse_ (bitraverse_ f (traverse_ g)) s
@@ -253,7 +258,7 @@ mapMScope_ :: (Monad m, Foldable f) =>
253 258
 mapMScope_ f g (Scope s) = mapM_ (bimapM_ f (mapM_ g)) s
254 259
 {-# INLINE mapMScope_ #-}
255 260
 
256  
---- | Traverse both bound and free variables
  261
+-- | Traverse both bound and free variables
257 262
 traverseBound :: (Applicative g, Traversable f) =>
258 263
                  (b -> g c) -> Scope b f a -> g (Scope c f a)
259 264
 traverseBound f (Scope s) = Scope <$> traverse f' s where
@@ -261,13 +266,13 @@ traverseBound f (Scope s) = Scope <$> traverse f' s where
261 266
   f' (F a) = pure (F a)
262 267
 {-# INLINE traverseBound #-}
263 268
 
264  
---- | Traverse both bound and free variables
  269
+-- | Traverse both bound and free variables
265 270
 traverseScope :: (Applicative g, Traversable f) =>
266 271
                  (b -> g d) -> (a -> g c) -> Scope b f a -> g (Scope d f c)
267 272
 traverseScope f g (Scope s) = Scope <$> traverse (bitraverse f (traverse g)) s
268 273
 {-# INLINE traverseScope #-}
269 274
 
270  
---- | mapM over both bound and free variables
  275
+-- | mapM over both bound and free variables
271 276
 mapMBound :: (Monad m, Traversable f) =>
272 277
              (b -> m c) -> Scope b f a -> m (Scope c f a)
273 278
 mapMBound f (Scope s) = liftM Scope (mapM f' s) where
@@ -275,7 +280,7 @@ mapMBound f (Scope s) = liftM Scope (mapM f' s) where
275 280
   f' (F a) = return (F a)
276 281
 {-# INLINE mapMBound #-}
277 282
 
278  
---- | A 'traverseScope' that can be used when you only have a 'Monad'
  283
+-- | A 'traverseScope' that can be used when you only have a 'Monad'
279 284
 -- instance
280 285
 mapMScope :: (Monad m, Traversable f) =>
281 286
              (b -> m d) -> (a -> m c) -> Scope b f a -> m (Scope d f c)
24  src/Bound/Term.hs
@@ -20,22 +20,44 @@ import Data.Foldable
20 20
 import Data.Traversable
21 21
 import Prelude hiding (all)
22 22
 
23  
--- | @'substitute' a p w@ replaces the free variable @a@ with @p@ in @w@
  23
+-- | @'substitute' a p w@ replaces the free variable @a@ with @p@ in @w@.
  24
+--
  25
+-- >>> substitute "hello" ["goodnight","Gracie"] ["hello","!!!"]
  26
+-- ["goodnight","Gracie","!!!"]
24 27
 substitute :: (Monad f, Eq a) => a -> f a -> f a -> f a
25 28
 substitute a p w = w >>= \b -> if a == b then p else return b
26 29
 {-# INLINE substitute #-}
27 30
 
  31
+-- | @'substituteVar' a b w@ replaces a free variable @a@ with another free variable @b@ in @w@.
  32
+--
  33
+-- >>> substitute "Alice" "Bob" ["Alice","Bob","Charlie"]
  34
+-- ["Bob","Bob","Charlie"]
28 35
 substituteVar :: (Functor f, Eq a) => a -> a -> f a -> f a
29 36
 substituteVar a p = fmap (\b -> if a == b then p else b)
30 37
 {-# INLINE substituteVar #-}
31 38
 
32 39
 -- | If a term has no free variables, you can freely change the type of
33 40
 -- free variables it is parameterized on.
  41
+--
  42
+-- >>> closed [12]
  43
+-- Nothing
  44
+--
  45
+-- >>> closed ""
  46
+-- Just []
  47
+--
  48
+-- >>> :t closed ""
  49
+-- closed "" :: Maybe [b]
34 50
 closed :: Traversable f => f a -> Maybe (f b)
35 51
 closed = traverse (const Nothing)
36 52
 {-# INLINE closed #-}
37 53
 
38 54
 -- | A closed term has no free variables.
  55
+--
  56
+-- >>> isClosed []
  57
+-- True
  58
+--
  59
+-- >>> isClosed [1,2,3]
  60
+-- False
39 61
 isClosed :: Foldable f => f a -> Bool
40 62
 isClosed = all (const False)
41 63
 {-# INLINE isClosed #-}
4  src/Bound/Var.hs
@@ -25,9 +25,9 @@ import Prelude.Extras
25 25
 
26 26
 -- | \"I am not a number, I am a /free monad/!\"
27 27
 --
28  
--- A @Var b a@ is a variable that may either be \"bound\" or \"free\".
  28
+-- A @'Var' b a@ is a variable that may either be \"bound\" ('B') or \"free\" ('F').
29 29
 --
30  
--- (It is also technically a free monad in the same near trivial sense as
  30
+-- (It is also technically a free monad in the same near-trivial sense as
31 31
 -- 'Either'.)
32 32
 data Var b a
33 33
   = B b -- ^ this is a bound variable
5  tests/doctests.hs
@@ -17,7 +17,10 @@ main = getSources >>= \sources -> doctest $
17 17
   : sources
18 18
 
19 19
 getSources :: IO [FilePath]
20  
-getSources = filter (isSuffixOf ".hs") <$> go "examples"
  20
+getSources = do
  21
+  examples <- go "examples"
  22
+  src <- go "src"
  23
+  return $ filter (isSuffixOf ".hs") (examples ++ src)
21 24
   where
22 25
     go dir = do
23 26
       (dirs, files) <- getFilesAndDirectories dir

0 notes on commit c235b48

Please sign in to comment.
Something went wrong with that request. Please try again.