Professional Documents
Culture Documents
Apress Standard
The publisher, the authors, and the editors are safe to assume that the
advice and information in this book are believed to be true and accurate
at the date of publication. Neither the publisher nor the authors or the
editors give a warranty, expressed or implied, with respect to the
material contained herein or for any errors or omissions that may have
been made. The publisher remains neutral with regard to jurisdictional
claims in published maps and institutional affiliations.
Neil Mitchell
is a Haskell programmer with a PhD in Computer Science from York
University, where he worked on making functional programs shorter,
faster, and safer. Neil is the author of popular Haskell tools such as
Hoogle, HLint, and Ghcid—all designed to help developers write good
code quickly. More recently, Neil has been researching build systems
with publications at ICFP and the Haskell Symposium, and a practical
system based on those ideas named Shake.
Marin Nikolovski
works at Massive Entertainment | A Ubisoft Studio as a senior web
developer on UPlay PC. He has over ten years of experience designing,
developing, and testing software across a variety of platforms. He takes
pride in coding to consistently high standards and constantly tries to
keep up with the latest developments in the IT industry.
Vlad Riscutia
is a software engineer at Microsoft working on Office. He is interested
in programming languages and type systems, and how these can best
be leveraged to write correct code.
Footnotes
1 The core concept of functional programming languages is a mathematical function.
2 Several other languages with dependent types support are Coq, Agda, Lean.
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2023
B. Sitnikovski, Introduction to Dependent Types with Idris
https://doi.org/10.1007/978-1-4842-9259-4_1
1. Formal Systems
Boro Sitnikovski1
Before you can construct proofs of correctness for software, you need
to understand what a proof is and what it means for a proof to be valid.
This is the role of formal systems. The purpose of formal systems is to
let you reason about reasoning—to manipulate logical proofs in terms
of their form, rather than their content. This level of abstraction makes
formal systems powerful tools.
2.
Grammar, which are rules that tell which formulas are
“well-formed.”
2.
A set of axioms, which are formulas that we accept as “valid”
without justification.
3.
A set of inference rules, which tell us how we can derive
new, valid formulas from old ones.
In the inference rules, the symbols M, I, and U are part of the system,
while x is a variable that stands for any symbol(s). For example, MI
matches rule 2 for x = I, and it can also match rule 1 for x = M.
Another example is MII, which matches rule 2 for x = II and rule 1
for x = MI.
You will now see how to show (or prove) how to get from MI to
MIIU using the inference rules:
1. MI (axiom)
2. MII (rule 2, x = I)
2. Applying rule 2 will double the number of Is, so you can have: I,
II, IIII, and IIIIIII (in particular, 2n Is). Invariant OK.
You’ve shown that with the starting axiom MI, it is not possible to
get to MU because no sequence of steps can turn a string with one I into
a string with no Is. But if you look carefully, this uses a different formal
system to reason about MU (i.e., divisibility by 3, which is not part of the
MU system). This is because the puzzle cannot be solved in its own
system. Otherwise, an algorithm would keep trying different inference
rules of MU indefinitely (not knowing that MU is impossible).
Every useful formal system has this limitation. As you’ve seen,
Gö del’s theorem shows that there’s no formal system that can contain
all possible truths, because it cannot prove some truths about its own
structure. Thus, having experience with different formal systems and
combining them as needed can be useful.
Footnotes
1 The word iff is an abbreviation for “If and only if” and means that two statements
are logically equivalent.
2 Note that this theorem only holds for systems that allow expressing arithmetic of
natural numbers (e.g., Peano, set theory, but first-order logic also has some paradoxes
if you allow self-referential statements). This is because the incompleteness theorem
relies on the Gö del numbering concept, which allows a formal system to reason
about itself by using symbols in the system to map expressions in that same system.
For example, 0 is mapped to 1, S is 2, = is 3, so 0 = S0 ⇐⇒ (1, 3, 2, 1). Using
this, you can express statements about the system within the system, which are self-
referential statements. The next chapter looks into these systems.
3 An invariant is a property that holds whenever you apply any of the inference
rules.
4 After having been introduced to proofs, you will be given an exercise to prove this
fact.
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2023
B. Sitnikovski, Introduction to Dependent Types with Idris
https://doi.org/10.1007/978-1-4842-9259-4_2
For example, you can say a = Salad is organic, and thus the
variable a represents a true statement. Another statement is a =
Rock is organic, and thus a is a false statement. The statement a
= Hi there! is neither a true nor a false statement, and thus is not a
proposition.
The “and” connective means that both a and b have to be true in
order for a ∧ b to be true. For example, the statement I like milk
and sugar is true as a whole iff (if and only if) both I like milk
and I like sugar are true.
a b a∧b
⊤ ⊤ ⊤
⊤ ⊥ ⊥
⊥ ⊤ ⊥
⊥ ⊥ ⊥
a b a∨b
⊤ ⊤ ⊤
⊤ ⊥ ⊤
⊥ ⊤ ⊤
a b a∨b
⊥ ⊥ ⊥
The negation connective simply swaps the truthiness of a
proposition. The easiest way to negate any statement is to just prepend
It is not the case that ... to it. For example, the negation
of I like milk is It is not the case that I like milk,
or simply I don't like milk.
a ¬a
⊤ ⊥
⊥ ⊤
a b a→b
⊤ ⊤ ⊤
⊤ ⊥ ⊥
⊥ ⊤ ⊤
⊥ ⊥ ⊤
Exercise 1
Come up with a few propositions and combine them using:
1. The “and” connective
For example, you can use the set {{1, {a1}}, {2, {a2}}, ...
, {n, {an}}} to represent the ordered collection (a1, a2, ..., an). This
will now allow you to extract the k-th element of the tuple, by picking x
such that {k, {x}} ∈ A. Having done that, now you have (a, b) = (c, d) ≡ a = c
∧ b = d, that is, two tuples are equal iff their first and second elements
respectively are equal. This is what makes them ordered.
One valid tuple is (1 pm, 2 pm, 3 pm) which represents three hours
of a day, sequentially.
For example, the expressions {1, 2} ⊆ {1, 2, 3} and {1, 2, 3} ⊆ {1, 2, 3} are
both true. But this expression is not true: {1, 2, 3} ⊆ {1, 2}.
Exercise 3
Think of a set of objects and express that some object belongs to that
set.
Exercise 4
Think of a set of objects whose order matters and express the set in
terms of an ordered collection.
Exercise 5
Think of a relation (for example, the relationship between two
people in a family tree) and express it using the notation described.
Exercise 6
Come up with two subset expressions—one that is true and one that
is false.
Exercise 7
Think of two sets and combine them using the definition of the
Cartesian product. Afterward, think of two subset expressions, one
that is true and one that is false.
Exercise 8
Think of a function and represent it using the table approach.
Exercise 9
Write down the corresponding input and output sets for the function
you implemented in Exercise 8.
2. 2 = 3 (axiom)
Exercise 10
With the given axioms of Peano, prove that 1 = S(0) and 2 = S(S(0))
are natural numbers.
Exercise 11
Come up with several axioms and inference rules and do a proof
similar to the previous example.
A B A∧B A∧B→B
⊤ ⊤ ⊤ ⊤
⊤ ⊥ ⊥ ⊤
⊥ ⊤ ⊥ ⊤
⊥ ⊥ ⊥ ⊤
Exercise 12
Given the two propositions A ∨ B and ¬B, prove (or conclude) A by
means of a truth table.
Hint: The statement to prove is ((A ∨ B) ∧ ¬B) → A.
2. Modus tollens states: If you are given p → q and ¬q, then you can
conclude ¬p.
For example, given A ∨ B, B → C, ¬C, prove A. You can approach the proof
as follows:
Proofs with truth tables work for only propositional (zeroth order)
theorems—the table method is essentially the decidability algorithm
for zeroth-order logic. That’s why they are easy (if verbose) and
always work, and why column proofs become necessary once you’re
using quantifiers.
Exercise 13
Prove ((A ∨ B) ∧ ¬B) → A using the three-column proof technique.
3. Thus, A ∨ B
3. Thus, A ∧ B ↔ B ∧ A.
3. ∀x, x = x. You know that for any number x, this number is equal to
itself. Thus, ∀x, x = x.
4. ∃x, x > 0. To prove this, you only need to find an x that is greater
than 1. One valid example is 1. Thus, ∃x, x > 0.
Exercise 14
Prove ((A ∨ B) ∧ ¬B) → A by means of a formal proof.
Exercise 15
We’ve used the rules modus tollens and modus ponens without
giving an actual proof for them. Try to prove by yourself that these
two rules hold, by constructing a truth table and by using a three-
column proof:
1. Modus tollens: ((p → q) ∧ ¬q) → ¬p
Exercise 16
Prove the formal proofs 1 and 2 from the previous examples using
both truth tables and three-column proofs techniques.
Exercise 17
Think of a proposition and try to prove it by means of a formal proof.
2. A set of rules that reduce the other cases toward the base case.
After proving the two parts, you can conclude that P(n) holds for
all natural numbers. The formula that you need to prove is P(0) ∧
(P(n) → P(n + 1)).
Footnotes
1 Since unrestricted quantification leads to inconsistency, higher-order logic is an
attempt to avoid this. You will look into Russell’s paradox later as an example.
2 This means that there is a decidability algorithm—an algorithm that will always
return a correct value (e.g., true or false), instead of looping infinitely or producing a
wrong answer.
3 It is worth noting that in set theory, P would be a subset of a relation, that is, P ⊆ A
×{T, F }, where A is a set of some inputs, for example Salad and Rock. When working
with other systems, you need to be careful, as this is not the case with first-order
logic. In the case of first-order logic, you have P(Salad) = ⊤, P(Rock) = ⊥, and so on. as
atomic statements, not mathematical functions (i.e., they cannot be broken down
into smaller statements). This is what makes first-order logic independent of set
theory. In addition, functions have a nice characterization that is dual to the concepts
of “one-to-one” (total) and “onto” (well-defined).
7 The notation ∃! stands for unique existential quantifier. It means that only one
object fulfills the predicate, as opposed to ∃, which states that at least one object
fulfills the predicate.
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2023
B. Sitnikovski, Introduction to Dependent Types with Idris
https://doi.org/10.1007/978-1-4842-9259-4_3
3. Type Theory
Boro Sitnikovski1
For example, with 1 : Nat, 2 : Nat, you can say that 1 and 2 are
of type Nat, that is natural numbers. An operation (function) + : Nat
→ Nat → Nat is interpreted as a function that takes two objects of
type Nat and returns an object of type Nat.
Definition 2 In type theory, a type constructor is a function
that builds new types from old ones. This function accepts types as
parameters and returns a new type.
Idris supports algebraic data types. These data types are a kind of
complex types, that is, types constructed by combining other types.
Two classes of algebraic types are product types and sum types.
To understand the algebra they capture, you denote with |A| the
number of possible values of type A. When you create an algebraic
sum, you have |A | B| = |A| +|B|. Similarly, for an algebraic
product, you have |A B| = |A| ∗ |B|.
As an example, assume that you have two types: Nat for natural
numbers, and Real for real numbers. Using sum (union), you can
construct a new type Nat | Real. Valid values of this type are 1 :
Nat | Real, 3.14 : Nat | Real, and so on. Using product, you
can construct a new type called Nat Real. Valid values of this type are
1 1.5 : Nat Real, 2 3.14 : Nat Real, and so on. Using this
approach, sums and products can be combined and thus more complex
data structures can be defined.
The Bool type has two possible values: True and False. Thus,
|Bool| = 2. The type Unit (equivalent to ()) has one possible value:
Unit. You can now form a sum type Bool | Unit, which has length
3 and values True, False, and Unit. Additionally, the product type
Bool Unit has length 2 and values True Unit and False Unit.
Besides the sum and product, there is another important operation
called exponentiation. This corresponds to functions, so a type a → b has
|a|
|b| possible values. Section 3.3 explains how this algebra can be
generalized.
Finally, Idris supports dependent types.2 These kinds of types are so
powerful that they can encode most properties of programs. With their
help, Idris can prove invariants at compile time. As you will see in
Section 4.2, types also allow you to encode mathematical proofs, which
brings computer programs closer to mathematical proofs. As a
consequence, this allows you to prove properties (e.g., specifications)
about your software.3
The Mail Box is Lengthened So That It Reaches through the Wall to the
Inside, Where a Door is Placed through Which the Mail can be Removed
A person having a mail box set flush in the outside wall of his
home can, with a little alteration, make it accessible from inside the
house: After removing the back side of the box a tin extension
should be soldered to the box giving it sufficient length to reach
through the wall in which an opening of the proper size has been cut.
The enlarged container is completed by adding a glass door to the
inner end which enables one to ascertain its contents at a glance.—
A. Pertle, Chicago, Ills.
Block Plane Converted for Use on Circular Work
Few amateur craftsmen can afford to own a circular plane, yet this
tool is decidedly necessary for such round work as table tops, half-
round shelves, segments, and the like. Any ordinary block plane will
accomplish such work if equipped as illustrated. A piece of half-
round hard wood is cut the width of the plane and attached with
countersunk machine screws, as indicated. The block elevates the
rear end of the plane, causing it to follow the curve of the work on
which it is used.
Pressure Spray Made of Old Oilcan
A Corn Popper Made from a Coffee Can, or Similar Tin Receptacle, and a
Piece of a Broom Handle
¶Boards exposed to the weather should be laid with the heart side
down, as determined by examining the end grain.
Fig. 2
Fig. 3
Fig. 1
Sharpened Poles, Two Feet Long, are Used with This Pile Driver in
Building Foundations, Wharves, and Other Structures of Piling. The
Details of the Headblock and the Nipper Device are Shown in Figs. 2 and
3
A Small Working Pile Driver
By EDWARD A. KRUEGER
[These directions will enable boys of varying skill with tools to make a pile
driver, as a toy or model. Several simple methods of making the parts in the
home workshop, with materials easily obtainable, are suggested.—Editor.]
Thebridges,
construction of small docks, wharves, piers, and foundations or
buildings, and other structures, by the driving of piling is
interesting out-of-door play, in which boys will find much fun. A pile
driver for this work is shown in the page plate, Fig. 1. The hammer is
raised by means of a winch, and is dropped automatically when it
reaches the cap of the derrick, as indicated in Fig. 3. The drum is
then released, and the weighted double-hook nipper drops down,
picking up the hammer on the next upstroke. A single-hook nipper,
that can be made easily of wire, is also shown in the detail sketch,
Fig. 6. The small boy who cannot make the nippers or the winch,
may tie the rope directly to the hammer, drawing it up by hand, and
dropping it as desired. The hammer need not be fitted to the guides,
but merely arranged to drop between them, and the derrick can be
made of only a few main pieces. The larger parts of the hammer and
nipper weight are best made of lead, babbitt, or white metal, as
these may be cut or melted readily. Iron, brass, or copper, solid or in
plates, may be used, if means for shaping them are at hand.
The making of the derrick may be undertaken first. Make two
pieces for the bed A, ⁷⁄₈ by ⁷⁄₈ by 17 in.; two hammer guides B, ⁷⁄₈ by
⁷⁄₈ by 33¹⁄₈ in.; one bed piece, C, ³⁄₈ by ⁷⁄₈ by 20 in.; two bed pieces,
D, ³⁄₈ by ⁷⁄₈ by 5¹⁄₄ in.; two posts, E, ¹⁄₂ by ¹⁄₂ by 34¹⁄₂ in.; two braces,
F, ³⁄₈ by ³⁄₄ by 26¹⁄₂ in. Cut these pieces slightly over their finished
lengths as given, allowing for trimming and fitting. Make strips, ¹⁄₄ by
¹⁄₂ in., for the bracing on the sides of the derrick and the ladder
bracing on the back.
Notch the lower ends of guides B, ¹⁄₈ by ⁷⁄₈. and the lower ends of
posts E, on an angle, ¹⁄₈ in. deep, to fit pieces A. Join the parts of the
bed, as shown in the page plate, pieces A being set 3¹⁄₂ in. apart,
fastening them with bolts or screws. Make braces G, of sheet metal,
and bolt them in place. Fit the posts E into place, and fasten them at
the bed and the top. Put on several ladder braces temporarily, to
steady the frame. Fit the braces F carefully, and bolt them in place.
Remove the piece C and the braces F, and nail the horizontal
bracing to the sides of the frame. Then fit and nail the diagonal
braces. The bolted construction is convenient in “knocking down” the
derrick for storing it. Reassemble the parts, and make the cap for the
headblock.
The headblock and cap are shown in detail in Figs. 2 and 3. Make
two pieces, H, ¹⁄₄ by 1⁵⁄₁₆ by 1³⁄₄ in.; one piece, J, ¹⁄₄ by 1 by 1³⁄₁₆ in.;
two braces, K, ¹⁄₄ by 1¹³⁄₃₂ by 1³⁄₄ in. Make the two beveled pieces of
the cap ⁷⁄₈ by 1³⁄₄ by 1¹⁄₂ in., and provide a wooden strip or metal
plate for the front and rear edges, as shown. Fasten strips of sheet
metal to the bevel of the notch, to protect it from wear by the striking
of the nipper hooks. Make the sheave 1¹⁄₂ in. in diameter and ³⁄₈ in.
thick, with a groove for the rope. Assemble the parts, as shown.
The details of the winch are shown in Figs. 4 and 5, and the
method of assembling the parts, in Fig. 1. The drum may also be
driven without gears by fixing the crank directly to the shaft. Gears
may be obtained from old machines, or purchased from dealers in
model supplies. Make the supports L and M, Fig. 4, ³⁄₄ by 4 by 6⁵⁄₈
in., cutting patterns of paper, if desired.
Fig. 4
The Supports of the Winch are Made of ³⁄₄-Inch Wood, Bolted to the Bed
The gear, Fig. 5, is 3¹⁄₂ and the pinion ³⁄₄ in. in diameter. The drum
is of wood, 2 in. in diameter and 3⁵⁄₁₆ in. long. Its ends are 3¹⁄₂-in.
metal disks, fastened with screws. The shaft is a ³⁄₈-in. bolt, 5³⁄₈ in.
long, and bears in holes bored in the supports, as shown in the
details of these parts. The crank N, Fig. 5, is made of a ³⁄₁₆-in. rod,
bent as shown, and fitted with a washer to fit next to the pinion. The
gear is set by means of the pawl O, which is bent from a strip of ¹⁄₁₆-
in. sheet metal. The brace P is bent from a ¹⁄₁₆ by ³⁄₄ by 1⁵⁄₈-in. strip
of sheet metal, and riveted to the pawl. Assemble the parts,
fastening the gear to the drum end, and bolt the supports into place.
Put the pinion into mesh with the gear at its proper place, and
carefully mark the hole for the crank. Square the end of the crank
and the hole in the pinion, and fit them to a driving fit. Fix the rope to
the drum, and reeve it through the head block. The derrick is then
ready for the hammer and the weighted nipper.
Fig. 5
Details of the Drum, Its Driving Mechanism, and Fittings