├── mylib ├── src │ ├── mylib_doc │ │ ├── IdrisDoc │ │ ├── index.html │ │ ├── docs │ │ │ ├── binary.html │ │ │ ├── unit.html │ │ │ ├── byte2.html │ │ │ ├── bit.html │ │ │ ├── byte4.html │ │ │ ├── nat.html │ │ │ └── bool.html │ │ └── styles.css │ ├── mylib.ipkg │ ├── badnat.idr │ ├── option.idr │ ├── eq.idr │ ├── binary.idr │ ├── pair.idr │ ├── unit.idr │ ├── byte2.idr │ ├── list_nat.idr │ ├── dictionary.idr │ ├── bit.idr │ ├── ifthenelse.idr │ ├── byte8_hw.idr │ ├── nat.idr │ ├── bool.idr │ ├── byte4.idr │ ├── byte4_plus.txt │ ├── Byte8.idr │ ├── exam2_key.idr │ └── list.idr ├── tests │ ├── plans.txt │ ├── unitTest.idr │ ├── optionTest.idr │ ├── tests.ipkg │ ├── pairTest.idr │ ├── byteTest.idr │ ├── binaryTest.idr │ ├── boolTest.idr │ ├── dictionaryTest.idr │ └── listTest.idr ├── new │ ├── ifthenelse.idr │ └── nat.idr └── README ├── mylang ├── src │ ├── mylang.ipkg │ ├── command-old.idr │ ├── expression-old.idr │ ├── variableTest.idr │ ├── expressionTest.idr │ ├── variable-old.idr │ ├── variable.idr │ ├── expression.idr │ ├── state-old.idr │ ├── commandTest0.idr │ ├── command.idr │ ├── stateTest.idr │ ├── commandTest.idr.sav │ ├── state.idr │ └── commandTest.idr └── new │ └── command.idr ├── .gitignore ├── python ├── practice1.py └── imperative.py └── README.md /mylib/src/mylib_doc/IdrisDoc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mylib/tests/plans.txt: -------------------------------------------------------------------------------- 1 | - visibility issues 2 | - visibility modifiers: private, export, public export 3 | - import, import public 4 | -------------------------------------------------------------------------------- /mylib/tests/unitTest.idr: -------------------------------------------------------------------------------- 1 | module unitTest 2 | 3 | import unit 4 | 5 | -- Remember that void is just MkUnit (see unit.idr) 6 | u: Unit 7 | u = void 8 | -------------------------------------------------------------------------------- /mylang/src/mylang.ipkg: -------------------------------------------------------------------------------- 1 | package mylang 2 | 3 | modules = command, variable, state, expression 4 | 5 | opts = "--nobasepkgs --noprelude --nobuiltins" 6 | 7 | pkgs = mylib 8 | -------------------------------------------------------------------------------- /mylib/new/ifthenelse.idr: -------------------------------------------------------------------------------- 1 | module ifthenelse 2 | import bool 3 | 4 | ifthenelse: (A: Type) -> Bool -> A -> A -> A 5 | ifthenelse a True t f = t 6 | ifthenelse a False t f = f 7 | -------------------------------------------------------------------------------- /mylib/tests/optionTest.idr: -------------------------------------------------------------------------------- 1 | module optiontest 2 | 3 | import option 4 | import nat 5 | 6 | o1: Option Nat 7 | o1 = None 8 | 9 | o2: Option Nat 10 | o2 = Some nat_one 11 | -------------------------------------------------------------------------------- /mylib/src/mylib.ipkg: -------------------------------------------------------------------------------- 1 | package mylib 2 | 3 | modules = unit, bool, bit, byte2, binary, byte4, nat, ifthenelse, list, eq, pair, option, dictionary 4 | 5 | opts = "--nobasepkgs --noprelude --nobuiltins" 6 | -------------------------------------------------------------------------------- /mylib/src/badnat.idr: -------------------------------------------------------------------------------- 1 | module badnad 2 | 3 | 4 | -- It's clear pretty quickly that this strategy won't work! 5 | export 6 | data BadNat = 7 | Zero | 8 | One | 9 | Two | 10 | Three | 11 | Four 12 | -------------------------------------------------------------------------------- /mylib/tests/tests.ipkg: -------------------------------------------------------------------------------- 1 | package mytests 2 | 3 | modules = boolTest, unitTest, byteTest, binaryTest, listTest, optionTest, pairTest 4 | 5 | opts = "--nobasepkgs --noprelude --nobuiltins" 6 | 7 | pkgs = mylib 8 | -------------------------------------------------------------------------------- /mylib/tests/pairTest.idr: -------------------------------------------------------------------------------- 1 | module pairTest 2 | 3 | import pair 4 | import nat 5 | import bool 6 | 7 | p1: Pair Nat Nat 8 | p1 = MkPair nat_zero nat_zero 9 | 10 | p2: Pair Bool Bool 11 | p2 = MkPair True False 12 | 13 | p3: Pair Bool Nat 14 | p3 = MkPair True nat_zero 15 | -------------------------------------------------------------------------------- /mylib/tests/byteTest.idr: -------------------------------------------------------------------------------- 1 | import byte2 2 | 3 | -- Constants of type Byte 4 | 5 | byte_zero : Byte2 6 | byte_zero = byte2_new B0 B0 7 | 8 | byte_one: Byte2 9 | byte_one = byte2_new B0 B1 10 | 11 | byte_two: Byte2 12 | byte_two = byte2_new B1 B0 13 | 14 | byte_three: Byte2 15 | byte_three = byte2_new B1 B1 16 | 17 | -- Add cases to test functions here 18 | -------------------------------------------------------------------------------- /mylib/src/option.idr: -------------------------------------------------------------------------------- 1 | ||| A module implementing the option data type 2 | module option 3 | 4 | import bool 5 | 6 | ||| The option type 7 | public export 8 | data Option a = None | Some a 9 | 10 | 11 | bo: Option Bool 12 | bo = None 13 | 14 | bo': Option Bool 15 | bo' = Some True 16 | 17 | ||| 18 | export 19 | isNone: Option a -> Bool 20 | isNone None = True 21 | isNone _ = False 22 | -------------------------------------------------------------------------------- /mylib/tests/binaryTest.idr: -------------------------------------------------------------------------------- 1 | import binary 2 | import byte2 3 | 4 | 5 | sum0: Byte2 6 | sum0 = full_adder B0 B0 B0 7 | -- expect (B0, B0) 8 | 9 | 10 | {- 11 | Fill in the remaining test cases 12 | Exhaustive testing is tantamount to a proof 13 | It shows that "FORALL argument values of the specified types, 14 | the program produces a correct result" 15 | -} 16 | 17 | 18 | sum7: Byte2 19 | sum7 = full_adder B1 B1 B1 20 | -- expect (B1 B1) 21 | -------------------------------------------------------------------------------- /mylang/src/command-old.idr: -------------------------------------------------------------------------------- 1 | module command 2 | 3 | import variable 4 | import nat 5 | import expression 6 | 7 | ----------------------------------------------------- 8 | ----------------- SYNTAX ---------------------------- 9 | ----------------------------------------------------- 10 | 11 | -- The syntax of a little imperative programming language! 12 | public export 13 | data Command = 14 | Skip | 15 | Assign Variable Expr | 16 | Seq Command Command 17 | -------------------------------------------------------------------------------- /mylib/tests/boolTest.idr: -------------------------------------------------------------------------------- 1 | module boolTest 2 | 3 | import bool 4 | 5 | b: Bool 6 | b = bool_id True 7 | -- expect b = True 8 | 9 | b2: Bool 10 | b2 = bool_not b 11 | -- expect b = False 12 | 13 | b3: Bool 14 | b3 = bool_true b 15 | -- expect b3 = True 16 | 17 | b4: Bool 18 | b4 = bool_false b 19 | -- expect b4 = False 20 | 21 | b5: Bool 22 | b5 = bool_and True True 23 | -- expect b5 = True 24 | 25 | b6: Bool 26 | b6 = bool_and True False 27 | -- expect b6 = False 28 | -------------------------------------------------------------------------------- /mylang/src/expression-old.idr: -------------------------------------------------------------------------------- 1 | module expr 2 | 3 | import nat 4 | import variable 5 | import state 6 | 7 | 8 | public export 9 | data Expr = 10 | LitExpr Nat | 11 | VarExpr Variable | 12 | PlusExpr Expr Expr 13 | 14 | exprEval: Expr -> State -> Nat 15 | exprEval (LitExpr n) st = n 16 | exprEval (VarExpr v) st = st v 17 | exprEval (PlusExpr e1 e2) st = 18 | nat_plus -- the result is the sum of 19 | (exprEval e1 st) -- the value of expr1 20 | (exprEval e2 st) -- and expr2 21 | -------------------------------------------------------------------------------- /mylib/src/eq.idr: -------------------------------------------------------------------------------- 1 | module eq 2 | 3 | import public bool 4 | 5 | public export 6 | interface Eq a where 7 | eq: a -> a -> Bool 8 | neq: a -> a -> Bool 9 | 10 | ||| Implementation of Eq interface for Bool 11 | ||| We provide this implementation here to avoid 12 | ||| an "include cycle," where this file imports 13 | ||| bool and bool imports this file. That doesn't 14 | ||| work and needs to be avoided. 15 | export 16 | implementation Eq Bool where 17 | eq b1 b2 = bool_eq b1 b2 18 | neq b1 b2 = bool_not (eq b1 b2) 19 | -------------------------------------------------------------------------------- /mylib/src/binary.idr: -------------------------------------------------------------------------------- 1 | ||| A library of functions for binary arithmetic 2 | ||| In particular, this module implements half and 3 | ||| full adder functions. 4 | module binary 5 | 6 | {- 7 | We use byte to return the two-bit result of a one-bit addition. 8 | -} 9 | import byte2 10 | 11 | ||| Implementation of a half-adder! 12 | export 13 | half_adder: Bit -> Bit -> Byte2 14 | half_adder b1 b0 = 15 | byte2_new 16 | (bit_carry b1 b0) 17 | (bit_plus b1 b0) 18 | 19 | 20 | ||| A full adder 21 | export 22 | full_adder: Bit -> Bit -> Bit -> Byte2 23 | full_adder b1 b2 cin = 24 | byte2_new 25 | (bit_carry3 b1 b2 cin) 26 | (bit_plus3 b1 b2 cin) 27 | -------------------------------------------------------------------------------- /mylib/src/pair.idr: -------------------------------------------------------------------------------- 1 | ||| A module implementing a polymorphic pair abstract data type 2 | module pair 3 | 4 | import nat 5 | import bool 6 | 7 | ||| A polymorphic pair type 8 | public export 9 | data Pair k v = MkPair k v 10 | 11 | p1: Pair Nat Nat 12 | p1 = MkPair nat_zero nat_zero 13 | 14 | 15 | p2: Pair Bool Bool 16 | p2 = MkPair True False 17 | 18 | 19 | p3: Pair Bool Nat 20 | p3 = MkPair True nat_zero 21 | 22 | export 23 | pair_first: Pair k v -> k 24 | pair_first (MkPair k v) = k 25 | 26 | export 27 | pair_second: Pair k v -> v 28 | pair_second (MkPair k v) = v 29 | 30 | export 31 | pair_key: Pair k v -> k 32 | pair_key p = pair_first p 33 | 34 | export 35 | pair_value: Pair k v -> v 36 | pair_value p = pair_second p 37 | -------------------------------------------------------------------------------- /mylang/new/command.idr: -------------------------------------------------------------------------------- 1 | module command 2 | 3 | import variable 4 | import expr 5 | import nat 6 | import bool 7 | import state 8 | import ifthenelse 9 | 10 | export 11 | data Command = 12 | Skip | 13 | AssignBool (Variable Bool) Bool | 14 | AssignNat (Variable Nat) Nat | 15 | Seq Command Command | 16 | Cond BoolExpr Command Command | 17 | While BoolExpr Command 18 | 19 | commandEval: State -> Command -> State 20 | commandEval st Skip = st 21 | commandEval st (AssignBool var val) = 22 | override_bool st var val 23 | commandEval st (AssignNat var val) = 24 | override_nat st var val 25 | commandEval st (Seq c1 c2) = 26 | commandEval (commandEval st c1) c2 27 | commandEval st (Cond b t f) = 28 | ifthenelse 29 | (boolExprEval b st) 30 | (commandEval st t) 31 | (commandEval st f) 32 | -------------------------------------------------------------------------------- /mylib/src/mylib_doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | IdrisDoc Index
IdrisDoc

Namespaces

-------------------------------------------------------------------------------- /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.

5 |
half_adder : Bit -> 6 | Bit -> 7 | Byte2

Implementation of a half-adder!

8 |
full_adder : Bit -> 9 | Bit -> 10 | Bit -> 11 | Byte2

A full adder

12 |
-------------------------------------------------------------------------------- /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.

4 |
void : Unit

Give a non-constructor name to the only value of this type

5 |
unit_id : Unit -> 6 | Unit

Ths function simulates the identity function on the Unit type

7 |
data Unit : Type

The type with only one value

8 |
MkUnit : Unit

The single term of type Unit

9 |
-------------------------------------------------------------------------------- /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
IdrisDoc: byte2

byte2

Abstract data type representing two-bit byte

3 |
byte2_new : Bit -> 4 | Bit -> 5 | Byte2

Provide a function that packs two bits into and returns a byte

6 |
byte2_b1 : Byte2 -> 7 | Bit

Projection function returning high-order bit of a Byte2

8 |
byte2_b0 : Byte2 -> 9 | Bit

Projection function returning low-order bit of a Byte2

10 |
data Byte2 : Type

A two-bit byte type; constructor is private

11 |
MkByte2 : Bit -> 12 | Bit -> 13 | 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
IdrisDoc: bit

bit

Abstract data type representing binary arithmetic

3 |
bit_plus3 : Bit -> 4 | Bit -> 5 | Bit -> 6 | Bit

Compute the sum bit of three bits

7 |
bit_plus : Bit -> 8 | Bit -> 9 | Bit

Compute the sum bit of two bits

10 |
bit_carry3 : Bit -> 11 | Bit -> 12 | Bit -> 13 | Bit

Compute the carry bit of three bits

14 |
bit_carry : Bit -> 15 | Bit -> 16 | Bit

Compute the carry bit of two bits

17 |
data Bit : Type
MkBit : Bool -> 18 | Bit

Construct a term of type Bit by "boxing up" a Bool

19 |
B1 : Bit

Represents constant bit value 1

20 |
B0 : Bit

Represents constant bit value 0

21 |
-------------------------------------------------------------------------------- /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
IdrisDoc: byte4

byte4

Abstract data type representing two-bit byte

3 |
byte4_zeros : Byte4
byte4_sub : Nat -> 4 | Byte4 -> 5 | Bit
byte4_plus' : Byte4 -> 6 | Byte4 -> 7 | Byte4

addition

8 |
byte4_ones : Byte4
byte4_new : Bit -> 9 | Bit -> 10 | Bit -> 11 | Bit -> 12 | Byte4

Pack four bits into, and return, a byte (a 4-tuple of bits)

13 |
byte4_bit3 : Byte4 -> 14 | Bit

Projection function returning leftmost (high-order) bit

15 |
byte4_bit2 : Byte4 -> 16 | Bit

Projection function returning next bit

17 |
byte4_bit1 : Byte4 -> 18 | Bit

Projection function returning next bit

19 |
byte4_bit0 : Byte4 -> 20 | Bit

Projection function returning rightmost bit

21 |
data Byte4 : Type

A two-bit byte type; constructor is private

22 |
MkByte4 : Bit -> 23 | Bit -> 24 | Bit -> 25 | Bit -> 26 | Byte4

Constructor that boxes four Bit values into a Byte4 value

27 |
-------------------------------------------------------------------------------- /mylang/src/commandTest.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 | 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 | -- Here's a version of the whole program all in one expression 111 | allInOne_sumOneToFive: Command 112 | allInOne_sumOneToFive = 113 | Seq 114 | (NatAssign X (NatLitExpr (nat_succ (nat_succ nat_three)))) 115 | (Seq 116 | (NatAssign Y (NatLitExpr nat_zero)) 117 | (While 118 | (BoolNeqExpr (NatVarExpr X) (NatLitExpr nat_zero)) 119 | (Seq 120 | (NatAssign Y (NatPlusExpr (NatVarExpr Y) (NatVarExpr X))) 121 | (NatAssign X (NatMinusExpr (NatVarExpr X) (NatLitExpr nat_one)))))) 122 | 123 | ----------------------------------------------------- 124 | -------- separate examples of other commands -------- 125 | ----------------------------------------------------- 126 | 127 | -- Example of ... 128 | -- if X != 0: 129 | -- X = 5 130 | condCmd: Command 131 | condCmd = IfThenElse 132 | Xnot0 133 | xGetsFive -- improve this kjs 134 | Skip -- improve this kjs 135 | 136 | 137 | 138 | ------------------------------------ 139 | ------------ older stuff ----------- 140 | ------------------------------------ 141 | 142 | -- Do nothing but skip 143 | prog0: Command 144 | prog0 = Skip 145 | 146 | {- 147 | s0: State 148 | s0 = run prog0 state_init 149 | -} 150 | 151 | -- A single assignment 152 | prog1: Command 153 | prog1 = NatAssign X (NatLitExpr nat_two) 154 | 155 | {- 156 | s1: State 157 | s1 = run prog1 state_init 158 | -- expect s1 X = 2, s1 Y = 0, s1 Z = 0 159 | 160 | -- An assignment in the state produced by the last assignment 161 | s2: State 162 | s2 = run (Assign Y nat_two) s1 163 | -- expect s2 X = 2, s2 Y = 2, s2 Z = 0 164 | -} 165 | 166 | -- A sequential composition of assignments 167 | prog2: Command 168 | prog2 = 169 | Seq 170 | (NatAssign X (NatLitExpr nat_one)) 171 | (NatAssign Y (NatLitExpr nat_two)) 172 | 173 | {- 174 | s3: State 175 | s3 = run prog2 state_init 176 | -- expect same result as s2 177 | -} 178 | 179 | -- A sequential composition of an assignment with a larger program 180 | prog3: Command 181 | prog3 = 182 | Seq 183 | (NatAssign X (NatLitExpr nat_one)) 184 | (Seq 185 | (NatAssign Y (NatLitExpr nat_two)) 186 | (NatAssign Z (NatLitExpr nat_three))) 187 | 188 | {- 189 | s4: State 190 | s4 = run prog3 state_init 191 | -} 192 | 193 | -- NEW TEST CASES USING EXPRESSIONS 194 | 195 | -- like "X = Y" in Python 196 | prog4: Command 197 | prog4 = NatAssign X (NatVarExpr Y) 198 | 199 | -- like "X = Y + 1" in Python 200 | prog5: Command 201 | prog5 = 202 | NatAssign 203 | X 204 | (NatPlusExpr 205 | (NatVarExpr Y) 206 | (NatLitExpr nat_one)) 207 | -------------------------------------------------------------------------------- /mylib/README: -------------------------------------------------------------------------------- 1 | # CS1 Through Type Theory 2 | 3 | This repository records the progression of my course, informally 4 | called "CS1 Through Type Theory" at the University of Virginia, Fall 5 | 2016. The driving philosophy of the course is that we build every 6 | concept before using it, starting with most minimal of all types, unit 7 | and bool. The only elements we use without building them ourselves are 8 | those provided by the core logic of the proof assistant we're using, 9 | including the ability to define data and function types and values, 10 | the built-in rules for evaluating expressions, etc. The fact that this 11 | is a CS1 course, not an upper division or graduate course, imposes a 12 | need for very careful attention to the ordering of concepts and to 13 | their presentation and explanation in much greater detail that would 14 | be necessary in a course like any that would use Pierce's Software 15 | Foundations book. 16 | 17 | # How to view this repository 18 | 19 | The key to making sense of this repository is to look at the 20 | progression of its tagged versions. (On Github, for example, click on 21 | "Master" for the master branch, then "Tags." Select a tagged 22 | version. Doing this through the sequence of versions will reveal the 23 | ordering of concepts as we spent two months building our own core 24 | library of data types, from unit and bool through state, expression, 25 | and command. The first version in the repo picks up a few lectures 26 | into the course. From there, minor version number changes roughly 27 | match daily lectures and specific concepts within broader subject 28 | areas, while major version changes corresponding to movement from one 29 | major abstract data type to another (unit and bool to nat, nat to 30 | list, list to dictionary, etc). 31 | 32 | ## Language: Idris 33 | 34 | The course is taught using the functional programming language of the 35 | Idris proof assistant. I judged Idris to be a better language than Coq 36 | for a CS1 course. For example, one can turn off the enforcement of 37 | totality checking, which is useful if one wants to explore ideas of 38 | generative recursions (e.g., hailstone sequence, or iterated state 39 | transformations for simulations or games). 40 | 41 | ## Strategy: A Sequence of Major Abstract Data Types 42 | 43 | We started by turning off import of all built-in Idris libraries, so 44 | that we could build our own library of "Prelude" types from scratch. 45 | The progression of types is similar to that in Pierce's Software 46 | Foundations book, but with the inclusion of a major section on bit and 47 | byte types, including computer architecture concepts of half and full 48 | adders, their implementation using Boolean algebra, and ultimately the 49 | definition of a recursive function to add bytes. 50 | 51 | 52 | ## Strategy: Programming and Software Engineering Concepts 53 | 54 | We used the progression through data types -- from unit, bool, bit, 55 | byte, nat, list, dictionary and others -- to drive the introduction of 56 | a range of programming mechanisms and software engineering concepts. 57 | The software engineering concepts include abstraction, aggregation, 58 | visibility and encapsulation, and information hiding modularity. 59 | 60 | The programming mechanisms we introduced are those we needed to do the 61 | job. We'd introduce them when needed. They includ kinds and evaluation 62 | of expressions, unification in during pattern matching, inductive data 63 | definitions, recursive functions, etc. 64 | 65 | Woven through the progression of major types we also some qintroduced 66 | fundamental concepts in computer science, including Boolean algebra 67 | and satisfiability, binary arithmetic, including subscripting (of bits 68 | within bytes), a recursive function to add bytes, through concepts of 69 | state, expressions and their evaluation, and imperative commands as 70 | composable state transformers (all implemented in Idris). 71 | 72 | # Strategy: Foundations of Imperative Programming as a Major Goal 73 | 74 | A goal of the course is to provides students with a foundational 75 | understanding of imperative programming /before/ throwing them into a 76 | complex industrial programming language, while eventually getting 77 | them up and running in such a language. 78 | 79 | Therefore, at the point where we introduce state and commands, we have 80 | the students start programming in Python. The first exposure to Python 81 | is /decidedly not/ "Hello World!". Rather, it's an exploration of the 82 | notion of state as an evolving function mapping variables to values, 83 | and the assignment command as a state transformer with a very specific 84 | behavior, namely overriding of that function for one variable. From 85 | there, we will proceed in parallel to build a simple imperative 86 | language in Idris while exploring its full realization in Python. As 87 | of the writing of this documentation (Nov 3, 2016), we are just now 88 | starting down this road, which will take us to the end of the course. 89 | By the end, the students should be able to write simulations, games, 90 | etc., including the use of object-oriented constructs in Python. -------------------------------------------------------------------------------- /mylib/src/mylib_doc/styles.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | 3 | margin: 0; 4 | padding: 0; 5 | border: 0; 6 | 7 | height: 100%; 8 | 9 | font-family: "Trebuchet MS", Helvetica, sans-serif; 10 | font-size: 11pt; 11 | 12 | background-color: #FFF; 13 | } 14 | 15 | a, a:active, a:visited { 16 | 17 | text-decoration: none; 18 | color: inherit; 19 | } 20 | 21 | a:hover { 22 | 23 | text-decoration: underline; 24 | } 25 | 26 | header { 27 | 28 | padding: 5px 11px; 29 | 30 | border-bottom: 3px solid #659FDB; 31 | box-shadow: 0 -8px 22px 0px; 32 | 33 | background-color: #252525; 34 | color: white; 35 | 36 | font-size: 9pt; 37 | } 38 | 39 | .wrapper { 40 | 41 | min-height: 100%; 42 | } 43 | 44 | header nav, header strong { 45 | 46 | font-size: 11pt; 47 | } 48 | 49 | header nav { 50 | 51 | float: right; 52 | } 53 | 54 | footer { 55 | 56 | height: 30px; 57 | width: 100%; 58 | border-top: 1px solid #AAAAAA; 59 | 60 | margin-top: -31px; 61 | 62 | text-align: center; 63 | color: #666; 64 | line-height: 30px; 65 | font-size: 9pt; 66 | 67 | background: none repeat scroll 0 0 #DDDDDD; 68 | } 69 | 70 | .container { 71 | 72 | padding: 10px 10px 41px 20px; 73 | } 74 | 75 | body.index .container a:visited { 76 | 77 | color: #666; 78 | } 79 | 80 | body.index .container a:hover { 81 | 82 | color: #04819E; 83 | } 84 | 85 | h1 { 86 | 87 | margin: 0; 88 | margin-bottom: 5px; 89 | font-size: 14pt; 90 | 91 | font-family: "Trebuchet MS", Helvetica, sans-serif; 92 | } 93 | 94 | body.namespace h1 { 95 | 96 | border-bottom: 1px solid #BBB; 97 | padding-bottom: 2px; 98 | } 99 | 100 | ul { 101 | 102 | list-style-type: none; 103 | 104 | margin: 0; 105 | padding: 0; 106 | } 107 | 108 | hr { 109 | 110 | margin: 0; 111 | padding: 0; 112 | border: 0; 113 | 114 | border-bottom: 1px solid #BBB; 115 | } 116 | 117 | p { 118 | 119 | margin: 0; 120 | padding: 0; 121 | } 122 | 123 | .code { 124 | 125 | font-family: "Lucida Console", Monaco, monospace; 126 | font-size: 10pt; 127 | } 128 | 129 | .decls { 130 | 131 | margin-top: 15px; 132 | } 133 | 134 | .decls > dt { 135 | 136 | font-family: "Lucida Console", Monaco, monospace; 137 | font-size: 10pt; 138 | line-height: 20px; 139 | 140 | padding: 2px 0; 141 | 142 | border: 1px solid #CCC; 143 | background-color: #F0F0F0; 144 | 145 | display: table; 146 | width: 100%; 147 | } 148 | 149 | .decls > dt > :first-child { 150 | 151 | padding-left: 6px; 152 | } 153 | 154 | .decls > dt > :last-child { 155 | 156 | padding-right: 6px; 157 | } 158 | 159 | .decls > dd { 160 | 161 | margin: 10px 0 10px 20px; 162 | 163 | font-family: Arial, sans-serif; 164 | font-size: 10pt; 165 | } 166 | 167 | .decls > dd > p { 168 | 169 | margin-bottom: 8px; 170 | } 171 | 172 | .decls > dd > dl:not(.decls):not(:first-child) { 173 | 174 | padding-top: 5px; 175 | border-top: 1px solid #EEE; 176 | } 177 | 178 | .decls > dd > dl:not(.decls) > dt { 179 | 180 | display: block; 181 | min-width: 70px; 182 | 183 | float: left; 184 | 185 | font-weight: bold; 186 | } 187 | 188 | .decls > dd > dl:not(.decls) > dd { 189 | 190 | margin-bottom: 2px; 191 | } 192 | 193 | .fixity { 194 | 195 | font-style: italic; 196 | font-weight: normal !important; 197 | } 198 | 199 | dd.fixity { 200 | 201 | cursor: default; 202 | } 203 | 204 | .word { 205 | 206 | display: table-cell; 207 | white-space: nowrap; 208 | width: 0; 209 | } 210 | 211 | .signature { 212 | 213 | display: table-cell; 214 | width: 100%; 215 | } 216 | 217 | .name { 218 | 219 | display: table-cell; 220 | white-space: nowrap; 221 | width: 0; 222 | } 223 | 224 | .documented, .name { 225 | 226 | cursor: default; 227 | } 228 | 229 | .documented { 230 | 231 | font-weight: bold; 232 | } 233 | 234 | a.function { 235 | 236 | color: #00BA00; 237 | } 238 | 239 | .function { 240 | 241 | color: #007C21; 242 | } 243 | 244 | a.constructor { 245 | 246 | color: #FF0000; 247 | } 248 | 249 | .constructor { 250 | 251 | color: #BF3030; 252 | } 253 | 254 | a.type { 255 | 256 | color: #0000FF; 257 | } 258 | 259 | .type { 260 | 261 | color: #050599; 262 | } 263 | 264 | .keyword { 265 | 266 | color: inherit; 267 | } 268 | 269 | .boundvar { 270 | 271 | color: #BF30BF; /* Too much colour makes it hard to differ the rest of the colours */ 272 | color: inherit; 273 | } 274 | 275 | .boundvar.implicit { 276 | 277 | text-decoration: underline; 278 | } 279 | 280 | /******************* Old colours 281 | 282 | a { 283 | 284 | color: #04819E; 285 | } 286 | 287 | a:visited { 288 | 289 | color: #26A3BF; 290 | } 291 | 292 | ********************/ 293 | 294 | ul.names { 295 | 296 | border: 1px solid #666; 297 | } 298 | 299 | ul.names li:nth-child(odd) { 300 | 301 | background-color: #EEEEEF; 302 | } 303 | 304 | ul.names li:nth-child(even) { 305 | 306 | background-color: white; 307 | } 308 | 309 | ul.names li { 310 | 311 | padding-left: 5px; 312 | } 313 | 314 | ul.names li a { 315 | 316 | display: inline-block; 317 | width: 100%; 318 | 319 | padding: 2px 0; 320 | } -------------------------------------------------------------------------------- /mylib/src/Byte8.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 = 25 | MkByte8 b7 b6 b5 b4 b3 b2 b1 b0 26 | 27 | 28 | -- Constant byte8 values filled with zeros and ones, resp. 29 | 30 | export 31 | byte8_zeros: Byte8 32 | byte8_zeros = byte8_new B0 B0 B0 B0 B0 B0 B0 B0 33 | 34 | 35 | export 36 | byte8_ones: Byte8 37 | byte8_ones = byte8_new B1 B1 B1 B1 B1 B1 B1 B1 38 | 39 | -- Projection functions, returning particular component bits 40 | 41 | ||| Return Bit 0 42 | export 43 | byte8_bit0: Byte8 -> Bit 44 | byte8_bit0 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b0 45 | 46 | ||| Return Bit 1 47 | export 48 | byte8_bit1: Byte8 -> Bit 49 | byte8_bit1 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b1 50 | 51 | ||| Return Bit 2 52 | export 53 | byte8_bit2: Byte8 -> Bit 54 | byte8_bit2 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b2 55 | 56 | ||| Return Bit 3 57 | export 58 | byte8_bit3: Byte8 -> Bit 59 | byte8_bit3 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b3 60 | 61 | 62 | ||| Return Bit 3 63 | export 64 | byte8_bit4: Byte8 -> Bit 65 | byte8_bit4 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b4 66 | 67 | 68 | ||| Return Bit 3 69 | export 70 | byte8_bit5: Byte8 -> Bit 71 | byte8_bit5 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b5 72 | 73 | 74 | ||| Return Bit 3 75 | export 76 | byte8_bit6: Byte8 -> Bit 77 | byte8_bit6 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b6 78 | 79 | 80 | ||| Return Bit 3 81 | export 82 | byte8_bit7: Byte8 -> Bit 83 | byte8_bit7 (MkByte8 b7 b6 b5 b4 b3 b2 b1 b0) = b7 84 | 85 | 86 | -- arithmetic functions on 8-bit bytes 87 | 88 | -- natural numbers we'll need 89 | nat_four: Nat 90 | nat_four = nat_succ nat_three 91 | 92 | nat_five: Nat 93 | nat_five = nat_succ nat_four 94 | 95 | nat_six: Nat 96 | nat_six = nat_succ nat_five 97 | 98 | nat_seven: Nat 99 | nat_seven = nat_succ nat_six 100 | 101 | 102 | ||| Byte8 equality 103 | byte8_eq: Byte8 -> Byte8 -> Bool 104 | byte8_eq 105 | (MkByte8 b17 b16 b15 b14 b13 b12 b11 b10) 106 | (MkByte8 b07 b06 b05 b04 b03 b02 b01 b00) = 107 | bool_and (bit_eq b17 b07) 108 | (bool_and (bit_eq b16 b06) 109 | (bool_and (bit_eq b15 b05) 110 | (bool_and (bit_eq b14 b04) 111 | (bool_and (bit_eq b13 b03) 112 | (bool_and (bit_eq b12 b02) 113 | (bool_and (bit_eq b11 b01) 114 | (bit_eq b10 b00))))))) 115 | 116 | 117 | ||| Return the indexed bit of the given byte or B0 if the index is out of range 118 | export 119 | byte8_sub: Nat -> Byte8 -> Bit 120 | byte8_sub index aByte8 = 121 | ifthenelse 122 | (nat_eq index nat_zero) 123 | (byte8_bit0 aByte8) 124 | (ifthenelse 125 | (nat_eq index nat_one) 126 | (byte8_bit1 aByte8) 127 | (ifthenelse 128 | (nat_eq index nat_two) 129 | (byte8_bit2 aByte8) 130 | (ifthenelse 131 | (nat_eq index nat_three) 132 | (byte8_bit3 aByte8) 133 | (ifthenelse 134 | (nat_eq index nat_four) 135 | (byte8_bit4 aByte8) 136 | (ifthenelse 137 | (nat_eq index nat_five) 138 | (byte8_bit5 aByte8) 139 | (ifthenelse 140 | (nat_eq index nat_six) 141 | (byte8_bit6 aByte8) 142 | (ifthenelse 143 | (nat_eq index nat_seven) 144 | (byte8_bit7 aByte8) 145 | (B0)))))))) 146 | 147 | 148 | ||| Return the carry bit in the indexed column when adding the two bytes 149 | ||| or the zero bit (B0) if the index is out of range 150 | carry_sub8: Nat -> Byte8 -> Byte8 -> Bit 151 | carry_sub8 n b1 b0 = 152 | ifthenelse 153 | (nat_eq n nat_zero) 154 | (B0) 155 | (bit_carry3 156 | (carry_sub8 (nat_pred n) b1 b0) 157 | (byte8_sub (nat_pred n) b1) 158 | (byte8_sub (nat_pred n) b0)) 159 | 160 | 161 | ||| Return the indexed bit of the sum of the given bytes 162 | sum_sub8: Nat -> Byte8 -> Byte8 -> Bit 163 | sum_sub8 n b1 b0 = 164 | bit_plus3 165 | (carry_sub8 n b1 b0) 166 | (byte8_sub n b1) 167 | (byte8_sub n b0) 168 | 169 | 170 | ||| Return the sum of two 8-bit bytes as a 8-bit byte ignoring overflows 171 | export 172 | byte8_plus: Byte8 -> Byte8 -> Byte8 173 | byte8_plus b0 b1 = 174 | byte8_new 175 | (sum_sub8 nat_seven b1 b0) 176 | (sum_sub8 nat_six b1 b0) 177 | (sum_sub8 nat_five b1 b0) 178 | (sum_sub8 nat_four b1 b0) 179 | (sum_sub8 nat_three b1 b0) 180 | (sum_sub8 nat_two b1 b0) 181 | (sum_sub8 nat_one b1 b0) 182 | (sum_sub8 nat_zero b1 b0) 183 | 184 | 185 | -- a little test case 186 | n: Byte8 187 | n = byte8_plus byte8_ones byte8_ones 188 | -- expect 11111110 189 | -------------------------------------------------------------------------------- /mylib/src/mylib_doc/docs/nat.html: -------------------------------------------------------------------------------- 1 | 2 | IdrisDoc: nat
IdrisDoc: nat

nat

An abstract data type, nat, simulating
3 | the natural numbers and common arithmetic
4 | operations involving natural numbers

5 |
nat_zero : Nat

The Nat representing zero

6 |
nat_two : Nat

And this one's two

7 |
nat_three : Nat

And three

8 |
nat_succ : Nat -> 9 | Nat

Return the (Nat representing the) successor of a given Nat

10 |
nat_pred : Nat -> 11 | Nat

Compute the predecessor of a given nat (with pred zero = zero)

12 |
nat_plus : Nat -> 13 | Nat -> 14 | Nat

Implement a function for computing the sum of two nats

15 |
nat_one : Nat

The one representing one

16 |
nat_mult : Nat -> 17 | Nat -> 18 | Nat

Compute the product of two nats

19 |
nat_minus : Nat -> 20 | Nat -> 21 | Nat

Subtraction

22 |
nat_id : Nat -> 23 | Nat

The identity function for values of type Nat

24 |
nat_fact : Nat -> 25 | Nat

Return the factorial of a given nat
26 | Recall: if n = 0, n! = 1 and if n > 0, n! = n * (n-1)!

27 |
nat_evenb : Nat -> 28 | Bool

Return true if a given nat is even otherwise false

29 |
nat_eq : Nat -> 30 | Nat -> 31 | Bool

Boolean equality for natural numbers

32 |
data Nat : Type

The primary data type we export is Nat.
33 | The constructors themselves are private.

34 |
Z : Nat

Z constructs a representation of zero

35 |
S : Nat -> 36 | Nat

S n constructs a representation of the successor of n

37 |
-------------------------------------------------------------------------------- /mylib/src/mylib_doc/docs/bool.html: -------------------------------------------------------------------------------- 1 | 2 | IdrisDoc: bool
IdrisDoc: bool

bool

Abstract data type simulating Boolean algebra

3 |
bool_xor : Bool -> 4 | Bool -> 5 | Bool

Representation of Boolean "xor" function

6 |
bool_true : Bool -> 7 | Bool

Representation of constant true function of one Bool

8 |
bool_or : Bool -> 9 | Bool -> 10 | Bool

Representation of Boolean "or" function

11 |
bool_not : Bool -> 12 | Bool

Representation of negation function on Boolean values

13 |
bool_nand : Bool -> 14 | Bool -> 15 | Bool

Representation of Boolean "nand" function

16 |
bool_implies : Bool -> 17 | Bool -> 18 | Bool

Representation of the Boolean implies function

19 |
bool_if_then_else : Bool -> 20 | Bool -> 21 | Bool -> 22 | Bool

Representation of if-then-else returning a Bool

23 |
bool_id : Bool -> 24 | Bool

Representation of identity function on Boolean values

25 |
bool_false : Bool -> 26 | Bool

Representation of constant false function of one Bool

27 |
bool_equiv : Bool -> 28 | Bool -> 29 | Bool

Representation of the Boolean equiv function

30 |
bool_and : Bool -> 31 | Bool -> 32 | Bool

Representation of Boolean "and" function

33 |
data Bool : Type

A data type the values of which represent Boolean true and false

34 |
True : Bool

Term representing Boolean true

35 |
False : Bool

Term representing Boolean false

36 |
-------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------