Agda-2.3.2.2: examples/Introduction/Modules.agda
-- This module gives an introduction to the module system of Agda.
module Introduction.Modules where
---------------------------------------------------------------------------
-- Simple sub-modules
---------------------------------------------------------------------------
-- As mentioned in 'Introduction.Basics' each file contains a single top-level
-- module. This module can contain any number of sub-modules. A sub-module is
-- declared in the same way as the top-level module, except that its name is
-- not qualified.
module Numbers where
-- The contents of the top-level module do not have to be indented, but the
-- contents of a sub-module do.
data Nat : Set where
zero : Nat
suc : Nat -> Nat
-- Outside a module its contents can be accessed using the name of the module.
one : Numbers.Nat
one = Numbers.suc Numbers.zero
-- Of course, this would get very tedious after a while, so to bring the
-- contents of a module into scope you can use an 'open' declaration.
open Numbers
two : Nat
two = suc one
-- When opening a module it is possible to control what names are brought into
-- scope. The open declaration supports three modifiers :
-- using (x1; ..; xn) only bring x1 .. xn into scope
-- renaming (x to y;..) bring y into scope and make it refer to the name x
-- from the opened module.
-- hiding (x1; ..; xn) bring everything except x1 .. xn into scope
-- The using and hiding modifiers can be combined with renaming but not with
-- each other.
-- For example, this will bring the names z and s (and nothing else) into
-- scope as new names for zero and suc.
open Numbers using () renaming (zero to z; suc to s)
-- We can now pattern match on the renamed constructors.
plus : Nat -> Nat -> Nat
plus z m = m
plus (s n) m = s (plus n m)
---------------------------------------------------------------------------
-- 'private' and 'abstract'
---------------------------------------------------------------------------
-- Above we saw how to control which names are brought into scope when opening
-- a module. It is also possible to restrict what is visible outside a module
-- by declaring things 'private'. Declaring something private will only prevent
-- someone from using it outside the module, it doesn't prevent it from showing
-- up after reduction, or from it to reduce.
-- To prevent something from reducing (effectively hiding the definition) it
-- can be declared 'abstract'.
module Datastructures where
private
data List (A : Set) : Set where
nil : List A
_::_ : A -> List A -> List A
_++_ : {A : Set} -> List A -> List A -> List A
nil ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)
reverse : {A : Set} -> List A -> List A
reverse nil = nil
reverse (x :: xs) = reverse xs ++ (x :: nil)
-- Not making the stack operations abstract will reveal the underlying
-- implementation, even though it's private.
Stack : Set -> Set
Stack A = List A
emptyS : {A : Set} -> Stack A
emptyS = nil
push : {A : Set} -> A -> Stack A -> Stack A
push x xs = x :: xs
abstract
-- An abstract datatype doesn't reveal its constructors
data Queue (A : Set) : Set where
queue : (front back : List A) -> Queue A -- invariant : if the front is
-- empty, so is the back
-- Abstraction is contagious, anything that pattern matches on a queue must
-- also be abstract.
private
-- make sure the invariant is preserved
flip : {A : Set} -> Queue A -> Queue A
flip (queue nil back) = queue (reverse back) nil
flip q = q
-- these functions will not reduce outside the module
emptyQ : {A : Set} -> Queue A
emptyQ = queue nil nil
enqueue : {A : Set} -> A -> Queue A -> Queue A
enqueue x (queue front back) = flip (queue front (x :: back))
open Datastructures
testS = push zero emptyS
testQ = enqueue zero emptyQ