The Design of Everyday R Functions

Jon Harmon, Atorus Research

“All artificial things are designed.”

1️⃣ Make functionality obvious

2️⃣ Forgive input

3️⃣ Describe mistake fixes

Make it obvious what functions do

Make it obvious what functions do

my_fun <- function(x, y, z) {
  y2 <- ceiling(y * z * 100) / 100
  y3 <- y + y2
  data.frame(
    item = x,
    price = y,
    sales_tax = y2,
    total_price = y3
  )
}

Make it obvious what functions do

compile_order_df <- function(x, y, z) {
  y2 <- ceiling(y * z * 100) / 100
  y3 <- y + y2
  data.frame(
    item = x,
    price = y,
    sales_tax = y2,
    total_price = y3
  )
}

Make it obvious what functions do

compile_order_df <- function(items, prices, tax_rate) {
  y2 <- ceiling(y * z * 100) / 100
  y3 <- y + y2
  data.frame(
    item = x,
    price = y,
    sales_tax = y2,
    total_price = y3
  )
}

Make it obvious what functions do

compile_order_df <- function(items, prices, tax_rate) {
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

Make it obvious what functions do

compile_order_df <- function(items, prices, tax_rate = 0.0825) {
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

Make it obvious what functions do

1️⃣ Functions = verbs


2️⃣ Arguments = nouns


3️⃣ Defaults = standards / hints

compile_order_df(
  c("paper towels", "toilet paper"),
  c(12.33, 20.77)
)

compile_order_df(
  c("paper towels", "toilet paper"),
  c(12.33, 20.77)
)
          item price sales_tax total_price
1 paper towels 12.33      1.02       13.35
2 toilet paper 20.77      1.72       22.49

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "20.77")
)

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "20.77")
)
Error in `prices * tax_rate`:
! non-numeric argument to binary operator

Forgive “close enough” input

Forgive “close enough” input

Forgive “close enough” input

compile_order_df <- function(items, prices, tax_rate = 0.0825) {
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

Forgive “close enough” input

compile_order_df <- function(items, prices, tax_rate = 0.0825) {
  prices <- stbl::to_dbl(prices)
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

Forgive “close enough” input

compile_order_df <- function(items, prices, tax_rate = 0.0825) {
  prices <- stbl::to_dbl(prices)
  tax_rate <- stbl::to_dbl_scalar(tax_rate)
  items <- stbl::to_chr(items)
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

Forgive “close enough” input

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "20.77")
)

Forgive “close enough” input

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "20.77")
)
          item price sales_tax total_price
1 paper towels 12.33      1.02       13.35
2 toilet paper 20.77      1.72       22.49

compile_order_df <- function(items, prices, tax_rate = 0.0825) {
  prices <- stbl::to_dbl(prices)
  tax_rate <- stbl::to_dbl_scalar(tax_rate)
  items <- stbl::to_chr(items)
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

compile_order_df <- function(items, prices, tax_rate = 0.0825) {
  prices <- as.double(prices)
  tax_rate <- as.double(tax_rate)
  items <- as.character(items)
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "20.77")
)
          item price sales_tax total_price
1 paper towels 12.33      1.02       13.35
2 toilet paper 20.77      1.72       22.49

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "b")
)

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "b")
)
Warning in compile_order_df(c("paper towels", "toilet paper"), c("12.33", : NAs
introduced by coercion
          item price sales_tax total_price
1 paper towels 12.33      1.02       13.35
2 toilet paper    NA        NA          NA

Tell users how to fix mistakes

Tell users how to fix mistakes

compile_order_df <- function(items, prices, tax_rate = 0.0825) {
  prices <- stbl::to_dbl(prices)
  tax_rate <- stbl::to_dbl_scalar(tax_rate)
  items <- stbl::to_chr(items)
  sales_tax <- ceiling(prices * tax_rate * 100) / 100
  total_price <- prices + sales_tax
  data.frame(
    item = items,
    price = prices,
    sales_tax = sales_tax,
    total_price = total_price
  )
}

Tell users how to fix mistakes

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "b")
)

Tell users how to fix mistakes

compile_order_df(
  c("paper towels", "toilet paper"),
  c("12.33", "b")
)
Error in `compile_order_df()`:
! `prices` <character> must be coercible to <double>
✖ Can't convert some values due to incompatible values.
• Locations: 2
• Values: "b"

Tell users how to fix mistakes

Error:
! `prices` <character> must be coercible to <double>
✖ Can't convert some values due to incompatible values.
• Locations: 2
• Values: "b"
ℹ Repair or remove these values and try again.

❗ Problem statement

❌ Exact issue (if known)

🔵 More info

ℹ️ Suggestions