Skip to content

Latest commit

 

History

History
183 lines (151 loc) · 5.88 KB

day5.org

File metadata and controls

183 lines (151 loc) · 5.88 KB

Day 5

Error Handling

Error Consumers

  • End users. Example: form validation. Generally feedback about errors in inputs.
  • Developers. Interested in debugging. Stack traces, intermediate values, etc.

Error Control Flow

  • Fail fast: stop as soon as you hit an error
  • Error accumulation: report all the errors. Typically when validating inputs, typically aimed at end users.

Usually error reporting proceeds in stages. Accumulate all errors in a stage. If not errors proceed to next stage otherwise fail.

Example:

  • validate form input
  • save stuff to the database
  • do some action

Each stage only proceeds to next if there are no errors.

Error Handling Tools

  • Exceptions, try, and catch
  • Watchdog / New Relic
  • Try, Either

Exceptions

Exceptions create stack traces which are great for debugging.

Throwable is the base class for all exceptions on the JVM. Error is for exceptions that you can’t do anything about (e.g. out of memory). Don’t catch Errors! Exception is for everything else.

throw to raise an exception try / catch / finally to handle exceptions

Match on a tag (which may or may not be equal to the type)

finally only runs for side effects

Advantages and Disadvantages

You get stack traces (useful for developers) Nothing in the type system forces you to handle errors you want to handle Break substitution

Recommendation:

  • Exceptions are ok for developers only errors (i.e. nothing gets reported to the user beyond “something went wrong”)
  • Not very good for user errors: type system doesn’t help get this right, very unstructured, cannot accumulate errors

Try

A class in the Scala standard library scala.util.Try. A value or an exception. An algebraic data type.

  • sign is variance, which we skipped over.

Basically a wrapper that can hold an exception or a value.

The constructor for Try will catch exceptions. Turns exceptions into values, maintains substitution.

A lot of methods on Try:

  • map
  • flatMap (???)
  • getOrElse
  • etc.

Advantages or Disadvantages

We get stack traces from exceptions, we can interoperate with code that produces exceptions. We work with values so we aren’t breaking substitution. We don’t get structured errors and we don’t get accumulation.

Either

Left is by convention the error / failure case (boo!) Right is the success case

Same understanding of map and flatMap.

Advantages or Disadvantages

We can store structured data in the failure case—good for presenting errors to user. We don’t get easy utilities for working with exceptions (e.g. the constructor doesn’t catch exceptions by default) flatMap is still fail fast.

Either and Cats

Error type must be something can be sensibly “added” together. Technically: must have a Semigroup. Good example is cats.data.NonEmptyList.

Use parMapN on a tuple of Either

Import cats.implicits.

Protip: mapN / parMapN builds on an operation called product that we have already seen (e.g. on animations / transducers)

Type class coherence.

Map and FlatMap

Type equation for Map:

F[A] map (A => B) = F[B] type equation. F is the container type and does not change. The element type can change.

F = Try

map transforms a value when we have a success. Note it cannot introduce a failure!

F[A] flatMap (A => F[B]) = F[B] type equation. F is the container type and does not change.

Notice f returns a Try and therefore can introduce a failure.

The expanded understanding of map and flatMap is:

  • we’re talking about sequencing operations. Operations that work in a defined order. This comes after that.
  • we have values in some context (where F is the context)
  • map transforms a value in a context without changing the context.
  • flatMap can change the context