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,281