Programming Without Ifs
Kyle Coberly
kylecoberly.com
- Educator
- Business Dork
- Software Developer
Kyle Coberly
kylecoberly.com
Learning Objectives
- Define cyclomatic complexity
- Recall 3 ways to avoid ifs
- Recall 3 ways to make ifs better
Cyclomatic Complexity
function writeTalk(){
const learningMaterial = getBooks("cyclomaticComplexity")
let whatIKnow = [];
const { length } = learningMaterial
for (let index = 0; index < length; index++) {
whatIKnow.push(learningMaterial[index])
}
const enough = Infinity
if (whatIKnow.length < enough) {
return "w/e, don't listen to me"
} else {
return getGoodWisdom(whatIKnow)
}
}
Cyclomatic Complexity
Cyclomatic Complexity is 2
function someShitDreithPosted() {
if (firstBatch !== this) {
if (this._hasChildren) {
expirationTime = this._expirationTime = firstBatch._expirationTime;
this.render(this._children);
}
}
}
- Nothing happens
-
`firstBatch` isn't `this`, but has no children
-
`firstBatch` isn't `this`, but has children
Cyclomatic Complexity is 3
Cyclomatic Complexity
// Bad
function someShitDreithPosted() {
if (firstBatch !== this) {
if (this._hasChildren) {
expirationTime = this._expirationTime = firstBatch._expirationTime;
this.render(this._children);
}
}
}
Cyclomatic Complexity is 3
Cyclomatic Complexity
// A little better
function someShitDreithPosted() {
if (firstBatch !== this && this._hasChildren) {
expirationTime = this._expirationTime = firstBatch._expirationTime;
this.render(this._children);
}
}
What Causes Cyclomatic Complexity?
if
Conditionals
- if, else, else if
- case
- catch
- &&
- ||
- ?
Who Cares?
- More tests
- More points of failure
- Harder to extend
- Harder to read
- Tests
- Errors
- Extend
- Readability
Suck
What Is Cyclomatic Complexity?
What do I do?
If-Free Strategies
- Functional Chains
- Dictionary Lookup
- Dynamic Dispatch
Functional Chain
const daysOfTheWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
const announce = message => console.log(`${message}!`)
// Bad
for (let i = 0; i < daysOfTheWeek.length; i++) {
if (daysOfTheWeek[i] !== "Sat") {
if (daysOfTheWeek[i] !== "Sun") {
console.log(daysOfTheWeek[i])
}
}
}
// Better, some temp variables
const weekendDays = ["Sat", "Sun"]
const weekDays = difference(weekendDays)(daysOfTheWeek)
weekDays.forEach(announce)
// Best, no variables
flow([
difference(["Sat", "Sun"])(daysOfTheWeek),
each(announce),
])(daysOfTheWeek)
Dictionary Lookup
// Bad
greetDay(day) {
let greeting
switch(day) {
case "mon":
greeting = "It's Monday!"
break
case "tue"
greeting = "Beautiful Tuesday"
break
case "wed":
greeting = "Should be a great Wednesday"
break
...
}
announce(greeting)
}
// Better
const greetings = {
mon: "It's Monday!",
tue: "Beautiful Tuesday",
wed: "Should be a great Wednesday",
}
announceDay(day){
announce(greetings(day))
}
Dynamic Dispatch
// Bad
function doubler(input) {
switch (typeof input) {
case "number":
return input + input
case "string":
return input
.split("")
.map((letter) => letter + letter)
.join("")
}
}
// Better
doublerOperations = {
number: (input) => input + input,
string: (input) => input
.split("")
.map((letter) => letter + letter)
.join(""),
}
function doubler(input) {
return doublerOperations[typeof input](input)
}
Review
- Functional Chain: Data transformation
- Dictionary Lookup: Data lookup
- Dynamic Dispatch: Variable behavior
How can you get rid of ifs?
Sane-If Strategies
- Flatten nested ifs
- Wrap blocks in functions
- Return a ternary
Flatten Nested Ifs
// Better
function someShitDreithPosted() {
if (firstBatch !== this && this._hasChildren) {
expirationTime = this._expirationTime = firstBatch._expirationTime;
this.render(this._children);
}
}
// Bad
function someShitDreithPosted() {
if (firstBatch !== this) {
if (this._hasChildren) {
expirationTime = this._expirationTime = firstBatch._expirationTime
this.render(this._children)
}
}
}
Wrap blocks in functions
// Better
function someShitDreithPosted() {
if (firstBatch !== this && this._hasChildren) {
resetExpirationAndRender()
}
}
resetExpirationAndRender() {
expirationTime = this._expirationTime = firstBatch._expirationTime;
this.render(this._children);
}
// Bad
function someShitDreithPosted() {
if (firstBatch !== this && this._hasChildren) {
expirationTime = this._expirationTime = firstBatch._expirationTime;
this.render(this._children);
}
}
Return Ternaries
// Bad
function finishTalk(){
let whatIKnow = ["Not Nothing"]
const enough = Infinity
if (whatIKnow.length < enough) {
return "So what if it was short"
} else {
return getGoodWisdom(whatIKnow)
}
}
// Better
function finishTalk(){
let whatIKnow = ["Not Nothing"]
const enough = Infinity
return whatIKnow.length < enough
? "It was kind of fun though right"
: getGoodWisdom(whatIKnow)
}
What are some saner ways to use ifs?
Learning Objectives
- Define cyclomatic complexity
- Recall 3 ways to avoid ifs
- Recall 3 ways to make ifs better
Kyle Coberly
kylecoberly.com
Programming Without Ifs
By Kyle Coberly
Programming Without Ifs
- 1,447