Programming,Only Better
@bodil
Dijkstra originally studied theoretical physics before
becoming a computer scientist.
“The use of COBOL cripples the mind; its teaching should,
therefore, be regarded as a criminal offence.”
“It is practically impossible to teach good
programming to students that have had a prior exposure to
BASIC: as potential programmers they are mentally
mutilated beyond hope of regeneration.”
“[Java is] embarrassing, because it is so bad. [...]A
good language, like a tool, is just a joy. But industry
doesn't want that. Probably because decisions are made by
technically incompetent people.”
Edsger W. Dijkstra1930–2002
“Programming is one of the most difficult branches
of applied mathematics; the poorer mathematicians had
better remain pure mathematicians.”
Reasoning About Software
Formal reasoning proves correctness.
Formal reasoning means maths.
Informal reasoning meansthinking about your code.
Debugging is a process of informal reasoning.
2001: The Agile Manifesto
Some people would have us think code quality can be
improved only through project management.
Whatever the path to great code might be, it doesn't go through project management.
Silver Bullets
- Iterative design
- Pair programming
- Test Driven Development
The enthusiasm is genuine.
Pinkie Pie
- Likes to pair program all day!
- Finds up front planning wasteful!
- Finds TDD extra super tedious!
Fluttershy
- Can't focus with others around.
- Prefers to know where she's going.
- Enjoys methodical TDD.
We have no idea what we're doing.
Agile is not Science.
Try it anyway. Some of it could work.
There is no Truth out there.
Agile sees programming as a matter of process, not tools.
“Bad programmers can writebad code in any language.”
This is our industry's best effort.
What is the cause of bad code?
Out of the Tar Pit
Moseley & Marks, 2006
Understanding Code
- Testing
- Informal reasoning
Informal Reasoning
“Those who want really reliable software will
discover that they must find means of avoiding the majority of bugs to
start with.”
Testing leads to more errors being detected.
Informal reasoning leads to less errors being created.
Testing
“testing is hopelessly inadequate … it can be
used very effectively to show the presence of bugs but
never to show their absence.”
Sources of Complexity
- State
- Control
- Code Volume
class Fac {
public static long fac(long n) {
return (n == 0) ? 1 : n * fac(n - 1);
}
}
Fac.fac(5);
Fac.fac(10);
State Spoils Testing
-
Testing a component in one state tells you nothing about
the same component in another state.
- We test it anyway because what else can you do?
- Programs still get into a bad state.
Example Based Testing
Testing a component with one input also tells you nothing about the same component with another input.
Generative testing is a thing.
State Spoils Informal Reasoning
- The number of possible states grows exponentially with added state.
- You have to reason about all possible states.
- It's hard to be sure where state changes come from.
Control is about the order in which things happen.
var a = 2;
var b = a + 3;
var c = b * 2;
c;
omg concurrency
- What does the control flow of a concurrent system look like?
- How do you test something when you can't even be sure of the order in which it happens?
- Or when something else might be changing its state at the same time?
Code Volume
“Many of the classic problems of developing software products
derive from this essential complexity and its nonlinear increase
with size.”
Code Volume
“It has been suggested that there is some kind of
law of nature telling us that the amount of intellectual
effort needed grows with the square of program length.
But, thank goodness, no one has been able to prove this
law.”
Managing State with Object-Orientation
- OOP is built around encapsulated state, but:
- It helps you structure the code in an understandable way.
- You can enforce internal integrity constraints.
- Less helpful when constraints must be enforced across objects.
Encapsulating state also encapsulates surprises.
Managing Control with Object-Orientation
- You don't.
- OO polymorphism makes it harder if anything.
Functional Programming and State
- Object-orientation idealises encapsulated state.
- Functional programming idealises referential transparency.
- Referential transparency means no state, only inputs and outputs.
- No side effects.
class Number {
private long value;
public Number(long value) {
this.value = value;
}
public long get() {
return this.value;
}
public void inc() {
this.value = this.value + 1;
}
}
Number n = new Number(0);
n.inc();
n.get();
(def number {:value 0})
(defn succ [number]
(assoc number :value (+ (:value number) 1)))
(succ number)
datatype number = number of int;
val n = number 0;
fun inc (number n) = number (n+1);
inc n;
Referential Transparency
- Eliminates the problem of state when testing.
- Still doesn't tell us anything about inputs you don't test for.
- Because you don't worry about state, informal reasoning becomes much easier.
So Where Did the State Go?
It's still there, but it's being passed around.
State is represented by values, not objects, so it can't mutate.
class Number {
private long number;
public Number(long number) {
this.number = number;
}
public void set(long n) {
this.number = n;
}
public static void mutableStateIsBad(Number n) {
n.set(666);
}
public long fac() {
return (this.number == 0) ? 1 : this.number * new Number(this.number-1).fac();
}
}
Concurrency Still Sucks, Right?
- No mutable state!
- Referential transparency means parallelisation is easier.
Control in Functional Programming
- Functional style lessens the problem of control.
- You still have control structures.
function fac(n) {
if (n === 0) {
return 1;
} else {
return n * fac(n - 1);
}
}
let fac 0 = 1
fac n = n * fac (n-1)
fac 5
fac 10
let inclist :: [Int] -> [Int]
inclist [] = []
inclist (head:tail) = (head+1) : inclist tail
tl;dr:
- OOP has a state abuse problem we need to stop ignoring.
- FP is not perfect, but it mostly eliminates mutable state.
- Complexity through control still exists in FP.
var a = 2;
var b = a + 3;
var c = b * 2;
c;
(let [a 2
b (+ a 3)
c (* b 2)]
c)
val a = 2;
val b = a + 3;
val c = b * 2;
c;
(use 'clojure.core.logic)
(require '[clojure.core.logic.fd :refer
[in interval + * != distinct eq]])
(run* [a b c]
)
Declarative Programming Now!
An ideal language eliminates control.
It only has to deal with essential state.
You tell it what you want,it figures out how to get there.
(use 'clojure.core.logic)
(require '[clojure.core.logic.fd :refer
[in interval + != distinct eq]])
(run* [a b]
)
- Modern functional languages aspire to this ideal.
- Modern imperative languages are headed in the opposite direction.
Maybe we should prefer the onewith fewer surprises.