Combinatorial Boolean Circuitry
Exciting function compositions
Monoidal Arithmetic Circuitry
Monadic Sequential Circuitry
Based entirely on function input and output
Relies on function composition for portability
Declarative, not imperative in nature
Encodes side effects in a type
CHIP MyChip {
IN a, b; // can be many ins
OUT out; // can be many outs
PARTS:
//Here we connect inputs and outputs using other chips!
//Wire the inputs a & b to BuiltInChip inputs,
//assign its output to a variable
BuiltInChip(aInput=a, bInput=b, out=myVar)
//Wire in the variable myVar (output of other chip) to BuiltInChips input,
//wire its output to the output of MyChip
BuiltInChip(a=myVar, b=myVar, out=out)
}
| Input A | Input B | Output |
|---|---|---|
| High | High | Low |
| High | Low | High |
| Low | High | High |
| Low | Low | High |
/**
* Not gate: out = not in
*/
CHIP Not {
IN in;
OUT out;
PARTS:
//Input of not is wired to both a & b of Nand
//Output of Nand is wired out
//Truth Table of Nand:
// T T -> F
// T F -> T
// F T -> T
// F F -> T
// The first and last options are our not!
Nand(a=in, b=in, out=out);
}
CHIP And {
IN a, b;
OUT out;
PARTS:
//Wire inputs a & b to Nand
//Wire Nand output to variable x
Nand(a=a, b=b, out=x);
//Wire variable x to Not, wire x to And output
Not(in=x, out=out);
}
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
CHIP And16 {
IN a[16], b[16];
OUT out[16];
PARTS:
And(a=a[0], b=b[0], out=out[0]);
And(a=a[1], b=b[1], out=out[1]);
And(a=a[2], b=b[2], out=out[2]);
And(a=a[3], b=b[3], out=out[3]);
And(a=a[4], b=b[4], out=out[4]);
And(a=a[5], b=b[5], out=out[5]);
And(a=a[6], b=b[6], out=out[6]);
And(a=a[7], b=b[7], out=out[7]);
And(a=a[8], b=b[8], out=out[8]);
And(a=a[9], b=b[9], out=out[9]);
And(a=a[10], b=b[10], out=out[10]);
And(a=a[11], b=b[11], out=out[11]);
And(a=a[12], b=b[12], out=out[12]);
And(a=a[13], b=b[13], out=out[13]);
And(a=a[14], b=b[14], out=out[14]);
And(a=a[15], b=b[15], out=out[15]);
}(.) :: (b -> c) -> (a -> b) -> a -> c
(f . g) x = f (g x)
-- imagine we want to convert our Voltage to a Boolean Type
voltageToBoolean :: Voltage -> Boolean
voltageToBoolean High = True
voltageToBoolean Low = False
-- contrived example to give an opposite boolean of a voltage
lie :: Voltage -> Boolean
lie x = voltageToBoolean (not' x)
-- can be composed!
lie :: Voltage -> Boolean
lie x = (voltageToBoolean . not') x
-- can be reduced to point free!
lie :: Voltage -> Boolean
lie = voltageToBoolean . not'
//In some UI File
Utils.getRGBWithAlpha("Red");
//In Utils File
const ColorUtils = {
getRGBWithAlpha: color => {
const hex = StringToHexLib.convert(color)
const rgb = ColorLib.toRGB(hex);
//args in wrong order, also no auto curry...
const withAlpha = OtherLib.addAlpha(rgb, 0.5);
return withAlpha
}
}
//all those functions that do nothing then call other functions...
//sure would be nice to just say
getRGBWithAlpha = convert . toRGB . (addAlpha 0.5)canItCompose f g x y = f (g x y)
-- 1. convert to lambda
\f g x y -> f (g x y)
-- 2. use $ to remove parentheses
\f g x y -> f $ g x y
-- 3. Trickiest part for me, follow the types of (.) and ($)
\f g x y -> f . g x $ y
-- 4. You can eliminate an argument that is only passed to other functions
\f g x -> f . g x
-- 5. Separate f and g with parentheses to make evaluation order explicit
\f g x -> (f .) (g x)
-- 6. Use $ to remove parentheses around g
\f g x -> (f .) $ g x
-- 7. Use our ole ($) -> (.) trick\f g x -> (f .) . g $ x
-- 8. Dear me, what abomination is this
\f g -> (f .) . g
-- 9. Live your best Haskell life, assign it to a cute infix
(...) f g = (f .) . g| Operand | Operand | Sum | Carry |
|---|---|---|---|
| 1 | 1 | 0 | 1 |
| 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 |
| 0 | 0 | 0 | 0 |
| Operand | Operand | Sum |
|---|---|---|
| 1 | 1 | 0 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 0 | 0 | 0 |
| Operand | Operand | Sum |
|---|---|---|
| True | True | False |
| True | False | True |
| False | True | True |
| False | False | False |
| Operand | Operand | Carry |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 0 | 0 |
| Operand | Operand | Sum |
|---|---|---|
| True | True | True |
| True | False | False |
| False | True | False |
| False | False | False |
/**
* Computes the sum of two bits.
*/
CHIP HalfAdder {
IN a, b; // 1-bit inputs
OUT sum, // Right bit of a + b
carry; // Left bit of a + b
PARTS:
Xor(a=a, b=b, out=sum);
And(a=a, b=b, out=carry);
}
| Operand | Operand | Operand | Sum | Carry |
|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 |
/**
* Computes the sum of two bits.
*/
CHIP FullAdder {
IN a, b c; // 1-bit inputs
OUT sum, // Right bit of a + b
carry; // Left bit of a + b
PARTS:
HalfAdder(a=a, b=b, sum=sum1, carry=carry1);
HalfAdder(a=sum1, b=c sum=sum, carry=carry2);
Or(a=carry1, b=carry2, out=carry);
}
/**
* 1-bit register.
* If load[t]=1 then out[t+1] = in[t]
* else out does not change (out[t+1]=out[t])
*/
CHIP Bit {
IN in, load;
OUT out;
PARTS:
//Multiplexor. If sel==1 then out=in else out=ffout.
Mux(a=ffout, b=in, sel=load, out=muxout);
//DFF send output to both out and input of Mux
//DFF gets input[t] and returns output[t - 1]
DFF(in=muxout, out=ffout, out=out);
}/**
* 16-bit register
* If load[t]=1 then out[t+1] = in[t]
* else out does not change
*/
CHIP Register {
IN in[16], load;
OUT out[16];
PARTS:
Mux(a=dff0out, b=in[0], sel=load, out=dff0in);
DFF(in=dff0in, out=dff0out, out=out[0]);
Mux(a=dff1out, b=in[1], sel=load, out=dff1in);
DFF(in=dff1in, out=dff1out, out=out[1]);
Mux(a=dff2out, b=in[2], sel=load, out=dff2in);
DFF(in=dff2in, out=dff2out, out=out[2]);
Mux(a=dff3out, b=in[3], sel=load, out=dff3in);
DFF(in=dff3in, out=dff3out, out=out[3]);
Mux(a=dff4out, b=in[4], sel=load, out=dff4in);
DFF(in=dff4in, out=dff4out, out=out[4]);
Mux(a=dff5out, b=in[5], sel=load, out=dff5in);
DFF(in=dff5in, out=dff5out, out=out[5]);
Mux(a=dff6out, b=in[6], sel=load, out=dff6in);
DFF(in=dff6in, out=dff6out, out=out[6]);
Mux(a=dff7out, b=in[7], sel=load, out=dff7in);
DFF(in=dff7in, out=dff7out, out=out[7]);
Mux(a=dff8out, b=in[8], sel=load, out=dff8in);
DFF(in=dff8in, out=dff8out, out=out[8]);
Mux(a=dff9out, b=in[9], sel=load, out=dff9in);
DFF(in=dff9in, out=dff9out, out=out[9]);
Mux(a=dff10out, b=in[10], sel=load, out=dff10in);
DFF(in=dff10in, out=dff10out, out=out[10]);
Mux(a=dff11out, b=in[11], sel=load, out=dff11in);
DFF(in=dff11in, out=dff11out, out=out[11]);
Mux(a=dff12out, b=in[12], sel=load, out=dff12in);
DFF(in=dff12in, out=dff12out, out=out[12]);
Mux(a=dff13out, b=in[13], sel=load, out=dff13in);
DFF(in=dff13in, out=dff13out, out=out[13]);
Mux(a=dff14out, b=in[14], sel=load, out=dff14in);
DFF(in=dff14in, out=dff14out, out=out[14]);
Mux(a=dff15out, b=in[15], sel=load, out=dff15in);
DFF(in=dff15in, out=dff15out, out=out[15]);
}/**
* Memory of 8 registers, each 16 bit-wide. Out holds the value
* stored at the memory location specified by address. If load=1, then
* the in value is loaded into the memory location specified by address
* (the loaded value will be emitted to out after the next time step.)
*/
CHIP RAM8 {
IN in[16], load, address[3];
OUT out[16];
PARTS:
DMux8Way(in=true, sel=address, a=ls0, b=ls1, c=ls2, d=ls3, e=ls4, f=ls5, g=ls6, h=ls7);
And(a=load, b=ls0, out=load0);
Register(in=in, load=load0, out=rout0);
And(a=load, b=ls1, out=load1);
Register(in=in, load=load1, out=rout1);
And(a=load, b=ls2, out=load2);
Register(in=in, load=load2, out=rout2);
And(a=load, b=ls3, out=load3);
Register(in=in, load=load3, out=rout3);
And(a=load, b=ls4, out=load4);
Register(in=in, load=load4, out=rout4);
And(a=load, b=ls5, out=load5);
Register(in=in, load=load5, out=rout5);
And(a=load, b=ls6, out=load6);
Register(in=in, load=load6, out=rout6);
And(a=load, b=ls7, out=load7);
Register(in=in, load=load7, out=rout7);
Mux8Way16(a=rout0, b=rout1, c=rout2, d=rout3,
e=rout4, f=rout5, g=rout6, h=rout7,
sel=address, out=out);
}/**
* Memory of 64 registers, each 16 bit-wide. Out hold the value
* stored at the memory location specified by address. If load=1, then
* the in value is loaded into the memory location specified by address
* (the loaded value will be emitted to out after the next time step.)
*/
CHIP RAM64 {
IN in[16], load, address[6];
OUT out[16];
PARTS:
DMux8Way(in=true, sel=address[3..5], a=ls0, b=ls1, c=ls2, d=ls3, e=ls4, f=ls5, g=ls6, h=ls7);
And(a=load, b=ls0, out=load0);
RAM8(in=in, load=load0, address=address[0..2], out=rout0);
And(a=load, b=ls1, out=load1);
RAM8(in=in, load=load1, address=address[0..2], out=rout1);
And(a=load, b=ls2, out=load2);
RAM8(in=in, load=load2, address=address[0..2], out=rout2);
And(a=load, b=ls3, out=load3);
RAM8(in=in, load=load3, address=address[0..2], out=rout3);
And(a=load, b=ls4, out=load4);
RAM8(in=in, load=load4, address=address[0..2], out=rout4);
And(a=load, b=ls5, out=load5);
RAM8(in=in, load=load5, address=address[0..2], out=rout5);
And(a=load, b=ls6, out=load6);
RAM8(in=in, load=load6, address=address[0..2], out=rout6);
And(a=load, b=ls7, out=load7);
RAM8(in=in, load=load7, address=address[0..2], out=rout7);
Mux8Way16(a=rout0, b=rout1, c=rout2, d=rout3,
e=rout4, f=rout5, g=rout6, h=rout7,
sel=address[3..5], out=out);
}