type Env = [(String,Bool)]containing names and their values. For example, the inputs to the following AND gate
[("x",True), ("y",False)]
representing a True input on x and a False input on y. The
corresponding output would be
[("z",False)]
Such a list of names and values is called an environment (in the sense of omgivning, not miljö); it contains the values of all the variables we are interested in, and provides a context in which any expression involving variables can be evaluated.
We will need to look up the values of variables stored in the environment.
value :: String -> Env -> Boolsuch that value x env returns the value associated with x in the environment env.
value "x" [("x",True),("y",False)] = True
value "y" [("x",True),("y",False)] = False
type Circuit = ([String], [String], Env -> Env)For example, the AND gate above would be modelled as
andGate :: Circuit
andGate = (["x", "y"], ["z"], andGateFn)
where andGateFn env = [("z",value "x" env && value "y" env)]
Notice how andGateFn uses value to extract the values of its
inputs from the environment it is given, and constructs an environment
containing a value for its output, "z", as its result. (We could also
have written this definition like this:
andGate = (["x", "y"], ["z"], \env -> [("z",value "x" env && value "y" env)])
using a λ-expression to write the definition of andGateFn "in
place" where it is used.)
Notice also that andGate is not itself a function; it is a tuple containing a function, which is not at all the same thing. If we want to simulate a circuit on a given input, we need to extract the function from it -- for example, using
simulate :: Circuit -> Env -> Env simulate (inputs,outputs,action) env = action envCalling simulate andGate [("x",True),("y",False)] produces the output [("z",False)].
x y | z -------------------------------- True True | True True False | False False True | False False False | FalseAll the information needed to produce such a table is given in the type Circuit: the first component of a circuit, the names of the inputs, tell us what to call the columns to the left of the "|"; the second component, the names of the outputs, tell us what to call the columns after the "|", and the third component, the function, tells us how to compute a row of the output from a row of the input.
tabulate :: Circuit -> Stringwhich generates a truth table from a circuit, in a format like the example above.
putStr (tabulate andGate)should print the table above.
I suggest writing a separate function to compute the left part of the table first, that is, to decide which rows should be present. Then you can compute the values of the outputs for each row. Don't forget that a circuit can have several outputs (thus, several columns after the "|"), even though the examples we have constructed so far have only one.
When two circuits are connected in series, the outputs of the first become the
inputs of the second. For example, we might connect the AND gate above to an
inverter

to create a NAND gate

(where z is now an internal wire, neither an input nor an output).
Obviously, the first circuit should produce as outputs all of the inputs that the second circuit requires, but it does not matter if it produces other outputs also -- they are simply discarded.
(>.>) :: Circuit -> Circuit -> Circuitwhich constructs the circuit consisting of its two arguments connected in series.
nandGate :: Circuit nandGate = andGate >.> inverterand then
putStr (tabulate nandGate)should print
x y | w -------------------------------- True True | False True False | True False True | True False False | True
(inputs,outputs,fn) >.> (inputs',outputs',fn') = ...(where you are supposed to fill in the ...), using pattern matching to give names to the components of the two circuits. When we declare the type of an operator, as I did above, we enclose its name in brackets.
If you add a line
infixr 9 >.>(which specifies how tightly the operator >.> binds), then Haskell will allow you to write, for example,
f >.> g >.> hwithout brackets.
The solution is to model a wire, connecting an input with one name to
an output with another:

wire :: String -> String -> Circuitso that wire x y constructs a circuit with one input, x, and one output, y, whose value is equal to the input.
andGate >.> wire "z" "w" >.> inverter

(|||) :: Circuit -> Circuit -> Circuitwhich constructs the circuit consisting of its two arguments connected in parallel. The inputs will be the combined inputs of the two argument circuits, and the outputs will be the combined outputs.
halfAdder :: Circuit halfAdder = (andGate >.> wire "z" "carry") ||| (xorGate >.> wire "z" "sum")Notice that you have to rename the outputs using wires, so that the outputs of the entire half-adder have the names we want.
Printing the truth table of the half-adder using
putStr (tabulate halfAdder)should produce
x y | carry sum ---------------------------------------- True True | True False True False | False True False True | False True False False | False False
import Listat the top of your program to use it.
Add a line
infixr 8 |||so you can use the new operator without brackets. The number 8 is its binding strength: since this is less than 9 (the strength we gave >.>), then ||| will bind less tightly than >.>. In other words, we can omit the brackets in the definition of halfAdder above.
Here the carry-in is called cin, and the carry out is called
cout.
fullAdder :: Circuitusing halfAdder, orGate, wires, and the operators ||| and >.>, and print its truth table using tabulate.
There is a nice recursive algorithm to construct the disjunctive normal form of a boolean function -- i.e. the job you do using Karnaugh maps -- but this lab is too short to contain it!
Solve this problem in pairs as before. You should hand in the code of your solution to your group tutor at the group meetings in the week beginning October 14th, and also submit it electronically. You should include instructions to enable your tutor to run your code.
If you want to work with a different partner (because your original partner wants to choose the alternative lab, for example), you may. Just make sure that