--------------------------------------------------------------------------------
/mylib/tests/dictionaryTest.idr:
--------------------------------------------------------------------------------
1 | module dictionaryTest
2 |
3 | import dictionary
4 | import nat
5 | import bool
6 | import eq
7 | import pair
8 |
9 | -- tests using a (Dictionary nat bool)
10 |
11 | -- An empty dictionary
12 | d: Dictionary Nat Bool
13 | d = dictionary_new
14 |
15 | -- insert (0, False)
16 | d': Dictionary Nat Bool
17 | d' = dictionary_insert d (MkPair nat_zero False)
18 |
19 | -- insert (1, True) into result of last operation
20 | d'': Dictionary Nat Bool
21 | d'' = dictionary_insert d' (MkPair nat_one True)
22 |
23 | -- Now find values for keys in and not in the dictionary
24 | b1: Option Bool
25 | b1 = dictionary_lookup d'' nat_zero
26 | -- Expect (Some False)
27 |
28 | b2: Option Bool
29 | b2 = dictionary_lookup d'' nat_one
30 | -- Expect (Some True)
31 |
32 | b3: Option Bool
33 | b3 = dictionary_lookup d'' nat_two
34 | -- expect None
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build artefacts
2 | dist
3 | *.ibc
4 | *.o
5 | *.a
6 | *.so
7 | *.dll
8 | *.dylib
9 | src/Version_idris.hs
10 |
11 | # Doc generation artefacts
12 | libs/base/base_doc/
13 | libs/effects/effects_doc/
14 | libs/prelude/prelude_doc/
15 | libs/contrib/contrib_doc/
16 | libs/pruviloj/pruviloj_doc/
17 | libs/oldeffects/oldeffects_doc/
18 |
19 | # Test artefacts
20 | test/output
21 | test/*[0-9][0-9][0-9]/output
22 | test/*[0-9][0-9][0-9]/*.exe
23 | test/runtest.exe
24 | test/runtest
25 | .tasty-rerun-log
26 |
27 | # Haskell build tools
28 | cabal-dev/
29 | .cabal-sandbox
30 | cabal.sandbox.config
31 | cabal.config
32 | .stack-work
33 | .hsenv
34 |
35 | # For convenience
36 | custom.mk
37 | \#*
38 | .\#*
39 | tags
40 | TAGS
41 |
42 | # IDE and editors
43 | *.swp
44 | *~
45 | .DS_Store
46 | .hpc
47 | *.orig
48 | *.tix
49 | *.dSYM
50 | .projectile
51 | .dir-locals.el
52 | .vscode
53 | .idea
--------------------------------------------------------------------------------
/mylib/src/unit.idr:
--------------------------------------------------------------------------------
1 | ||| Abstract data type simulating algebra with one value
2 | ||| and the identity function.
3 | module unit
4 |
5 | import public bool
6 | import public eq
7 |
8 | ||| The type with only one value
9 | export
10 | data Unit =
11 | ||| The single term of type Unit
12 | MkUnit
13 |
14 | ||| Give a non-constructor name to the only value of this type
15 | export
16 | void: Unit
17 | void = MkUnit
18 |
19 | ||| Ths function simulates the identity function on the Unit type
20 | export
21 | unit_id: Unit -> Unit
22 | unit_id MkUnit = MkUnit
23 |
24 |
25 | ||| Unit equality
26 | export
27 | unit_eq: Unit -> Unit -> Bool
28 | unit_eq u1 u2 = True
29 |
30 |
31 | ||| Implementation of Eq interface for Unit type
32 | export
33 | implementation Eq Unit where
34 | eq u1 u2 = unit_eq u1 u2
35 | neq u1 u2 = bool_not (eq u1 u2)
36 | {-
37 | eq _ _ = True
38 | neq _ _ = False
39 | -}
40 |
--------------------------------------------------------------------------------
/python/practice1.py:
--------------------------------------------------------------------------------
1 | # A python module should start with a
2 | # documentation string (like the inline
3 | # documentation we put in Idris modules.)
4 | # Start and end docstrings with """.
5 | """A few examples for practice"""
6 |
7 | # Function names should be lower case and can contain _
8 | # And the same goes for the names of arguments
9 | # Use descriptive names to document intended meanings
10 | def quad(a_coeff, b_coeff, c_coeff, x_value):
11 | # Each function should also have a docstring
12 | # Such a docstring goes right after the #def line
13 | # Intervening comments like this one are ignored
14 | """Evaluate quadratic function at x = v"""
15 | return ((a_coeff * x_value * x_value) +
16 | b_coeff * x_value +
17 | c_coeff)
18 | # Expressions in parenthesis can be broken across lines
19 |
20 | # A little test case, expect 1.
21 | print(quad(3, 2, 1, 0))
22 |
--------------------------------------------------------------------------------
/mylang/src/variableTest.idr:
--------------------------------------------------------------------------------
1 | module variableTest
2 |
3 | import variable
4 | import nat
5 |
6 | -- A few test cases for var_eq (refactor)
7 |
8 | public export
9 | X: Variable Nat
10 | X = variable_new nat_zero
11 |
12 | public export
13 | Y: Variable Nat
14 | Y = variable_new nat_one
15 |
16 | public export
17 | Z: Variable Nat
18 | Z = variable_new nat_two
19 |
20 | -- Now we can also declare variable that
21 | -- to which we will assign (associate) Bool values
22 |
23 | public export
24 | A: Variable Bool
25 | A = variable_new nat_zero
26 |
27 | public export
28 | B: Variable Bool
29 | B = variable_new nat_one
30 |
31 | public export
32 | C: Variable Bool
33 | C = variable_new nat_two
34 |
35 |
36 | -- Tests of bool_eq
37 |
38 | b1: Bool
39 | b1 = var_eq X X
40 |
41 | b2: Bool
42 | b2 = var_eq X Y
43 |
44 |
45 | -- Tests of eq (for Bool)
46 |
47 | b3: Bool
48 | b3 = eq X Y
49 |
50 | b4: Bool
51 | b4 = eq Y Y
52 |
--------------------------------------------------------------------------------
/mylib/new/nat.idr:
--------------------------------------------------------------------------------
1 | module nat
2 |
3 | export
4 | data Nat = Z | S Nat
5 |
6 | -- Always a good idea to define a few constants, for testing, to be refactored
7 |
8 | nat0: Nat
9 | nat0 = Z
10 |
11 | nat1: Nat
12 | nat1 = S Z
13 |
14 | nat1': Nat
15 | nat1' = S nat0
16 |
17 | nat2: Nat
18 | nat2 = S nat1
19 |
20 | nat3: Nat
21 | nat3 = S nat2
22 |
23 | -- We can define a successor function, succ: Nat -> Nat
24 | export
25 | nat_succ: Nat -> Nat
26 | nat_succ n = S n
27 |
28 | -- Crucial idea: a Nat can be only a Z or an S with a one-smaller Nat inside!
29 |
30 | -- Getting the smaller Nat inside gives us the predecessor, except for Z, and
31 | -- for now we'll just define the predecessor of Z to be Z
32 |
33 | export
34 | nat_pred: Nat -> Nat
35 | nat_pred Z = Z
36 | nat_pred (S n') = n'
37 |
38 | -- And here it comes -- a real mind-blower -- addition!
39 |
40 | export
41 | nat_plus: Nat -> Nat -> Nat
42 | nat_plus Z m = m
43 | nat_plus (S n') m = S (nat_plus n' m)
44 |
--------------------------------------------------------------------------------
/mylib/src/byte2.idr:
--------------------------------------------------------------------------------
1 | ||| Abstract data type representing two-bit byte
2 | module byte2
3 |
4 | {-
5 | A byte aggregates several bits into a tuple of bits.
6 | The bits are essential, thus visible, parts of a byte.
7 | Note that this is a comment. You cannot attach inline
8 | documentation to an import directive.
9 | -}
10 | import public bit
11 | import public bool
12 |
13 | ||| A two-bit byte type; constructor is private
14 | export
15 | data Byte2 =
16 | ||| Constructor that boxes two Bit values into a Byte2 value
17 | MkByte2 Bit Bit
18 |
19 |
20 | ||| Provide a function that packs two bits into and returns a byte
21 | export
22 | byte2_new: Bit -> Bit -> Byte2
23 | byte2_new b1 b0 = MkByte2 b1 b0
24 |
25 |
26 | ||| Projection function returning low-order bit of a Byte2
27 | export
28 | byte2_b0: Byte2 -> Bit
29 | byte2_b0 (MkByte2 b1 b0) = b0
30 |
31 |
32 | ||| Projection function returning high-order bit of a Byte2
33 | export
34 | byte2_b1: Byte2 -> Bit
35 | byte2_b1 (MkByte2 b1 b0) = b1
36 |
37 | ||| 2-bit byte equality
38 | byte2_eq: Byte2 -> Byte2 -> Bool
39 | byte2_eq (MkByte2 bit11 bit10) (MkByte2 bit01 bit00) =
40 | bool_and (bit_eq bit11 bit01) (bit_eq bit10 bit00)
41 |
--------------------------------------------------------------------------------
/mylang/src/expressionTest.idr:
--------------------------------------------------------------------------------
1 | module expressionTest
2 |
3 | import expression
4 | import variable
5 | import state
6 | import nat
7 |
8 | ---- TEST CODE, TO BE REFACTORED INTO expressionTest.idr
9 |
10 | -- just a Bool Variable with an easy name, V
11 | V: Variable Bool
12 | V = variable_new nat_zero
13 |
14 | {-
15 | This code didn't work, because "S" is already
16 | defined as a constructor, *visible* in this module,
17 | in the nat module. In what follows, we use st instead,
18 | and add primes to the name as we derive new states.
19 |
20 | S: State
21 | S = override_state_bool state_init V True
22 | -}
23 |
24 | --- The rest shows how we can chain together a sequence
25 | --- of Nat and Bool Variable overrides, using the result
26 | --- of the previous one as the start point for the next.
27 |
28 |
29 | --- We'll call the initial state, st
30 | st: State
31 | st = state_init
32 |
33 | --- override: result function like input but maps V to True
34 | st': State
35 | st' = state_override_bool st V True
36 |
37 | --- a Nat Variable with a nice name
38 | N: Variable Nat
39 | N = variable_new nat_zero
40 |
41 | --- Now create state with V = True and N = nat_one.
42 | st'': State
43 | st'' = state_override_nat st' N nat_one
44 |
--------------------------------------------------------------------------------
/mylang/src/variable-old.idr:
--------------------------------------------------------------------------------
1 | module variable
2 |
3 | import nat
4 | import bool
5 | import ifthenelse
6 | import public eq
7 |
8 | -----------------------------------------------------
9 | ----------------- Variables -------------------------
10 | -----------------------------------------------------
11 |
12 |
13 | ||| A type the values of which are "variables.
14 | ||| Variables are distinguished from each other by
15 | ||| an additional natural number argument. We will
16 | ||| call that value an index. The set of values of
17 | ||| this type is thus "indexed" by values of type
18 | ||| Nat.
19 | export
20 | data Variable = MkVariable Nat
21 |
22 |
23 | ||| A public function that returns a variable
24 | ||| given its index. This is the public interface
25 | ||| that clients of this module can use to obtain
26 | ||| Variables.
27 | export
28 | variable_new: Nat -> Variable
29 | variable_new n = MkVariable n
30 |
31 |
32 | ||| Variables are equal iff their indices are equal.
33 | export
34 | var_eq: Variable -> Variable -> Bool
35 | var_eq (MkVariable n1) (MkVariable n2) = nat_eq n1 n2
36 |
37 |
38 | ||| Overload eq for Variables
39 | public export
40 | implementation Eq Variable where
41 | eq v1 v2 = var_eq v1 v2
42 | neq v1 v2 = bool_not (eq v1 v2)
43 |
--------------------------------------------------------------------------------
/mylib/src/mylib_doc/docs/binary.html:
--------------------------------------------------------------------------------
1 |
2 | IdrisDoc: binary
IdrisDoc: binary
binary
A library of functions for binary arithmetic
3 | In particular, this module implements half and
4 | full adder functions.
--------------------------------------------------------------------------------
/mylang/src/variable.idr:
--------------------------------------------------------------------------------
1 | module variable
2 |
3 | import nat
4 | import bool
5 | import ifthenelse
6 | import public eq
7 |
8 | -----------------------------------------------------
9 | ----------------- Variables -------------------------
10 | -----------------------------------------------------
11 |
12 |
13 | ||| We first define a simple abstract data type the
14 | ||| values of which are "variables capable of having
15 | ||| associated values of type t."" We make the Variable
16 | ||| type public but make the constructor private to this
17 | ||| module.
18 | export
19 | data Variable t = MkVariable Nat
20 |
21 |
22 | ||| A public function that returns a variable
23 | ||| given its type (implicit) and index. This is the
24 | ||| public funtion that clients of this module can
25 | ||| use to obtain Variables, as the MkVariable
26 | ||| constructor is private to this module.
27 | export
28 | variable_new: { t: Type } -> Nat -> Variable t
29 | variable_new n = MkVariable n
30 |
31 |
32 | -- simple demonstration
33 | v: Variable Bool
34 | v = variable_new nat_zero
35 |
36 |
37 | ||| Equality for variables: True if two variables of a give
38 | ||| type are equal. They are "iff" their numeric "indexes"
39 | ||| are equal.
40 | export
41 | var_eq: {t: Type} -> Variable t -> Variable t -> Bool
42 | var_eq (MkVariable n1) (MkVariable n2) = nat_eq n1 n2
43 |
44 |
45 | ||| Overload eq for Variables
46 | public export
47 | implementation Eq (Variable t) where
48 | eq v1 v2 = var_eq v1 v2
49 | neq v1 v2 = bool_not (eq v1 v2)
50 |
--------------------------------------------------------------------------------
/mylib/src/mylib_doc/docs/unit.html:
--------------------------------------------------------------------------------
1 |
2 | IdrisDoc: unit
IdrisDoc: unit
unit
Abstract data type simulating algebra with one value
3 | and the identity function.
--------------------------------------------------------------------------------
/mylib/src/list_nat.idr:
--------------------------------------------------------------------------------
1 | ||| An abstract data type, nat, simulating
2 | ||| the natural numbers and common arithmetic
3 | ||| operations involving natural numbers
4 | module list_nat
5 |
6 | import nat
7 |
8 | ||| The primary data type we export is Nat.
9 | ||| The constructors are hidden from other modules.
10 | export
11 | data ListNat =
12 | ||| Nil_Nat represents the empty list of natural numbers
13 | NilNat |
14 | ||| Con_Nat head tail represents the
15 | ConsNat Nat ListNat
16 |
17 |
18 | {-
19 | We provide a slightly more abstract way to obtain
20 | values of type Nat.
21 | -}
22 |
23 | ||| Return the Nat representing zero
24 | export
25 | list_nat_empty: ListNat
26 | list_nat_empty = NilNat
27 |
28 |
29 | ||| Identity function for list_nat_cons
30 | export
31 | list_nat_id: ListNat -> ListNat
32 | list_nat_id l = l
33 |
34 |
35 |
36 | ||| Return the (Nat representing the) successor of the given Nat
37 | export
38 | list_nat_cons: Nat -> ListNat -> ListNat
39 | list_nat_cons n l = ConsNat n l
40 |
41 |
42 |
43 | ||| Compute the tail of a given list (with tail nil = nil)
44 | list_nat_tail: ListNat -> ListNat
45 | list_nat_tail NilNat = NilNat
46 | list_nat_tail (ConsNat head tail) = tail
47 |
48 |
49 | {-
50 | Key idea: a Nat can be only a Z or an S and a one-smaller Nat
51 | The smaller Nat inside gives us the predecessor except for Z
52 | For Z we'll define the predecessor to be just Z itself.
53 | -}
54 |
55 | -- And here it comes -- a real mind-blower -- arithmetic!
56 |
57 | ||| Return true if a given number is even otherwise false
58 |
59 | ||| (This function simulates) natural number addition
60 | ||| in Peano arithmetic
61 | export
62 | list_nat_plus: ListNat -> ListNat -> ListNat
63 | list_nat_plus NilNat m = m
64 | list_nat_plus (ConsNat h t) m = ConsNat h (list_nat_plus t m)
65 |
66 | -- And some code to test it all out
67 |
--------------------------------------------------------------------------------
/mylang/src/expression.idr:
--------------------------------------------------------------------------------
1 | module expr
2 |
3 | import nat
4 | import variable
5 | import state
6 |
7 | -- SYNTAX for Nat expressions
8 |
9 | ||| NatExpr is the type of expressions that evaluate to
10 | ||| natural numbers (Nat). We'll call them Nat Expressions.
11 | public export
12 | data NatExpr =
13 | NatLitExpr Nat |
14 | NatVarExpr (Variable Nat) |
15 | NatPlusExpr NatExpr NatExpr |
16 | NatMinusExpr NatExpr NatExpr
17 |
18 | -- SEMANTICS for Nat Expressions
19 |
20 | ||| natExprEval evaluates Nat Expressions to yield Nats.
21 | export
22 | natExprEval: NatExpr -> State -> Nat
23 | natExprEval (NatLitExpr n) st = n
24 | natExprEval (NatVarExpr v) st = (getNatState st) v
25 | natExprEval (NatPlusExpr e1 e2) st =
26 | nat_plus -- the result is the sum of
27 | (natExprEval e1 st) -- the value of expr1
28 | (natExprEval e2 st) -- and expr2
29 | natExprEval (NatMinusExpr e1 e2) st =
30 | nat_minus
31 | (natExprEval e1 st)
32 | (natExprEval e2 st)
33 |
34 | -- Syntax for Bool expressions
35 |
36 |
37 | ||| BoolExpr is the type of expressions that evaluate to
38 | ||| Boolean values (Bool). We'll call them Bool Expressions.
39 | public export
40 | data BoolExpr =
41 | BoolLitExpr Bool |
42 | BoolVarExpr (Variable Bool) |
43 | BoolAndExpr BoolExpr BoolExpr |
44 | BoolEqExpr NatExpr NatExpr |
45 | BoolNeqExpr NatExpr NatExpr
46 |
47 |
48 | -- Semantics for Bool expressions
49 |
50 | ||| natExprEval evaluates Nat Expressions to yield Nats.
51 | export
52 | boolExprEval: BoolExpr -> State -> Bool
53 | boolExprEval (BoolLitExpr b) st = b
54 | boolExprEval (BoolVarExpr v) st = (getBoolState st) v
55 | boolExprEval (BoolAndExpr e1 e2) st =
56 | bool_and (boolExprEval e1 st) (boolExprEval e2 st)
57 | boolExprEval (BoolEqExpr n1 n2) st =
58 | eq (natExprEval n1 st) (natExprEval n2 st)
59 | boolExprEval (BoolNeqExpr n1 n2) st =
60 | neq (natExprEval n1 st) (natExprEval n2 st)
61 |
--------------------------------------------------------------------------------
/mylib/tests/listTest.idr:
--------------------------------------------------------------------------------
1 | module listTest
2 |
3 | import list
4 | import nat
5 | import bool
6 |
7 | --- Some example values, eventually move to test file
8 | ln: List Nat
9 | ln = Nil
10 |
11 | ln1: List Nat
12 | ln1 = Cons nat_one ln
13 |
14 | ln2: List Nat
15 | ln2 = Cons nat_two ln1
16 |
17 | ln3: List Nat
18 | ln3 = Cons nat_three ln2
19 |
20 |
21 | -- Another example, fully expanded
22 | lb4: List Bool
23 | lb4 = Cons (True) (Cons False (Cons (True) (Cons False Nil)))
24 |
25 | -- Filter test
26 | ln4: List Nat
27 | ln4 = list_filter nat_evenb ln3
28 |
29 | -- Map test
30 | ln5: List Nat
31 | ln5 = list_map nat_succ ln3
32 |
33 | -- Pasted from list.idr
34 |
35 | -- A few tests, to be refactored out into separate file
36 |
37 | -- A few list values to use in test cases
38 | l: List Nat
39 | l = Nil
40 |
41 | l': List Nat
42 | l' = Cons nat_one (Cons nat_two Nil)
43 |
44 | l'': List Nat
45 | l'' = (Cons nat_one (Cons nat_two (Cons nat_three Nil)))
46 |
47 |
48 | -- Test cases for list_range_by_to
49 | r1: List Nat
50 | r1 = list_range_by_to nat_zero nat_one nat_zero
51 | -- expect [0], i.e., (Cons 0 Nil), the list containing just 0
52 |
53 | r2: List Nat
54 | r2 = list_range_by_to nat_three nat_one nat_one
55 | -- expect [3, 2, 1]
56 |
57 | r3: List Nat
58 | r3 = list_range_by_to nat_three nat_two nat_one
59 | -- expect [3, 1]
60 |
61 | r4: List Nat
62 | r4 = list_range_by_to nat_three nat_three nat_one
63 | -- expect [3]
64 |
65 | r5: List Nat
66 | r5 = list_range_by_to nat_one nat_one nat_three
67 | -- expect Nil
68 |
69 | r6: List Nat
70 | r6 = ?fill_hole_with_ambiguous_case
71 | -- expect Nil
72 |
73 | b1: List Bool
74 | b1 = (Cons False (Cons False Nil)) -- [False, False]
75 |
76 | b2: List Bool
77 | b2 = (Cons False (Cons True (Cons False (Cons True Nil))))
78 |
79 |
80 | br1: List Bool
81 | br1 = list_filter_True b1
82 | -- expect Nil
83 |
84 | br2: List Bool
85 | br2 = list_filter_True b2
86 | -- expect [True, True]
87 |
88 | -- Reduce [1, 2, 3] under addition (with zero as the identity)
89 | n1: Nat
90 | n1 = list_fold_right nat_plus nat_zero l''
91 | -- expect 6
92 |
93 | -- Reduce [1, 2, 3] under addition (with zero as the identity)
94 | n2: Nat
95 | n2 = list_fold_right nat_mult nat_one l''
96 | -- expect 6
97 |
--------------------------------------------------------------------------------
/mylib/src/dictionary.idr:
--------------------------------------------------------------------------------
1 | ||| Implements a dictionary abstract data type
2 | module dictionary
3 |
4 | import list
5 | import pair
6 | import public option
7 | import ifthenelse
8 |
9 | ||| A dictionary data type with a private implemented
10 | export
11 | data Dictionary k v = MkDictionary (List (Pair k v))
12 |
13 |
14 | ||| A public function to build and return an empty dictionary
15 | export
16 | dictionary_new: Dictionary k v
17 | dictionary_new = MkDictionary Nil
18 |
19 |
20 | ||| Function to insert (k, v) into dictionary. If there was no
21 | ||| previous value for k in the dictionary, v will now be that
22 | ||| value. If there was already a value for k, v will be the new
23 | ||| value for k. In other words, dictionary_insert "overwrites"
24 | ||| any previous definition.
25 | export
26 | dictionary_insert: Dictionary k v -> Pair k v -> Dictionary k v
27 | {-
28 | We implement insert in a very straightforward way: We just add
29 | the given pair as the head of the existing list. When we do a
30 | lookup, we will search down the list and return the value (as
31 | an option) for the first pair that matches the given key. that
32 | is our implementation strategy.
33 | -}
34 | dictionary_insert (MkDictionary l) p =
35 | MkDictionary (Cons p l)
36 |
37 | ||| A private helper function in support of dictionary_lookup.
38 | dict_list_lookup: Eq k => List (Pair k v) -> k -> Option v
39 | dict_list_lookup Nil k = None
40 | dict_list_lookup (Cons h t) k =
41 | ifthenelse
42 | (eq k (pair_key h))
43 | (Some (pair_value h))
44 | (dict_list_lookup t k)
45 |
46 | ||| Function to look up and return the value for a given key if
47 | ||| there is one. This function requires the ability to decide
48 | ||| whether the given key, k, is equal to keys in the dictionary;
49 | ||| therefore, as with list_eq, we need to constrain the key type
50 | ||| to be a type for which "eq" is defined, i.e., to be a type for
51 | ||| which there is an implementation of the "Eq" interface.
52 | export
53 | dictionary_lookup: Eq k => (dict: Dictionary k v) -> (key: k) -> Option v
54 | dictionary_lookup (MkDictionary l) k = dict_list_lookup l k
55 |
56 | -- Tests involving empty dictionaries
57 |
58 | aDict: Dictionary Nat Nat
59 | aDict = dictionary_new
60 |
61 | aVal: Option Nat
62 | aVal = dictionary_lookup aDict nat_zero
63 |
--------------------------------------------------------------------------------
/mylib/src/bit.idr:
--------------------------------------------------------------------------------
1 | ||| Abstract data type representing binary arithmetic
2 | module bit
3 |
4 | -- Client of this module will need bool as well
5 | import bool
6 |
7 | {-
8 | Where's a bit is considered an essential part of a byte
9 | (thus "aggregation"), we consider a bool an inessential
10 | detail in the implementation of bit. We use import bool
11 | rather than import public bool because our clients should
12 | not even need to know that this module is implemented in
13 | part using bool. Therefore, while we must export the Bit
14 | type, we hide the constructor, because using it requires a
15 | bool, and we don't want our clients to know that. Instead,
16 | we export two abstract bit values, B0 and B1. Modules that
17 | import this one can use B0 and B1 as the two values of type
18 | Bit. The fact that they are implemented as bools remains a
19 | secret of this module. Thus "abstraction."
20 | -}
21 |
22 | export
23 | data Bit =
24 | ||| Construct a term of type Bit by "boxing up" a Bool
25 | MkBit Bool
26 |
27 |
28 | ||| Represents constant bit value 0
29 | export
30 | B0: Bit
31 | B0 = MkBit False
32 |
33 |
34 | ||| Represents constant bit value 1
35 | export
36 | B1: Bit
37 | B1 = MkBit True
38 |
39 | ||| Return the concrete representation (unbox and return the Bool)
40 | ||| This function mainly just illustrates destructuring of a bit to
41 | ||| get at -- and return -- the underlying bit representation.
42 | bit_rep: Bit -> Bool
43 | bit_rep (MkBit b) = b
44 |
45 | ||| Bit equality
46 | export
47 | bit_eq: Bit -> Bit -> Bool
48 | bit_eq (MkBit bool1) (MkBit bool2) =
49 | bool_eq bool1 bool2
50 |
51 |
52 | ||| Compute the sum bit of two bits
53 | export
54 | bit_plus: Bit -> Bit -> Bit
55 | bit_plus (MkBit b1) (MkBit b2) =
56 | MkBit (bool_xor b1 b2)
57 |
58 |
59 | ||| Compute the carry bit of two bits
60 | export
61 | bit_carry: Bit -> Bit -> Bit
62 | bit_carry (MkBit b1) (MkBit b2) =
63 | MkBit (bool_and b1 b2)
64 |
65 | --- and also, correspondingly, this consider
66 | ||| Compute the sum bit of three bits
67 | export
68 | bit_plus3: Bit -> Bit -> Bit -> Bit
69 | bit_plus3 (MkBit b1) (MkBit b2) (MkBit cin) =
70 | MkBit (bool_xor (bool_xor b1 b2) cin)
71 |
72 |
73 | ||| Compute the carry bit of three bits
74 | export
75 | bit_carry3: Bit -> Bit -> Bit -> Bit
76 | bit_carry3 (MkBit b1) (MkBit b2) (MkBit cin) =
77 | MkBit (bool_or
78 | (bool_and (bool_xor b1 b2) cin)
79 | (bool_and b1 b2))
80 |
--------------------------------------------------------------------------------
/mylib/src/mylib_doc/docs/byte2.html:
--------------------------------------------------------------------------------
1 |
2 | IdrisDoc: byte2
Constructor that boxes two Bit values into a Byte2 value
14 |
--------------------------------------------------------------------------------
/python/imperative.py:
--------------------------------------------------------------------------------
1 | # A single-line comment starts with a hash mark
2 |
3 | '''
4 | This is a multi-line comment.
5 | It starts and ends with sequences
6 | of three single quotes in a row.
7 |
8 | This program illustrates concepts
9 | of state and assignment operations
10 | that modify state. It also introduces
11 | the print statement. Note: This code
12 | is written in Python version 3, not
13 | in Python 2. The syntax of Python 2
14 | is a little different. Be sure you
15 | know what version you're using when
16 | you program in Python.
17 | '''
18 |
19 | # STATE AND ASSIGNMENT Commands
20 |
21 | x = 5
22 | y = 6
23 | z = 7
24 |
25 | print(x, y, z)
26 |
27 | x = 11
28 | y = 13
29 | z = x
30 |
31 |
32 | print(x, y, z)
33 |
34 | # IF THEN ELSE Commands
35 |
36 | print("Let's explore conditionals!")
37 |
38 | x = 5
39 | if x == 11: # if (boolean expr):
40 | print("It's 11!") # true branch; "..." is a string
41 | else: # optional else:
42 | print("It wasn't 11 ...") # and the false branch
43 | x = 11
44 | print("... but now it is!")
45 |
46 |
47 | # WHILE Commands
48 |
49 | print("Let's loop!")
50 | n = 5 # establish "loop index"
51 | while n > 0: # if (boolean expression)
52 | print(n) # loop body, executed if True
53 | n = n - 1 # You must avoid infinite loops!!!
54 |
55 |
56 | # Exercise: compute the sum of integers from 1 to 10
57 |
58 | # Note that this solution starts with 10 and goes to 0
59 | print("Let's sum 1 to n!")
60 | n = 10
61 | sum = 0
62 |
63 | while n > 0:
64 | sum = sum + n
65 | n = n - 1
66 | print("The sum is", sum)
67 |
68 | # Here's a version that starts at 0 and goes to 10
69 |
70 | end = 10
71 | cur = 0
72 | sum = 0
73 |
74 | while cur <= end:
75 | sum = sum + cur
76 | cur = cur + 1
77 | print("The sum is", sum)
78 |
79 |
80 | # Finally, functions and recursion
81 |
82 | def sum_rec(n):
83 | if n == 0:
84 | return 0
85 | else:
86 | return n + sum_rec(n-1)
87 |
88 | print("The sum is", sum(10))
89 |
90 | def fac(n):
91 | if n == 0:
92 | return 1
93 | else:
94 | return n * fac(n-1)
95 |
96 | print(fac(100))
97 |
98 | n = 100
99 | fact = 1
100 | while n > 0:
101 | fact = fact * n
102 | n = n - 1
103 | print(fact)
104 |
--------------------------------------------------------------------------------
/mylang/src/state-old.idr:
--------------------------------------------------------------------------------
1 | module state
2 |
3 | import public variable
4 | import public nat
5 | import ifthenelse
6 |
7 | -----------------------------------------------------
8 | ----------------- STATE -----------------------------
9 | -----------------------------------------------------
10 |
11 | -- We define State to be the *type* (Variable -> Nat)
12 | -- We're really just giving this type a useful name.
13 | -- Here we see that types are values, too, in Idris.
14 |
15 | public export
16 | State: Type
17 | State = (Variable -> Nat)
18 | -- Now we can use "State" instead of "Variable -> Nat".
19 | -- The state of an imperative program *is* a function that
20 | -- associates a value with each variable.
21 |
22 | -- A common initial state maps each Variable to zero.
23 | ||| Return an initial state mapping all variables to zero.
24 | -- A State *is* a function! We need the value of state_init
25 | -- to be a function. Here's notation for "anonymous functions."
26 |
27 | export
28 | state_init: State
29 | state_init = \v: Variable => nat_zero
30 | -- the value of state_init is a function that takes one argument,
31 | -- v, of type Variable and that always returns nat_zero.
32 |
33 |
34 | ||| Function override takes a state, which is to say a function
35 | ||| from variables to values, along with a variable and a value,
36 | ||| and returns a new state, which is to say a new function from
37 | ||| variables to values, and one that is just like the origninal
38 | ||| state (function) except that the given variable now maps to
39 | ||| the given new value.
40 | export
41 | override: (s: State) -> (var: Variable) -> (val: Nat) -> State
42 | override s var val =
43 | \v: Variable => -- return a new state (Variable -> Nat)
44 | ifthenelse -- that when applied to a variable, v,
45 | (var_eq var v) -- asks if v is the variable just overridden
46 | val -- and if so it returns the overriding value,
47 | (s v) -- and otherwise uses the original state (s)
48 | -- to find the value of the given variable
49 | {-
50 | The export function requires some careful thinking. What it returns
51 | is a function that, if given the variable being overridden, returns
52 | the new value, and that otherwise just applies the old state function
53 | to find the value of the variable in the original state.
54 | -}
55 |
56 | -- this file may have been saved too late from buffer into a newer
57 | -- version of the rest of the system
58 |
--------------------------------------------------------------------------------
/mylang/src/commandTest0.idr:
--------------------------------------------------------------------------------
1 | module langTest
2 |
3 | import command
4 | import nat
5 | import variableTest -- defines variables X, Y, Z
6 | import variable
7 | import expression
8 |
9 | -- Tests: Programs are just values of this data type!
10 |
11 | -- Do nothing but skip
12 | prog0: Command
13 | prog0 = Skip
14 |
15 | {-
16 | s0: State
17 | s0 = run prog0 state_init
18 | -}
19 |
20 | -- A single assignment
21 | prog1: Command
22 | prog1 = NatAssign X (NatLitExpr nat_two)
23 |
24 | {-
25 | s1: State
26 | s1 = run prog1 state_init
27 | -- expect s1 X = 2, s1 Y = 0, s1 Z = 0
28 |
29 | -- An assignment in the state produced by the last assignment
30 | s2: State
31 | s2 = run (Assign Y nat_two) s1
32 | -- expect s2 X = 2, s2 Y = 2, s2 Z = 0
33 | -}
34 |
35 | -- A sequential composition of assignments
36 | prog2: Command
37 | prog2 =
38 | Seq
39 | (NatAssign X (NatLitExpr nat_one))
40 | (NatAssign Y (NatLitExpr nat_two))
41 |
42 | {-
43 | s3: State
44 | s3 = run prog2 state_init
45 | -- expect same result as s2
46 | -}
47 |
48 | -- A sequential composition of an assignment with a larger program
49 | prog3: Command
50 | prog3 =
51 | Seq
52 | (NatAssign X (NatLitExpr nat_one))
53 | (Seq
54 | (NatAssign Y (NatLitExpr nat_two))
55 | (NatAssign Z (NatLitExpr nat_three)))
56 |
57 | {-
58 | s4: State
59 | s4 = run prog3 state_init
60 | -}
61 |
62 | -- NEW TEST CASES USING EXPRESSIONS
63 |
64 | -- like "X = Y" in Python
65 | prog4: Command
66 | prog4 = NatAssign X (NatVarExpr Y)
67 |
68 | -- like "X = Y + 1" in Python
69 | prog5: Command
70 | prog5 =
71 | NatAssign
72 | X
73 | (NatPlusExpr
74 | (NatVarExpr Y)
75 | (NatLitExpr nat_one))
76 |
77 |
78 | ---------------------
79 |
80 |
81 | skip: Command
82 | skip = Skip
83 |
84 | -- X = 2
85 | assnCmd: Command
86 | assn = NatAssign X (NatLitExpr nat_two)
87 |
88 |
89 | -- if True:
90 | -- prog1
91 | -- prog2
92 | condCmd: Command
93 | condCmd = IfThenElse
94 | (BoolLitExpr True)
95 | prog1
96 | prog2
97 |
98 |
99 | -- if True:
100 | -- prog1
101 | -- prog2
102 | bodyCmd: Command
103 | bodyCmd = NatAssign
104 | X
105 | (NatMinusExpr X (NatLitExpr nat_one))
106 |
107 |
108 | whileCmd: Command
109 | whileCmd = While
110 | (BoolLitExpr False)
111 | bodyCmd
112 |
113 |
114 | cmd5: Command
115 | cmd5 =
116 | Seq
117 | (NatAssign X (NatLitExpr nat_one))
118 | (NatAssign Y (NatLitExpr nat_two))
119 |
--------------------------------------------------------------------------------
/mylib/src/ifthenelse.idr:
--------------------------------------------------------------------------------
1 | ||| A module providing a polymorphic if/then/else function
2 | module ifthenelse
3 |
4 | import public bool
5 |
6 | -- temporary, for testing, remove
7 | import nat
8 |
9 |
10 | {-
11 | Polymorphism is a fancy word, but all it means for us here is that
12 | we can specidy a whole family of ifthenelse function types, varying
13 | in the type of value they return.
14 |
15 | Go back now and have a look at bool_if_then_else. You will recall
16 | that it takes a Bool (the condition) and two other Bools (the return
17 | values for the True and false cases of the condition).
18 |
19 | It always return a Bool. It's type is Bool -> Bool -> Bool -> Bool.
20 | The first is the condition, then second and third are the true and
21 | false return values, respectively, and the last is the type of the
22 | return value, again Bool.
23 |
24 | What we need is a conditional/ifthenelse expression that returns not
25 | a Bool but a value of some other type. In our particular case, we want
26 | a conditional expression that returns one Bit or another depending on
27 | the Boolean result of comparing two nats for equality (using nat_eq).
28 | The type we need here is Bool -> Bit -> Bit -> Bit, but is is easy to
29 | imagine that we'll want to be able to return values of arbitrary types
30 | based on ifthenelse comparisons.
31 |
32 | What we need is an ifthenelse function that takes an arbitrary type,
33 | call it T, as an argument value, where that *value* (which is some
34 | type, such as Bool, Nat, Bit) defines the *type* of the values returned
35 | by the branches and thus by the expression as a hole.
36 |
37 | In other words, we need an ifthenelse function that has as its type
38 | (T: Type) -> Bool -> T -> T -> T. If the value of the first argument,
39 | now given the name T, defines the type shared by the third and fourth
40 | arguments and the return result. If, for example, T is Bool, then we
41 | have the type of bool_if_the_else.
42 |
43 | What we'll need first is the case where the value of that first "T"
44 | argument is Bit. Now you can see something deep about the logic of the
45 | Idris language: in Idris, types are themselves values. When a function
46 | takes and uses a type as a value, we call it polymorphic in that type.
47 | A function can have several arguments, several of which could be types,
48 | and so a function can be polymorphic in several different types.
49 | -}
50 |
51 | ||| If-then-else returning a value of the specified type
52 | export
53 | ifthenelse: { t: Type } -> Bool -> t -> t -> t
54 | ifthenelse True tbranch _ = tbranch
55 | ifthenelse False _ fbranch = fbranch
56 |
--------------------------------------------------------------------------------
/mylang/src/command.idr:
--------------------------------------------------------------------------------
1 | module command
2 |
3 | import variable
4 | import nat
5 | import expression
6 | import state
7 | import ifthenelse
8 |
9 | -----------------------------------------------------
10 | ----------------- SYNTAX ----------------------------
11 | -----------------------------------------------------
12 |
13 | ||| The Command type defines the forms of programs in our language
14 | ||| The syntax of a little imperative programming language!
15 | public export
16 | data Command =
17 | Skip |
18 | NatAssign (Variable Nat) NatExpr |
19 | BoolAssign (Variable Bool) BoolExpr |
20 | IfThenElse BoolExpr Command Command | -- Whoa! Inductive
21 | While BoolExpr Command | -- Whoa! Iductive
22 | Seq Command Command -- Whoa! Inductive
23 |
24 |
25 | -----------------------------------------------------
26 | --------------- OPERATIONAL SEMANTICS! --------------
27 | -----------------------------------------------------
28 |
29 | ||| CommandEval is a function that takes a Command and a state
30 | ||| in which the evaluate it, evaluates it starting in that state,
31 | ||| and returns the resulting state. This is what it means to evaluate
32 | ||| an imperative program. (We aren't considering effects, such as I/O).
33 | export
34 | CommandEval: Command -> State -> State
35 |
36 | -- Evaluating the skip command in a state st returns st unchanged
37 | CommandEval Skip st =
38 | st
39 |
40 | -- Evaluating an assignment v = e in state st returns the overridden state
41 | CommandEval (NatAssign v e) st =
42 | state_override_nat
43 | st
44 | v
45 | (natExprEval e st)
46 |
47 | -- Evaluating an assignment v = e in state st returns the overridden state
48 | CommandEval (BoolAssign v e) st =
49 | state_override_bool
50 | st
51 | v
52 | (boolExprEval e st)
53 |
54 | -- Evaluating IfThenElse command reduces to evaluating true or false branch
55 | CommandEval (IfThenElse cond tcmd fcmd) st =
56 | ifthenelse
57 | (boolExprEval cond st)
58 | (CommandEval tcmd st)
59 | (CommandEval fcmd st)
60 |
61 | -- Evaluating a While does nothing if the condition is False
62 | -- Otherwise it repeats the while loop in the state produced by
63 | -- executing the body of the command in the start at the start
64 | CommandEval (While cond cmd) st =
65 | ifthenelse
66 | (boolExprEval
67 | cond
68 | st)
69 | (CommandEval
70 | (While cond cmd)
71 | (CommandEval
72 | cmd
73 | st))
74 | (st)
75 |
76 |
77 | -- Evaluating a sequential composition evaluates the second command
78 | -- in the state produceD by evaluating the first in the given state st
79 | CommandEval (Seq c1 c2) st =
80 | (CommandEval
81 | c2
82 | (CommandEval
83 | c1
84 | st))
85 |
--------------------------------------------------------------------------------
/mylib/src/mylib_doc/docs/bit.html:
--------------------------------------------------------------------------------
1 |
2 | IdrisDoc: bit
--------------------------------------------------------------------------------
/mylib/src/byte8_hw.idr:
--------------------------------------------------------------------------------
1 | ||| Abstract data type representing two-bit byte
2 | module byte8
3 |
4 | {-
5 | A byte aggregates several bits into a tuple of bits.
6 | The bits are essential, thus visible, parts of a byte.
7 | Note that this is a comment. You cannot attach inline
8 | documentation to an import directive.
9 | -}
10 | import public bit
11 | import public nat
12 | import ifthenelse
13 |
14 | ||| A two-bit byte type; constructor is private
15 | export
16 | data Byte8 =
17 | ||| Constructor that boxes four Bit values into a Byte8 value
18 | MkByte8 Bit Bit Bit Bit Bit Bit Bit Bit
19 |
20 |
21 | ||| Pack four bits into, and return, a byte (a 8-tuple of bits)
22 | export
23 | byte8_new: Bit -> Bit -> Bit -> Bit -> Bit -> Bit -> Bit -> Bit -> Byte8
24 | byte8_new b7 b6 b5 b4 b3 b2 b1 b0 = ?byte8_new_hole
25 |
26 |
27 | -- Constant byte8 values filled with zeros and ones, resp.
28 |
29 | export
30 | byte8_zeros: Byte8
31 | byte8_zeros = ?byte8_zeros_hole
32 |
33 |
34 | export
35 | byte8_ones: Byte8
36 | byte8_ones = ?byte8_ones_hole
37 |
38 | -- Projection functions, returning particular component bits
39 |
40 | ||| Return Bit 0
41 | export
42 | byte8_bit0: Byte8 -> Bit
43 | byte8_bit0 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b0
44 |
45 | ||| Return Bit 1
46 | export
47 | byte8_bit1: Byte8 -> Bit
48 | byte8_bit1 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b1
49 |
50 | ||| Return Bit 2
51 | export
52 | byte8_bit2: Byte8 -> Bit
53 | byte8_bit2 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b2
54 |
55 | ||| Return Bit 3
56 | export
57 | byte8_bit3: Byte8 -> Bit
58 | byte8_bit3 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b3
59 |
60 |
61 | ||| Return Bit 3
62 | export
63 | byte8_bit4: Byte8 -> Bit
64 | byte8_bit4 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b4
65 |
66 |
67 | ||| Return Bit 3
68 | export
69 | byte8_bit5: Byte8 -> Bit
70 | byte8_bit5 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b5
71 |
72 |
73 | ||| Return Bit 3
74 | export
75 | byte8_bit6: Byte8 -> Bit
76 | byte8_bit6 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b6
77 |
78 |
79 | ||| Return Bit 3
80 | export
81 | byte8_bit7: Byte8 -> Bit
82 | byte8_bit7 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b7
83 |
84 |
85 | -- arithmetic functions on 8-bit bytes
86 |
87 | -- natural numbers we'll need
88 | nat_four: Nat
89 | nat_four = nat_succ nat_three
90 |
91 | nat_five: Nat
92 | nat_five = nat_succ nat_four
93 |
94 | nat_six: Nat
95 | nat_six = nat_succ nat_five
96 |
97 | nat_seven: Nat
98 | nat_seven = nat_succ nat_six
99 |
100 | ||| Return the indexed bit of the given byte or B0 if the index is out of range
101 | export
102 | byte8_sub: Nat -> Byte8 -> Bit
103 | byte8_sub index aByte8 = ?byte8_sub_hole
104 |
105 |
106 | ||| Return the carry bit in the indexed column when adding the two bytes
107 | ||| or the zero bit (B0) if the index is out of range
108 | carry_sub8: Nat -> Byte8 -> Byte8 -> Bit
109 | carry_sub8 n b1 b0 = ?carry_sub8_hole
110 |
111 |
112 | ||| Return the indexed bit of the sum of the given bytes
113 | sum_sub8: Nat -> Byte8 -> Byte8 -> Bit
114 | sum_sub8 n b1 b0 = ?sub_sub8_hole
115 |
116 |
117 | ||| Return the sum of two 8-bit bytes as a 8-bit byte ignoring overflows
118 | export
119 | byte8_plus: Byte8 -> Byte8 -> Byte8
120 | byte8_plus b0 b1 = ?byte8_plus_hole
121 |
122 |
123 | -- a little test case
124 | n: Byte8
125 | n = byte8_plus byte8_ones byte8_ones
126 | -- expect 11111110
127 |
--------------------------------------------------------------------------------
/mylib/src/nat.idr:
--------------------------------------------------------------------------------
1 | ||| An abstract data type, nat, simulating
2 | ||| the natural numbers and common arithmetic
3 | ||| operations involving natural numbers
4 | module nat
5 |
6 | -- Some arithmetic functions return Boolean values; so
7 | -- clients will need bool, so we'll import it publicly
8 | import public bool
9 | import eq
10 |
11 |
12 | ||| The primary data type we export is Nat.
13 | ||| The constructors themselves are private.
14 | export
15 | data Nat =
16 | ||| Z constructs a representation of zero
17 | Z |
18 | ||| S n constructs a representation of the successor of n
19 | S Nat
20 |
21 | -- Values and functions
22 |
23 | ||| The Nat representing zero
24 | export
25 | nat_zero: Nat
26 | nat_zero = Z
27 |
28 |
29 | ||| The one representing one
30 | export
31 | nat_one: Nat
32 | nat_one = S Z
33 |
34 |
35 | ||| And this one's two
36 | export
37 | nat_two: Nat
38 | nat_two = S nat_one
39 |
40 |
41 | ||| And three
42 | export
43 | nat_three: Nat
44 | nat_three = S nat_two
45 |
46 |
47 | ||| The identity function for values of type Nat
48 | export
49 | nat_id: Nat -> Nat
50 | nat_id n = n
51 |
52 |
53 | ||| Return the (Nat representing the) successor of a given Nat
54 | export
55 | nat_succ: Nat -> Nat
56 | nat_succ n = S n
57 |
58 | -- Oh, Huge Yay, and Finally: Arithmetic!
59 |
60 | ||| Compute the predecessor of a given nat (with pred zero = zero)
61 | export
62 | nat_pred: Nat -> Nat
63 | nat_pred Z = Z
64 | nat_pred (S n) = n
65 |
66 |
67 | ||| Implement a function for computing the sum of two nats
68 | export
69 | nat_plus: Nat -> Nat -> Nat
70 | nat_plus Z n = n
71 | nat_plus (S n) m = S (nat_plus n m)
72 |
73 |
74 | ||| Subtraction
75 | export
76 | nat_minus: Nat -> Nat -> Nat
77 | nat_minus Z m = Z
78 | nat_minus m Z = m
79 | nat_minus (S m) (S n) = nat_minus m n
80 |
81 |
82 | ||| Return true if a given nat is even otherwise false
83 | export
84 | nat_evenb: Nat -> Bool
85 | nat_evenb Z = True
86 | nat_evenb (S Z) = False
87 | nat_evenb (S (S n)) = nat_evenb n
88 |
89 | ||| Natural number equality
90 | export
91 | nat_eq: Nat -> Nat -> Bool
92 | nat_eq Z Z = True
93 | nat_eq Z (S n) = False
94 | nat_eq (S n) Z = False
95 | nat_eq (S n) (S m) = nat_eq n m
96 |
97 |
98 | public export
99 | implementation Eq Nat where
100 | eq n m = nat_eq n m
101 | neq n m = bool_not (eq n m)
102 |
103 |
104 | ||| Compute the product of two nats
105 | export nat_mult: Nat -> Nat -> Nat
106 | nat_mult Z m = Z
107 | nat_mult (S n) m = nat_plus m (nat_mult n m)
108 |
109 |
110 | ||| Return the factorial of a given nat
111 | ||| Recall: if n = 0, n! = 1 and if n > 0, n! = n * (n-1)!
112 | export
113 | nat_fact: Nat -> Nat
114 | nat_fact Z = nat_succ nat_zero
115 | nat_fact (S n) = nat_mult (S n) (nat_fact n)
116 |
117 | ||| Return True if n1 <= n2 otherwise False
118 | export
119 | nat_le: Nat -> Nat -> Bool
120 | nat_le Z Z = True
121 | nat_le (S n) Z = False
122 | nat_le Z (S n) = True
123 | nat_le (S n) (S m) = nat_le n m
124 |
125 | ||| Return True if n1 is strictly less than n2
126 | export
127 | nat_lt: Nat -> Nat -> Bool
128 | nat_lt n1 n2 =
129 | bool_and
130 | (nat_le n1 n2)
131 | (bool_not (nat_eq n1 n2))
132 |
133 | ||| Return True if n1 is strictly greater than n2 else False
134 | export
135 | nat_gt: Nat -> Nat -> Bool
136 | nat_gt n1 n2 = bool_not (nat_le n1 n2)
137 |
138 | ||| Return true if n1 >= n2 else False
139 | export
140 | nat_ge: Nat -> Nat -> Bool
141 | nat_ge n1 n2 =
142 | bool_or
143 | (nat_gt n1 n2)
144 | (nat_eq n1 n2)
145 |
--------------------------------------------------------------------------------
/mylib/src/bool.idr:
--------------------------------------------------------------------------------
1 | ||| Abstract data type simulating Boolean algebra
2 | module bool
3 |
4 | ||| A data type the values of which represent Boolean true and false
5 | public export
6 | data Bool =
7 | ||| Term representing Boolean true
8 | True |
9 | ||| Term representing Boolean false
10 | False
11 |
12 | {-
13 | Note that here we've made the judgment that it's ok to export
14 | the two constructors for direct use by clients. They're simple
15 | and already perfectly named, so we just let clients use them.
16 | The public export makes the constructors as well as the Bool
17 | type visible in clients that import this module.
18 | -}
19 |
20 |
21 | -- Unary functions
22 |
23 | ||| Representation of identity function on Boolean values
24 | export
25 | bool_id: Bool -> Bool
26 | bool_id b = b
27 |
28 |
29 | ||| Representation of negation function on Boolean values
30 | export
31 | bool_not: Bool -> Bool
32 | bool_not True = False
33 | bool_not _ = True
34 |
35 |
36 | ||| Representation of constant true function of one Bool
37 | export
38 | bool_true: Bool -> Bool
39 | bool_true _ = True
40 |
41 |
42 | ||| Representation of constant false function of one Bool
43 | export
44 | bool_false: Bool -> Bool
45 | bool_false _ = False
46 |
47 | -- Binary functions
48 |
49 | ||| Representation of Boolean "and" function
50 | export
51 | bool_and: Bool -> Bool -> Bool
52 | bool_and True True = True
53 | bool_and True False = False
54 | bool_and False True = False
55 | bool_and False False = False
56 |
57 |
58 | ||| Representation of Boolean "or" function
59 | export
60 | bool_or: Bool -> Bool -> Bool
61 | bool_or False False = False
62 | bool_or _ _ = True
63 |
64 |
65 | ||| Representation of Boolean "nand" function
66 | export
67 | bool_nand: Bool -> Bool -> Bool
68 | bool_nand b1 b2 = bool_not (bool_and b1 b2)
69 |
70 |
71 | ||| Representation of Boolean "xor" function
72 | export
73 | bool_xor: Bool -> Bool -> Bool
74 | bool_xor True True = False
75 | bool_xor False False = False
76 | bool_xor _ _ = True
77 |
78 |
79 | ||| Representation of the Boolean implies function
80 | export
81 | bool_implies: Bool -> Bool -> Bool
82 | bool_implies True False = False
83 | bool_implies _ _ = True
84 |
85 |
86 | ||| Representation of the Boolean equiv function
87 | export
88 | bool_equiv: Bool -> Bool -> Bool
89 | bool_equiv True True = True
90 | bool_equiv False False = True
91 | bool_equiv _ _ = False
92 |
93 | ||| Boolean equality
94 | export
95 | bool_eq: Bool -> Bool -> Bool
96 | bool_eq b1 b2 = bool_equiv b1 b2
97 |
98 |
99 | ||| Representation of if-then-else returning a Bool
100 | export
101 | bool_if_then_else: Bool -> Bool -> Bool -> Bool
102 | bool_if_then_else True t _ = t
103 | bool_if_then_else False _ f = f
104 |
105 | {-
106 | Here it'd be great to check to see that our data type
107 | really does simulate properties of "The Booleans." For
108 | example, we might want to check that the following is
109 | true: if b is any value of type Bool, then (bool_equiv
110 | b (bool_not (bool_not b))) is true. We could do this by
111 | exhaustive testing in this case. But the approach that
112 | is needed when exhaustive testing is infeasible is what
113 | we call theorem proving. A theorem is just a type and a
114 | proof is just a value of that type. Here's an example:
115 |
116 | ||| Theorem: bool_not cancels itself (it's "involutive")
117 |
118 |
119 | doubleNeg: (b: Bool) ->
120 | (bool_equiv
121 | (bool_not (bool_not b))
122 | b) = true
123 | doubleNeg = ?dnProof
124 | -}
125 |
--------------------------------------------------------------------------------
/mylang/src/stateTest.idr:
--------------------------------------------------------------------------------
1 | module stateTest
2 |
3 | import state
4 | import variable
5 | import nat
6 | import variableTest
7 |
8 |
9 |
10 | --
11 |
12 | -- Again, the way to think about nat_state_init is that
13 | -- (as a function) it's like a two-column table with Variables
14 | -- in the first column and corresponding values, here all zero,
15 | -- in the second column.
16 |
17 | -- Override updates such a state one variable at a time.
18 | -- Suppose for example that we want a new state in which
19 | -- all variables have corresponding value zero except for
20 | -- variable #2 (MkVariable nat_two), which we now want to
21 | -- map to have one as its corresponding value. We can use
22 | -- override to produce the desired new state like this:
23 | nat_state_new: StateT Nat
24 | nat_state_new = override_t nat_state_init Z nat_one
25 |
26 | -- Now in this *new* state, Variable #2 has the value one,
27 | -- where in the init state it had value zero.
28 | nval': Nat
29 | nval' = nat_state_new Z
30 |
31 |
32 | ---------
33 |
34 | -- TEST
35 |
36 | b01: Bool
37 | b01 = bool_state_init A
38 |
39 | b02: Bool
40 | b02 = bool_state_init B
41 |
42 | b03: Bool
43 | b03 = bool_state_init C
44 |
45 | -- Assignment: A = True
46 |
47 | bs: t_state Bool
48 | bs = override bool_state_init A True
49 |
50 | -- Assignment transformed the state to a new state
51 | -- In this new state, all's the same but for A = True
52 |
53 | b11: Bool
54 | b11 = bs A
55 |
56 | b12: Bool
57 | b12 = bs B
58 |
59 | b13: Bool
60 | b13 = bs C
61 |
62 | -- Assignment: C = 3 starting in previous state
63 |
64 | bs': t_state Bool
65 | bs' = override bs C True
66 |
67 | -- In the new state, both A = True and C = True
68 |
69 | b21: Bool
70 | b21 = bs' A
71 |
72 | b22: Bool
73 | b22 = bs' B
74 |
75 | b23: Bool
76 | b23 = bs' C
77 |
78 |
79 | -- A applying state functions to variables gives values
80 |
81 | bval: Bool
82 | bval = bool_state_init C -- C is the Variable (MkVariable nat_two)
83 | -- expect False
84 |
85 | nval: Nat
86 | nval = nat_state_init Z -- Z is the Variable (MkVariable nat_two)
87 | -- expect zero
88 |
89 |
90 |
91 | -- refactored from state.Idris
92 | -- Examples (refactor)
93 |
94 | -- Here's an example of a function of type (StateT Bool)
95 | aBoolFunc: Variable Bool -> Bool
96 | aBoolFunc v = False
97 |
98 | -- StateT Bool *means* Variable Bool -> Bool, so we can
99 | -- assign a function of this type to a variable of type
100 | -- StateT Bool. This is just a demonstration of this idea.
101 | aBoolState: StateT Bool
102 | aBoolState = aBoolFunc
103 |
104 | -- Often we don't want to have to write a named function,
105 | -- such as boolFunc, to assign a function value to a variable.
106 | -- In these cases we can use an "anonymous function" instead.
107 | aBoolState': StateT Bool
108 | aBoolState' = \v: (Variable Bool) => False
109 | -- This says that the value of aBoolState' is "a function that
110 | -- takes an argument, v, of type (Variable Bool) and that always
111 | -- returns False."" One could write a more complex return result
112 | -- expression, just as with any function definition. anonymous
113 | -- functions are essential in writing functions that return other
114 | -- functions. We specify the return result as an anonymous function.
115 | -- Note that in this case, boolState' is absolutely equivalent to
116 | -- boolState as we have defined them.
117 |
118 | -- We can also define a similar mapping from variables to Natural
119 | -- numbers. Here we allow Idris to infer the type of v in the
120 | -- anonymous function expression. Be sure you read that expression
121 | -- as "a function that takes a variable, v (of type Variable Nat),"
122 | -- and that always returns zero. Visualize a table with variables in
123 | -- the left column and a zero for each variable in the right column.
124 | aNatState: StateT Nat
125 | aNatState = \v => nat_zero -- note the use of type inference!
126 |
--------------------------------------------------------------------------------
/mylib/src/byte4.idr:
--------------------------------------------------------------------------------
1 | ||| Abstract data type representing two-bit byte
2 | module byte4
3 |
4 | {-
5 | A byte aggregates several bits into a tuple of bits.
6 | The bits are essential, thus visible, parts of a byte.
7 | Note that this is a comment. You cannot attach inline
8 | documentation to an import directive.
9 | -}
10 | import public bit
11 | import public nat
12 | import ifthenelse
13 |
14 | ||| A two-bit byte type; constructor is private
15 | export
16 | data Byte4 =
17 | ||| Constructor that boxes four Bit values into a Byte4 value
18 | MkByte4 Bit Bit Bit Bit
19 |
20 |
21 | ||| Pack four bits into, and return, a byte (a 4-tuple of bits)
22 | export
23 | byte4_new: Bit -> Bit -> Bit -> Bit -> Byte4
24 | byte4_new b3 b2 b1 b0 = MkByte4 b3 b2 b1 b0
25 |
26 |
27 | -- Constant byte4 values filled with zeros and ones, resp.
28 |
29 | export
30 | byte4_zeros: Byte4
31 | byte4_zeros = byte4_new B0 B0 B0 B0
32 |
33 |
34 | export
35 | byte4_ones: Byte4
36 | byte4_ones = byte4_new B1 B1 B1 B1
37 |
38 | -- Projection functions, returning particular component bits
39 |
40 | ||| Projection function returning rightmost bit
41 | export
42 | byte4_bit0: Byte4 -> Bit
43 | byte4_bit0 (MkByte4 b3 b2 b1 b0) = b0
44 |
45 | ||| Projection function returning next bit
46 | export
47 | byte4_bit1: Byte4 -> Bit
48 | byte4_bit1 (MkByte4 b3 b2 b1 b0) = b1
49 |
50 | ||| Projection function returning next bit
51 | export
52 | byte4_bit2: Byte4 -> Bit
53 | byte4_bit2 (MkByte4 b3 b2 b1 b0) = b2
54 |
55 | ||| Projection function returning leftmost (high-order) bit
56 | export
57 | byte4_bit3: Byte4 -> Bit
58 | byte4_bit3 (MkByte4 b3 b2 b1 b0) = b3
59 |
60 | byte4_eq: Byte4 -> Byte4 -> Bool
61 | byte4_eq (MkByte4 b13 b12 b11 b10) (MkByte4 b03 b02 b01 b00) =
62 | bool_and
63 | (bit_eq b13 b03)
64 | (bool_and
65 | (bit_eq b12 b02)
66 | (bool_and
67 | (bit_eq b11 b01)
68 | (bit_eq b10 b00)))
69 |
70 |
71 | -- arithmetic functions on 4-bit bytes
72 |
73 | ||| addition
74 | byte4_plus: Byte4 -> Byte4 -> Byte4
75 | byte4_plus (MkByte4 b13 b12 b11 b10) (MkByte4 b03 b02 b01 b00) =
76 | MkByte4
77 | (bit_plus3
78 | b13
79 | b03
80 | (bit_carry3
81 | b12
82 | b02
83 | (bit_carry3
84 | b11
85 | b01
86 | (bit_carry3
87 | B0
88 | b10
89 | b00))))
90 | (bit_plus3
91 | b12
92 | b02
93 | (bit_carry3
94 | b11
95 | b01
96 | (bit_carry
97 | b10
98 | b00)))
99 | (bit_plus3
100 | b11
101 | b01
102 | (bit_carry
103 | b10
104 | b00))
105 | (bit_plus3
106 | B0
107 | b10
108 | b00)
109 |
110 | -- A better implementation
111 |
112 | ||| Return the indexed bit of the given byte or B0 if the index is out of range
113 | export
114 | byte4_sub: Nat -> Byte4 -> Bit
115 | byte4_sub index aByte4 =
116 | ifthenelse
117 | (nat_eq index nat_zero)
118 | (byte4_bit0 aByte4)
119 | (ifthenelse
120 | (nat_eq index nat_one)
121 | (byte4_bit1 aByte4)
122 | (ifthenelse
123 | (nat_eq index nat_two)
124 | (byte4_bit2 aByte4)
125 | (ifthenelse
126 | (nat_eq index nat_three)
127 | (byte4_bit3 aByte4)
128 | (B0))))
129 |
130 |
131 | ||| Return the carry bit in the indexed column when adding the two bytes
132 | ||| or the zero bit (B0) if the index is out of range
133 | carry_sub: Nat -> Byte4 -> Byte4 -> Bit
134 | carry_sub n b1 b0 =
135 | ifthenelse
136 | (nat_eq n nat_zero)
137 | (B0)
138 | (bit_carry3
139 | (carry_sub (nat_pred n) b1 b0)
140 | (byte4_sub (nat_pred n) b1)
141 | (byte4_sub (nat_pred n) b0))
142 |
143 |
144 | ||| Return the indexed bit of the sum of the given bytes
145 | sum_sub: Nat -> Byte4 -> Byte4 -> Bit
146 | sum_sub n b1 b0 =
147 | bit_plus3
148 | (carry_sub n b1 b0)
149 | (byte4_sub n b1)
150 | (byte4_sub n b0)
151 |
152 |
153 | ||| Return the sum of two 4-bit bytes as a 4-bit byte ignoring overflows
154 | export
155 | byte4_plus': Byte4 -> Byte4 -> Byte4
156 | byte4_plus' b0 b1 =
157 | byte4_new
158 | (sum_sub nat_three b1 b0)
159 | (sum_sub nat_two b1 b0)
160 | (sum_sub nat_one b1 b0)
161 | (sum_sub nat_zero b1 b0)
162 |
163 |
164 | -- a little test case
165 | n: Byte4
166 | n = byte4_plus' byte4_ones byte4_ones
167 | -- expect 1110
168 |
--------------------------------------------------------------------------------
/mylib/src/byte4_plus.txt:
--------------------------------------------------------------------------------
1 | The problem that we're now solving is to construct an implementation of a
2 | function, byte4_sum, that takes two 4-bit bytes and returns a 4-bit byte
3 | representing the sum while simply discarding any high-order overflow bit.
4 |
5 | With the addition of natural numbers and the concept of recursive functions
6 | to our toolkit (through the nat module), We're now in a position to take on
7 | this problem.
8 |
9 | Our overall strategy for constructing a satisfactory implementation will be
10 | top-down functional decomposition, realized by systematically introducing,
11 | and then constructing code to fill, holes in Idris code.
12 |
13 | We already have our implementation of the top-level byte4_plus function.
14 | We're done at that level. Here's the function implementation.
15 |
16 | ========
17 |
18 | ||| addition
19 | export
20 | byte4_plus': Byte4 -> Byte4 -> Byte4
21 | byte4_plus' b0 b1 =
22 | byte4_new
23 | (sum_sub nat_three b1 b0)
24 | (sum_sub nat_two b1 b0)
25 | (sum_sub nat_one b1 b0)
26 | (sum_sub nat_zero b1 b0)
27 |
28 | ========
29 |
30 | This function implementation uses a lower-level function called sum_sub.
31 | The sum_sub function takes an index (a natural number) and two 4-bit bytes
32 | and that returns the indexed bits in the two given bytes. If the index is
33 | too large for the number of bits in the bytes, the function will return B0.
34 | That's it!
35 |
36 | We make the code compile by declaring the type of sum_sub and by binding
37 | it to a hole that we will then have to refine into working code. The key
38 | when we introduce the hole is that we understand precisely what that code
39 | will eventually have to do. In the case of sum_sub, for example, it must
40 | take an index and two bytes and return the index-designated bit in the sum
41 | of the two bytes.
42 |
43 | Now we just recurse! That is, we apply the same strategy at 'the next level
44 | down." To be clear, we need an implementation of sum_sub, and we can use the
45 | same tricks in constructing it. It's jobs is pretty simple if you look at the
46 | diagram of byte addition: the job of sum_sub is to add the three bits in one
47 | column of a byte addition problem: the carry value from the previous column
48 | and the two bits in the present column. That's it.
49 |
50 | So now we write the code; and once again we write it to clearly express the
51 | essence of the problem we're solving, introducing yet lower-level functions
52 | as might be required to achieve our aim of clarity, precision, elegance and
53 | of course logical and mathematical correctness.
54 |
55 | Following next is the code, including the use of two functions that we have
56 | yet to implement. The first is carry_sub. The second is byte4_sub.
57 |
58 | The carry_sub function takes an index n and two bytes and returns the carry
59 | bit in the indexed column. The byte4_sub function takes an index and a bytes
60 | and returns the indexed bit of the given byte. If indices are too big, these
61 | functions return the zero bit (B0).
62 |
63 | ========
64 |
65 | -- compute the indexed bit of the sum of the given bytes
66 | sum_sub: Nat -> Byte4 -> Byte4 -> Bit
67 | sum_sub n b1 b0 =
68 | bit_plus3
69 | (carry_sub n b1 b0)
70 | (byte4_sub n b1)
71 | (byte4_sub n b0)
72 |
73 | ========
74 |
75 | It's important to grasp that when applying the method of top-down functional
76 | decomposition, can simply assume that we have the lower-level functions that
77 | we need ("wish we had") to make our code easy to write and understand. We go
78 | back and fill in the hole later. In this way, we maintain clarity and control
79 | at each level of decomposition. Here's the stub code for our present problem.
80 |
81 | ========
82 |
83 | ||| Return the indexed bit of the given byte or B0 if the index is out of range
84 | export
85 | byte4_sub: Nat -> Byte4 -> Bit
86 | byte4_sub n b4 = ?byte4_sub_hole
87 | -- this function is stubbed out; the hole must be filled!
88 |
89 |
90 | ||| Return the carry bit in the indexed column when adding the two bytes
91 | ||| or the zero bit (B0) if the index is out of range
92 | carry_sub: Nat -> Byte4 -> Byte4 -> Bit
93 | carry_sub n b1 b0 = ?carry_sub_hole
94 | -- this function is stubbed out; the hole must be filled!
95 |
96 | ========
97 |
98 | Now all we have to do is resolve the remaining holes. You can find working code
99 | as described here in my github repo, under tag v4.1
100 |
--------------------------------------------------------------------------------
/mylang/src/commandTest.idr.sav:
--------------------------------------------------------------------------------
1 | module langTest
2 |
3 | import command
4 | import nat
5 | import variableTest -- defines variables X, Y, Z
6 | import variable
7 | import expression
8 | import state
9 |
10 | -- Tests: Programs are just values of this data type!
11 |
12 | -------------------------------------------
13 | ---- putting together a program for sum-1-X
14 | -------------------------------------------
15 |
16 | st: State
17 | st = init_state
18 |
19 | {-
20 | In Python, the code looks like this:
21 |
22 | x = 5
23 | y = 0
24 | while x != 0:
25 | y = y + x
26 | x = x - 1
27 | -}
28 |
29 | -- X = 5
30 | xGetsFive: Command
31 | xGetsFive = NatAssign
32 | X
33 | (NatLitExpr (nat_succ (nat_succ nat_three)))
34 |
35 |
36 | st': State
37 | st' = CommandEval xGetsFive st
38 |
39 |
40 | -- Y = 0
41 | yGetsZero: Command
42 | yGetsZero = NatAssign
43 | Y
44 | (NatLitExpr nat_zero)
45 |
46 |
47 | st'': State
48 | st'' = CommandEval yGetsZero st'
49 |
50 |
51 | -- X != 0
52 | Xnot0: BoolExpr
53 | Xnot0 = BoolNeqExpr
54 | (NatVarExpr X)
55 | (NatLitExpr nat_zero)
56 |
57 |
58 |
59 | -- Y = Y + X
60 | accumXinY: Command
61 | accumXinY = NatAssign
62 | Y
63 | (NatPlusExpr
64 | (NatVarExpr Y)
65 | (NatVarExpr X))
66 |
67 | st''': State
68 | st''' = CommandEval accumXinY st''
69 |
70 | -- X = X - 1
71 | decrX: Command
72 | decrX = NatAssign
73 | X
74 | (NatMinusExpr (
75 | NatVarExpr X)
76 | (NatLitExpr nat_one))
77 |
78 |
79 |
80 | -- acculumate x in y decrementing x until x = 0
81 | iterateSum: Command
82 | iterateSum = While
83 | Xnot0
84 | (Seq
85 | accumXinY
86 | decrX)
87 |
88 |
89 |
90 | -- initialize X = 5, then run the loop
91 | sumOneToFive: Command
92 | sumOneToFive =
93 | Seq
94 | xGetsFive
95 | (Seq
96 | yGetsZero
97 | iterateSum)
98 | -- now (st X) = 0 and (st Y) = result
99 |
100 |
101 | --------------------------------------
102 | ---- now we run (evaulate) our program
103 | --------------------------------------
104 |
105 | -- the answer will be in Y in the resulting state
106 | st'''': State
107 | st'''' = CommandEval sumOneToFive init_state
108 |
109 |
110 | -----------------------------------------------------
111 | -------- separate examples of other commands --------
112 | -----------------------------------------------------
113 |
114 | -- Example of ...
115 | -- if X != 0:
116 | -- X = 5
117 | condCmd: Command
118 | condCmd = IfThenElse
119 | Xnot0
120 | xGetsFive -- improve this kjs
121 | Skip -- improve this kjs
122 |
123 |
124 |
125 | ------------------------------------
126 | ------------ older stuff -----------
127 | ------------------------------------
128 |
129 | -- Do nothing but skip
130 | prog0: Command
131 | prog0 = Skip
132 |
133 | {-
134 | s0: State
135 | s0 = run prog0 state_init
136 | -}
137 |
138 | -- A single assignment
139 | prog1: Command
140 | prog1 = NatAssign X (NatLitExpr nat_two)
141 |
142 | {-
143 | s1: State
144 | s1 = run prog1 state_init
145 | -- expect s1 X = 2, s1 Y = 0, s1 Z = 0
146 |
147 | -- An assignment in the state produced by the last assignment
148 | s2: State
149 | s2 = run (Assign Y nat_two) s1
150 | -- expect s2 X = 2, s2 Y = 2, s2 Z = 0
151 | -}
152 |
153 | -- A sequential composition of assignments
154 | prog2: Command
155 | prog2 =
156 | Seq
157 | (NatAssign X (NatLitExpr nat_one))
158 | (NatAssign Y (NatLitExpr nat_two))
159 |
160 | {-
161 | s3: State
162 | s3 = run prog2 state_init
163 | -- expect same result as s2
164 | -}
165 |
166 | -- A sequential composition of an assignment with a larger program
167 | prog3: Command
168 | prog3 =
169 | Seq
170 | (NatAssign X (NatLitExpr nat_one))
171 | (Seq
172 | (NatAssign Y (NatLitExpr nat_two))
173 | (NatAssign Z (NatLitExpr nat_three)))
174 |
175 | {-
176 | s4: State
177 | s4 = run prog3 state_init
178 | -}
179 |
180 | -- NEW TEST CASES USING EXPRESSIONS
181 |
182 | -- like "X = Y" in Python
183 | prog4: Command
184 | prog4 = NatAssign X (NatVarExpr Y)
185 |
186 | -- like "X = Y + 1" in Python
187 | prog5: Command
188 | prog5 =
189 | NatAssign
190 | X
191 | (NatPlusExpr
192 | (NatVarExpr Y)
193 | (NatLitExpr nat_one))
194 |
--------------------------------------------------------------------------------
/mylang/src/state.idr:
--------------------------------------------------------------------------------
1 | ||| A module defining variables, state as a functions
2 | ||| mapping variables to correspondng values, and a key
3 | ||| function, override, for changing a state (computing
4 | ||| a new one) by changing the previousl value of just
5 | ||| one variable.
6 | module state
7 |
8 | import dictionary
9 | import variable
10 | import nat
11 | import bool
12 | import ifthenelse
13 |
14 | {-
15 |
16 | Section #1: a little polymorphic subsystem for dealing
17 | with state-specific "assignment functions -- in particular
18 | for (Variable Nat) -> Nat and (Variable Bool) -> Bool. It
19 | defines values of reasonable initial states, with all Nat
20 | Variables assiged zero and all Bool Variables assigned
21 | False.
22 | -}
23 |
24 | ||| StateT is the type of function from (Variable t) to t
25 | ||| In our language we instantiate StateT with Nat and Bool
26 | ||| obtaining the types (Variable Nat) -> Nat and (Variable
27 | ||| Bool) -> Bool: the types of our type-specific state
28 | ||| functions.
29 | public export
30 | StateT: (t: Type) -> Type
31 | StateT t = (Variable t) -> t
32 |
33 |
34 | ||| override is a polymorphic function with an implicit
35 | ||| type parameter that yields a function that when given
36 | ||| a StateT function for that state along with a variable
37 | ||| and a value returns a new StateT function, the original
38 | ||| function with the specified variable-value override.
39 | export
40 | override_t: {a: Type} -> (s: StateT a) -> (var: Variable a) -> (val: a) -> StateT a
41 | override_t s var val =
42 | \v: Variable a =>
43 | ifthenelse
44 | (var_eq var v)
45 | val
46 | (s v)
47 |
48 | -- We export values that we designate as "initial states"
49 | -- for Boolean and Nat-valued variables. nat_state_init
50 | -- maps every (Variable Nat) to zero; and bool_state_init
51 | -- maps every Bool-valued Variable to False.
52 |
53 | ||| nat_state_init is the state-specific asignment function
54 | ||| that defines the "initial values" of all Nat variables
55 | ||| in our little language. In particular, nat_state_init is
56 | ||| the function that when given *any* Nat Variable in our
57 | ||| language returns the value assigned to that variable in
58 | ||| the state that the function represents.
59 | export
60 | nat_state_init: StateT Nat
61 | nat_state_init = \v: Variable Nat => nat_zero
62 |
63 |
64 | ||| Initial state for Bool Variables assigns False to
65 | ||| all Bool variables
66 | export
67 | bool_state_init: StateT Bool
68 | bool_state_init = \v: Variable Bool => False
69 |
70 | {-
71 | Section #2: Defining a pair-like type, State type,
72 | that we will use to package up a pair of type-specific
73 | state functions, of the kind just discussed, one for Nat
74 | and one for Bool, and a set of convenient operations on
75 | these state-function pairs.
76 | -}
77 |
78 | ||| The state of program execution in our system will
79 | ||| be a pair of type-specific state functions, one for
80 | ||| Nat Variables and one for Bool Variables.
81 | public export
82 | data State = MkState (StateT Bool) (StateT Nat)
83 |
84 | ||| given a State, getBoolState returns its StateT Bool
85 | ||| component: the function in the state that maps Bool
86 | ||| variabes to their assigned values
87 | export
88 | getBoolState: State -> (StateT Bool)
89 | getBoolState (MkState bs ns) = bs
90 |
91 | ||| given a State, getNatState returns its StateT Nat
92 | ||| component: the function in the state that maps Nat
93 | ||| Variabes to their assigned values
94 | export
95 | getNatState: State -> (StateT Nat)
96 | getNatState (MkState bs ns) = ns
97 |
98 |
99 | {-
100 | These functions override the appropriate type-specific
101 | assignement functions within the given state.
102 | -}
103 |
104 | ||| Override the Bool assignment function in the state
105 | export
106 | state_override_bool: State -> (Variable Bool) -> Bool -> State
107 | state_override_bool (MkState bs ns) var val =
108 | MkState (override_t bs var val) (ns)
109 |
110 |
111 | ||| Override the Nat assignment function in the state
112 | export
113 | state_override_nat: State -> (Variable Nat) -> Nat -> State
114 | state_override_nat (MkState bs ns) var val =
115 | MkState (bs) (override_t ns var val)
116 |
117 |
118 | ||| we define and export a state value, state_init, in which
119 | ||| all Nat Variables are assigned zero and all Bool Variables,
120 | ||| False
121 | export
122 | state_init: State
123 | state_init = MkState bool_state_init nat_state_init
124 |
125 | ||| init_state is redundant with state_init
126 | export
127 | init_state: State
128 | init_state = MkState bool_state_init nat_state_init
129 |
--------------------------------------------------------------------------------
/mylib/src/mylib_doc/docs/byte4.html:
--------------------------------------------------------------------------------
1 |
2 | IdrisDoc: byte4
--------------------------------------------------------------------------------
/mylib/src/exam2_key.idr:
--------------------------------------------------------------------------------
1 | module exam2
2 |
3 | import nat
4 | import bool
5 | import list
6 | import dictionary
7 | import pair
8 |
9 | -----------------------------------------
10 | -- #1: TUPLES AND PROJECTION FUNCTIONS --
11 | -----------------------------------------
12 |
13 | {-
14 | Here is a data type definition for a tuple type called FunTuple,
15 | where a value of this type represents a tuple with a first component
16 | of type Nat and a second component of type Bool.
17 | -}
18 |
19 | data Tuple = MkTuple Nat Bool
20 |
21 | -- Here's an example of a value of this type
22 |
23 | mt1: Tuple
24 | mt1 = MkTuple nat_one True
25 |
26 | {-
27 | Write two projection functions. The first, called tuple_nat, takes
28 | a Tuple and returns the value of its Nat component. The second, called
29 | tuple_bool, takes a Tuple and returns its Bool component. You need to
30 | fill in the types of these functions and their implementations.
31 | -}
32 |
33 | tuple_nat: Tuple -> Nat
34 | tuple_nat (MkTuple n _) = n
35 |
36 | tuple_bool: Tuple -> Bool
37 | tuple_bool (MkTuple _ b) = b
38 |
39 |
40 | -------------------
41 | -- #2: SUBSCRIPTING
42 | -------------------
43 |
44 | {-
45 | Refer back to our list module and in particular to the list_sub
46 | function. Remind yourself how it works. Now, given the following
47 | list of natural numbers, complete the expression using the list_sub
48 | function so that the value of aNat is set to the third element in
49 | the list (i.e., to nat_one). Remember that we start indexing at
50 | zero. You can use the REPL to make sure you've got it right.
51 | -}
52 |
53 | aList: List Nat
54 | aList = (Cons nat_three (Cons nat_two (Cons nat_one Nil)))
55 |
56 | aNat: Nat
57 | aNat = list_sub nat_two aList nat_zero
58 | -- Expect nat_one; be sure to check that you get the right answer
59 |
60 |
61 | ----------------------------
62 | -- #3: POLYMORPHIC FUNCTIONS
63 | ----------------------------
64 |
65 | -- Consider this function definition
66 |
67 | foo: { a: Type } -> (val: a) -> Nat
68 | foo v = nat_one
69 |
70 | {-
71 | In a sentence, state precisely what this function does. In
72 | a second sentence or two explain the purpose and the effect
73 | of the curly braces around "a: Type." Then write code to test
74 | the application of foo to the arguments nat_one and False,
75 | respectively. What is the result in each case?
76 | -}
77 |
78 | -- This function takes a single argument of any type and
79 | -- always returns the natural number 1 (nat_one).
80 |
81 |
82 | -----------------------------
83 | -- #4: POLYMORPHIC DATA TYPES
84 | -----------------------------
85 |
86 | {-
87 | Write a type constructor called Foo with two type arguments,
88 | a and b, and two constructors, Left a and Right b, and then
89 | write code showing how you would assign values of this type
90 | to variables, g and h, using the Left and Right constructors.
91 | Pick different types for a and b to make it more interesting.
92 | -}
93 |
94 | data Foo a b = Left a | Right b
95 |
96 | g: Foo Nat Bool
97 | g = Left nat_zero
98 |
99 | h: Foo Nat Bool
100 | h = Right False
101 |
102 |
103 |
104 | ---------------------------
105 | -- #5: INDUCTIVE DATA TYPES
106 | ---------------------------
107 |
108 | {-
109 | A "Tree of values of type, a,"" is either Empty or it is a
110 | Node with a value of type a and two smaller "Trees of values
111 | of type a". The Tree type is thus defined inducively.
112 |
113 | Write an Idris data type definition for a polymorphic type,
114 | Tree a, accordingly.
115 |
116 | Hint: It will have two constructors; you should call them Empty
117 | and Node; and the Node constructor will take three arguments.
118 | -}
119 |
120 | data Tree a = Empty | Node a (Tree a) (Tree a)
121 |
122 | {-
123 | Next, write Idris code that binds an empty tree of natural
124 | numbers to the variable, et, and a non-empty tree of natural
125 | numbers to the variable, ft. Your non-empty tree may have
126 | only one Node, though more would be ok as well.
127 | -}
128 |
129 | et: Tree Nat
130 | et = Empty
131 |
132 | ft: Tree Nat
133 | ft = Node nat_one Empty Empty
134 |
135 | --------------------------
136 | -- #6: RECURSIVE FUNCTIONS
137 | --------------------------
138 |
139 | {-
140 | Write the implementation of a recursive function called
141 | tree_size that takes a value of type (Tree a) and that
142 | returns the number of Nodes in the tree. Hint the number
143 | of Nodes in an Empty tree is zero, while the number of
144 | Nodes is a non-empty tree is one (for the current Node)
145 | plus the number of nodes in each of the two sub-trees.
146 | -}
147 |
148 | tree_size: Tree a -> Nat
149 | tree_size Empty = nat_zero
150 | tree_size (Node a left right) =
151 | nat_succ (nat_plus (tree_size left) (tree_size right))
152 |
153 | ------------------
154 | -- #7: Termination
155 | ------------------
156 |
157 | -- Part A. Why should you NOT evaluate badfun, below, in your REPL?
158 |
159 | badfun: Nat -> Nat
160 | badfun n = badfun n
161 |
162 | -- Evaluation of this function never terminates.
163 |
164 | {-
165 | Part B. Is it true that a function that calls itself recursively
166 | on the tail of a list argument will always terminate? Explain (as
167 | text inside this comment).
168 | -}
169 |
170 | -- It's true. Every list is finite in length. Therefore one takes
171 | -- recurse on its tail only a finite number of times before reaching
172 | -- Nil, for which an answer is produced without further recursion.
173 |
174 | -----------------------------
175 | -- #8: HIGHER-ORDER FUNCTIONS
176 | -----------------------------
177 |
178 | {-
179 | Write a function called applyfun that takes a function of type
180 | Nat -> Nat and a value of type Nat and that returns the result of
181 | applying the given function to the given value. Then write one
182 | test case showing that it works. Document the expected answer and
183 | use your REPL to confirm that that's the answer you're obtaining.
184 | -}
185 |
186 | applyfun: (Nat -> Nat) -> Nat -> Nat
187 | applyfun f n = f n
188 |
189 | n: Nat
190 | n = applyfun nat_succ nat_zero
191 | -- expect nat_one
192 |
193 |
194 |
195 | -----------------------------------------
196 | -- #9: THE MAP, FILTER AND FOLD FUNCTIONS
197 | -----------------------------------------
198 |
199 | {-
200 | Part A. Define a value of type (List Nat) representing the list,
201 | [1, 2, 3, 4]. Note: If you don't have a definition of nat_four,
202 | you can always apply nat_succ to a smaller value, such as nat_three,
203 | to construct the value you need.
204 | -}
205 |
206 | l: List Nat
207 | l = (Cons nat_one
208 | (Cons nat_two
209 | (Cons nat_three
210 | (Cons (nat_succ nat_three)
211 | Nil))))
212 |
213 | {-
214 | Part B: Define s to be a variable of type Nat, and use the
215 | list_fold_right to assign to s the value that is the sum of
216 | the elements of the list you just defined.
217 | -}
218 |
219 | s: Nat
220 | s = list_fold_right nat_plus nat_zero l
221 |
222 | {-
223 | Part C: Define q to be a variable of type (List Bool), and
224 | use the list_map function to assign to q a list of Bool values,
225 | one for each element of the list you defined, namely True if
226 | the element is even and False otherwise. Hint: use nat_evenb
227 | in your answer. Use the REPL to check the answer (False, True,
228 | False, True).
229 | -}
230 |
231 | q: List Bool
232 | q = list_map nat_evenb l
233 |
234 | ------------------------------
235 | -- #10: EXTRA CREDIT FOR AN A+
236 | ------------------------------
237 |
238 | {-
239 | Write code to bind to a variable d a value of type
240 | (Dictionary Nat Bool) containing the following three
241 | key-value pairs, followed by two test cases where you
242 | lookup the values for the keys zero and three respectively.
243 | Check that you're getting the expected answers. Here are
244 | the key-value pairs: 0:True, 1:False, 2:True. Note: You can
245 | write the code that produces the required dictionary in one
246 | big expression, or you can assign smaller dictionaries to
247 | other variables to build up to the dictionary you assign to
248 | the variable d. Final hint: dictionary_insert takes a
249 | dictionary as an argument but also returns a dictionary as
250 | a result. That returned dictionary can be an argument to
251 | another dictionary_insert operation.
252 | -}
253 |
254 | d: Dictionary Nat Bool
255 | d = dictionary_insert
256 | (dictionary_insert
257 | (dictionary_insert
258 | dictionary_new
259 | (MkPair nat_zero True))
260 | (MkPair nat_one False))
261 | (MkPair nat_two False)
262 |
263 | o1: Option Bool
264 | o1 = dictionary_lookup d nat_zero
265 |
266 | o2: Option Bool
267 | o2 = dictionary_lookup d nat_three
268 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CS1 Through Type Theory
2 |
3 | This repository records the progression of my CS1 course at UVa,
4 | Fall 2016. I informally call it "A Constructive Approach to CS1"
5 | or "CS1 Through Type Theory." It draws on lessons from Pierce's
6 | Software Foundations. With students it first constructs, step by
7 | step, starting from nothing but the core constructive logic of
8 | Idris, a "standard library" or "prelude" of the usual data types
9 | for functional programming (unit, bool, byte, nat, list a, etc).
10 | About 2/3 of the way through the semester, it builds a simple
11 | imperative language using this library, introducing types for
12 | state (variable -> value), and the syntax and evaluation of,
13 | both Boolean and arithmetic expressions and imperative commands
14 | (skip, assignment, ifthenelse, while, etc). That Idris allows for
15 | non-enforcement of totality is beneficial here, as otherwise it
16 | would not be possible to write a semantic evaluator as a function.
17 |
18 | At this point we also bring in Python to see how the fundamentals
19 | of imperative programming play out in an industrial language. We
20 | start decidedly *not* with "Hello World!" but with programs that
21 | create state and perform assignment operations. (We introduce the
22 | Python print statement here, for the first time giving students a
23 | capability that they haven't built entirely by themselves, for
24 | ease of inspecting program state.)
25 |
26 | The progression of concepts and mechanisms is "constructive" (in
27 | the math/logic sense). No ideas or capabilities are used in the
28 | first 2/3 of course that we don't build ourselves. We started
29 | with the idea that there are (data and function) (types and values).
30 | That's a two-by-two.
31 |
32 | We build through a progression of abstract data type modules, each
33 | comprising definitions of data and function types and values. We
34 | start with unit and bool types and run up through dictionaries
35 | implemented using lists and finally state and commands.
36 |
37 | State and commands come a little past 2/3 of the way through the
38 | semester. At that point, we bring in Python and guide students
39 | through the language with what we've learned in the foundational
40 | part of the class to understand it very rapidly: the basic data
41 | types of Python (variables, tuples, dictionaries, etc.), and the
42 | concept of commands as compositions of state transformers.
43 |
44 | The that this is a CS1 course, not an upper division or graduate
45 | course, imposes a need for very careful attention to the ordering
46 | and explication of concepts, from programming language mechanisms
47 | to software design concepts to functional and imperative paradigms,
48 | and careful separation of computation from effects.
49 |
50 | The contribution of this class, if there is one, is the analysis
51 | and design work, validated through several offerings of this class,
52 | to produce the specific progression of ideas that you'll find in
53 | this repo.
54 |
55 | By iterating through the tagged versions of this repository, you
56 | will animate the evolution of a simple Idris prelude library that
57 | the class and I built, followed by the construction of a simple
58 | imperative language. A class project required the extension of the
59 | language to support unsigned integer arithmetic (in limited form)
60 | based on our previously implemented Byte8 type and the algorithm
61 | we implemented (based on half- and full-adders) to do addition of
62 | natural numbers represented as 8-bit bytes.
63 |
64 | ## How to view this repository
65 |
66 | This repository presents a sequence of tagged versions of the
67 | prelude library that our class is building. On Github, click on
68 | "Master" for the master branch, then "Tags" to select a tagged
69 | version. You will then see the files from that version when you
70 | browse the directories on Github.
71 |
72 | Going through the sequence of versions in order will animate the
73 | introduction of concepts over the two months we spent building our
74 | prelude library.
75 |
76 | The first version picks up just a few lectures into the course.
77 | From there, minor version number changes roughly match daily lectures
78 | and specific concepts (such as programming mechanisms) within broader
79 | data-type-focused areas. Major version changes corresponding to
80 | transitions from one major abstract data type to another (unit and
81 | bool to nat, nat to list, list to dictionary, to state, etc). Note
82 | that some versions of some files contain more or less extensive
83 | lecture notes, and that in general these notes disappear from later
84 | versions to keep the code clean.
85 |
86 | ## Strategy: Language = Idris
87 |
88 | The course is taught using the functional programming language of the
89 | Idris proof assistant. The goal was to have students use a language
90 | with an extremely clean type system, in which they could do ordinary
91 | prorgamming, and that would put them on the path to the type-driven
92 | development of programs and proofs.
93 |
94 | I judged Idris to be a better language than Coq for a CS1 course. For
95 | example, one can turn off the enforcement of totality checking, which
96 | is useful if one wants to explore ideas of generative recursions (e.g.,
97 | hailstone sequence, or iterated state transformations for simulations
98 | or games).
99 |
100 | I have also found the ability to give students partial programs with
101 | "holes" to fill in to be incredibly useful. My exams, for instance, are
102 | Idris programs with definitions that have lots of holes to fill in.
103 | That one can ask the environment about the type of each hole, is very
104 | useful, among other things. This mechanism also supports a beautiful
105 | narrative around top-down functional decomposition as an engineering
106 | design strategy for program implementation.
107 |
108 |
109 | ## Strategy: A Sequence of Major Abstract Data Types
110 |
111 | We started by turning off import of all built-in Idris libraries, so
112 | that we could build our own library of "Prelude" types from scratch.
113 | The progression of types is similar to that in Pierce's Software
114 | Foundations book, but with the inclusion of a major section on bit and
115 | byte types, including computer architecture concepts of half and full
116 | adders, their implementation using Boolean algebra, and ultimately the
117 | definition of a recursive function to add bytes.
118 |
119 |
120 | ## Strategy: Programming and Software Engineering Concepts
121 |
122 | We used the progression through data types -- from unit, bool, bit,
123 | byte, nat, list, dictionary and others -- to drive the introduction of
124 | a range of programming mechanisms and software engineering concepts.
125 | The software engineering concepts include abstraction, aggregation,
126 | visibility and encapsulation, and information hiding modularity.
127 |
128 | The programming mechanisms we introduced are those we needed to do the
129 | job. We'd introduce them when needed. They includ kinds and evaluation
130 | of expressions, unification in during pattern matching, inductive data
131 | definitions, recursive functions, etc.
132 |
133 | Woven through the progression of major types we also some qintroduced
134 | fundamental concepts in computer science, including Boolean algebra
135 | and satisfiability, binary arithmetic, including subscripting (of bits
136 | within bytes), a recursive function to add bytes, through concepts of
137 | state, expressions and their evaluation, and imperative commands as
138 | composable state transformers (all implemented in Idris).
139 |
140 | ## Strategy: Foundations of Imperative Programming as a Major Goal
141 |
142 | A goal of the course is to provides students with a foundational
143 | understanding of imperative programming /before/ throwing them into a
144 | complex industrial programming language, while eventually getting
145 | them up and running in such a language.
146 |
147 | Therefore, at the point where we introduce state and commands, we have
148 | the students start programming in Python. The first exposure to Python
149 | is /decidedly not/ "Hello World!". Rather, it's an exploration of the
150 | notion of state as an evolving function mapping variables to values,
151 | and the assignment command as a state transformer with a very specific
152 | behavior, namely overriding of that function for one variable. From
153 | there, we will proceed in parallel to build a simple imperative
154 | language in Idris while exploring its full realization in Python. As
155 | of the writing of this documentation (Nov 3, 2016), we are just now
156 | starting down this road, which will take us to the end of the course.
157 | By the end, the students should be able to write simulations, games,
158 | etc., including the use of object-oriented constructs in Python.
159 |
--------------------------------------------------------------------------------
/mylib/src/list.idr:
--------------------------------------------------------------------------------
1 | ||| A module providing a polymorphic list abstract data type
2 | module list
3 |
4 | -- The length of a list is a nat, so we need to import and export nat
5 | import public nat
6 | import ifthenelse
7 | import public eq
8 |
9 | import unit
10 |
11 | ||| A polymorphic inductive list data type
12 | ||| We leave the constructors externally visible for now
13 | public export
14 | data List a =
15 | Nil |
16 | Cons a (List a)
17 |
18 |
19 | ||| An attempt to implement a polymorphic (List a) equality operator.
20 | ||| Two ists are equal if their elements correspond and they are equal.
21 | ||| The problem is that we have no idea what type of elementw wlil be
22 | ||| in a given list, so we don't know what code to use to elements. We
23 | ||| can't fill the hole in this code.
24 | public export
25 | list_eq: Eq a => List a -> List a -> Bool
26 | list_eq Nil Nil = True
27 | list_eq Nil (Cons h t) = False
28 | list_eq (Cons h t) Nil = False
29 | list_eq (Cons h1 t1) (Cons h2 t2) =
30 | bool_and
31 | (eq h1 h2)
32 | (list_eq t1 t2)
33 |
34 | b: Bool
35 | b = list_eq (Cons True Nil) (Nil)
36 |
37 | ||| A function that returns as a nat the length of a given list
38 | export
39 | list_length: List a -> Nat
40 | list_length Nil = nat_zero
41 | list_length (Cons head tail) =
42 | nat_succ (list_length tail)
43 |
44 |
45 | ||| Returns a list obtained by appending the second list to the first
46 | -- be sure to add "export" here; we didn't do that in class last time
47 | export
48 | list_append: List a -> List a -> List a
49 | list_append Nil l = l
50 | list_append (Cons head tail) l =
51 | Cons (head) (list_append tail l)
52 | -- Note that Idris interprets "a" as an implicitly declared type
53 | -- This is the same as: { a: Type } -> List a -> List a -> List a
54 |
55 |
56 | ||| Return the element at the head of the list or
57 | ||| return the second argument (the "default") if the
58 | ||| list is empty.
59 | export
60 | list_head: List a -> a -> a
61 | list_head Nil default = default
62 | list_head (Cons h t) _ = h
63 |
64 |
65 | ||| Return the tail of the given list, or Nil if the list itself is Nil
66 | export
67 | list_tail: List a -> List a
68 | list_tail Nil = Nil
69 | list_tail (Cons h t) = t
70 |
71 |
72 | ||| Return the n'th element of a given list, or a default value
73 | ||| (the argument of type a) if n is outside the length of the list
74 | export
75 | -- As with list_head, we pass in a value (default) to be returned
76 | -- if the list is Nil or if the index is too big for the number of
77 | -- elements in the list ("out of bounds").
78 | list_sub: (index: Nat) -> List a -> (default: a) -> a
79 | -- If the list is Nil, we return the default value.
80 | -- This is the base case for this recursive function.
81 | list_sub _ Nil default = default
82 | -- If the list is not Nil and the index is zero, then
83 | -- the element we're looking for is at the head of the
84 | -- list, and we return it; otherwise we recursively return
85 | -- the element with index (n-1) in the tail of the list.
86 | -- If the index is too big for the list, we'll eventually
87 | -- hit the Nil list, at which point the base case kicks in.
88 | -- Otherwise we'll end up with the index being zero with
89 | -- the desired element at the head of the list, at which
90 | -- point the condition (index = 0) is true and we return
91 | -- that element.
92 | list_sub index (Cons head tail) default =
93 | ifthenelse
94 | (nat_eq index nat_zero)
95 | (head)
96 | (list_sub (nat_pred index) tail default)
97 |
98 |
99 | ||| Return a list with n elements of the given value
100 | export
101 | list_repeat: (value: a) -> (n: Nat) -> List a
102 | list_repeat val len =
103 | ifthenelse
104 | (nat_eq len nat_zero)
105 | Nil
106 | (Cons
107 | val
108 | (list_repeat val (nat_pred len)))
109 |
110 |
111 | ||| Given a list of nats, return their sum
112 | list_sum: List Nat -> Nat
113 | list_sum Nil = nat_zero
114 | list_sum (Cons h t) =
115 | nat_plus (h) (list_sum t)
116 |
117 |
118 | ||| Given a list of nats, return their product
119 | list_prod: List Nat -> Nat
120 | list_prod Nil = nat_one
121 | list_prod (Cons h t) =
122 | nat_mult (h) (list_prod t)
123 |
124 | ||| Given a list of nats, return the list with each nat incremented by one
125 | list_inc: List Nat -> List Nat
126 | list_inc Nil = Nil
127 | list_inc (Cons h t) =
128 | Cons
129 | (nat_succ h)
130 | (list_inc t)
131 |
132 | ||| Given a list of nats, return the list with each nat squared
133 | list_map_square: List Nat -> List Nat
134 | list_map_square Nil = Nil
135 | list_map_square (Cons h t) =
136 | Cons
137 | (nat_mult h h)
138 | (list_map_square t)
139 |
140 |
141 | ||| Return a list starting with a given nat and descending by 1s to 0
142 | ||| Example: list_range 3 = (Cons 3 (Cons 2 (Cons 1 (Cons 0 Nil))))
143 | ||| Example: list_range 0 = (Cons 0 Nil)
144 | ||| Note: I've used shorthand for nats: E.g., 3 is really (S (S (S Z)))
145 | export
146 | list_range: Nat -> List Nat
147 | list_range n =
148 | ifthenelse (nat_eq n nat_zero)
149 | (Nil)
150 | (Cons n (list_range (nat_pred n)))
151 |
152 | --- HOMEWORK STARTS HERE.
153 |
154 | ||| The function (list_range_by_to n b t) should teturn a list of natural
155 | ||| numbers starting with n, decreasing by b, until t is reached or passed,
156 | ||| where t is included in the result if it is reached exactly.
157 | ||| Examples:
158 | ||| list_range_by_to 5 1 1 = [5, 4, 3, 2, 1]
159 | ||| list_range_by_to 5 2 1 = [5, 3, 1]
160 | ||| list_range_by_to 5 3 1 = [5, 2]
161 | ||| To avoid infinite loops, we specify if b = 0, the result is Nil
162 | ||| Example:
163 | ||| list_range_by_to 5 0 1 = Nil
164 | ||| To be clear, if n = t the result is [n] (a list with just n)
165 | ||| Example:
166 | ||| list_range_by_to 5 2 5 = [5] ("from 5 down to 5 by twos is just [5]")
167 | ||| Tere is one case for which this specification is ambiguous. Identify is
168 | ||| and represent it in the definition of r6, below. For that case, the
169 | ||| result should be Nil.
170 | export
171 | list_range_by_to: (n: Nat) -> (b: Nat) -> (t: Nat) -> List Nat
172 | list_range_by_to n b t = ?fill_this_hole
173 | -- If you haven't figured out how to solve this problem yet
174 | -- keep working on it. It's important to be able to solve
175 | -- problems like this on your own. Ask for help if you need it.
176 |
177 |
178 | ||| Given a list of natural numbers return the sum of their squares
179 | ||| Example: list_sum_squares Nil = 0
180 | ||| Example: list_sum_squares [0] = 0
181 | ||| Example: list_sum_squares [3, 4, 1, 2] = 30
182 | list_sum_squares: List Nat -> Nat
183 | list_sum_squares Nil = nat_zero
184 | list_sum_squares (Cons h t) =
185 | nat_plus
186 | (nat_mult h h)
187 | (list_sum_squares t)
188 |
189 |
190 | ||| Given a list of natural numbers, return a list of Boolean values
191 | ||| where a Boolean is True if the corresponding number is even and
192 | ||| False otherwise.
193 | ||| Example: list_nat_ev_bool [5,4,3,2,1,0] = [F,T,F,T,F,T]
194 | list_nat_ev_bool: List Nat -> List Bool
195 | list_nat_ev_bool Nil = Nil
196 | list_nat_ev_bool (Cons h t) =
197 | ifthenelse
198 | (nat_evenb h)
199 | (Cons True (list_nat_ev_bool t))
200 | (Cons False (list_nat_ev_bool t))
201 |
202 |
203 | ||| Given a list of natural numbers, return the sublist of even numbers
204 | ||| Example: list_filter_even [5, 4, 3, 2, 1, 4, 2, 0] = [4, 2, 4, 2, 0]
205 | export
206 | list_filter_even: List Nat -> List Nat
207 | list_filter_even Nil = Nil
208 | list_filter_even (Cons h t) =
209 | ifthenelse
210 | (nat_evenb h)
211 | (Cons h (list_filter_even t))
212 | (list_filter_even t)
213 |
214 |
215 | ||| Given a list of Booleans, return the sublist of True ones
216 | ||| Example: list_filter_even [False, False] = Nil
217 | ||| Example: list_filter_even [T, F, T, F] = [T, T]
218 | export
219 | list_filter_True: List Bool -> List Bool
220 | list_filter_True Nil = Nil
221 | list_filter_True (Cons h t) =
222 | ifthenelse
223 | (bool_id h)
224 | (Cons h (list_filter_True t))
225 | (list_filter_True t)
226 |
227 |
228 | -- Higher-order functions involving lists
229 |
230 | ||| Return the list of elements transformed by a given function
231 | export
232 | list_map: (a -> b) -> List a -> List b
233 | list_map func Nil = Nil
234 | list_map func (Cons h t) =
235 | Cons (func h) (list_map func t)
236 |
237 |
238 | ||| Return the value obtained by reducing the list using the given
239 | ||| function and identity element.
240 | export
241 | list_fold_right: (op: a -> a -> a) -> (id: a) -> (l: List a) -> a
242 | list_fold_right op id Nil = id
243 | list_fold_right op id (Cons h t) =
244 | op h (list_fold_right op id t)
245 |
246 |
247 | ||| Return the sublist of elements for which a predicate is true
248 | export
249 | list_filter: (a -> Bool) -> List a -> List a
250 | list_filter predicate Nil = Nil
251 | list_filter predicate (Cons head tail) =
252 | ifthenelse
253 | (predicate head)
254 | (Cons head (list_filter predicate tail))
255 | (list_filter predicate tail)
256 |
257 | ||| Example: Implement list_filter_even using list_filter
258 | list_filter_even': List Nat -> List Nat
259 | list_filter_even' l = list_filter nat_evenb l
260 |
261 | ||| Example: Implement list_filter_True using list_filter
262 | list_filter_True': List Bool -> List Bool
263 | list_filter_True' l = list_filter bool_id l
264 |
265 |
266 | ||| A private-to-this-module helper function for the function
267 | ||| that follows: return the square of a given natural number.
268 | nat_square: Nat -> Nat
269 | nat_square n = nat_mult n n
270 |
271 |
272 | ||| Example: Implement list_sum_squares using map and fold_right
273 | list_sum_squares': List Nat -> Nat
274 | list_sum_squares' l =
275 | list_fold_right
276 | (nat_plus)
277 | (nat_zero)
278 | (list_map nat_square l)
279 |
280 | t: Nat
281 | t = list_sum_squares'
282 | (Cons nat_three (Cons nat_two (Cons nat_one Nil)))
283 | -- expect 14
284 |
--------------------------------------------------------------------------------