Chapter 11: Optional and Result, built

Chapter 11 meets two small containers for uncertainty: Optional, a box that holds a value or none, and Result, a box that holds success or an error. Divide asks safeDivide for a / b and gets back a Result of (Integer, String) rather than a runtime exception - isOk()/ok() on success, isError()/error() on failure, and the compiler refuses any read that skips the matching check. The IDE's own tools make safeDivide's and Div's whole structure; Surround With folds the division itself under an if ... else head before the real branches are typed in, and Analyse ▸ Effective API then reads Result's own surface before the program runs. To play it in the IDE: Help > Lessons > Writing code…, or ek9 -ide -lesson book-optional-result.

The EK9 IDE as the lesson opens

What you'll build

The lesson builds this program from a file holding only its module line - the finished file, as the book prints it.

#!ek9
defines module book.divide

  defines function
    safeDivide() as pure
      ->
        a as Integer
        b as Integer
      <- rtn <- Result() of (Integer, String)

      if b == 0
        rtn: rtn.asError("cannot divide by zero")
      else
        rtn: rtn.asOk(a / b)

  defines program
    Div()
      stdout <- Stdout()

      good <- safeDivide(10, 2)
      if good.isOk()
        stdout.println(`Answer: ${good.ok()}`)

      bad <- safeDivide(10, 0)
      if bad.isError()
        stdout.println(`Cannot: ${bad.error()}`)

Step 1: safeDivide — a pure function returning Result

Choose Code > New Construct > Function…

The EK9 IDE once this is done: Choose Code > New Construct > Function…

safeDivide is the function's name. Add two parameters, a and b, both Integer, set Returns to Result of (Integer, String), and tick pure. Choose Preview.

The EK9 IDE just before choosing Preview: safeDivide is the function's name

The preview shows safeDivide about to be written, pure, a and b in and Result of (Integer, String) out. Choose Apply.

The EK9 IDE just before choosing Apply: The preview shows safeDivide about to be written, pure, a and b in and Result of (Integer, String) out

Select the stub, from <- rtn as Result of (Integer, String)? down to its //AI line, and type a single, unguarded division - the next part shows the compiler why that is not enough. Saves when typed.

The EK9 IDE once this is done: Select the stub, from <- rtn as Result of (Integer, String)? down to its //AI line, and type a single, unguarded division - the next part shows the compiler why that is not enough

Step 2: Divide by zero, with Surround With

Put the caret on the division line, right-click - or choose Refactor > Refactor This… - and choose Surround With ▸ if ... else.

The EK9 IDE once this is done: Put the caret on the division line, right-click - or choose Refactor > Refactor This… - and choose Surround With ▸ if ..

Select the stub, from surroundGuard <- Boolean(true) down to its //AI line, and type the real check: b == 0 is the error path the if branch now reports, dividing is what the else branch does instead. Saves when typed.

The EK9 IDE once this is done: Select the stub, from surroundGuard <- Boolean(true) down to its //AI line, and type the real check

Step 3: The program

Choose Code > New Construct > Program…

The EK9 IDE once this is done: Choose Code > New Construct > Program…

Div is the program's name - no application to bind, no parameters, nothing injected. Type Div and choose Preview.

The EK9 IDE just before choosing Preview: Div is the program's name - no application to bind, no parameters, nothing injected

The preview shows Div about to be written. Choose Apply.

The EK9 IDE just before choosing Apply: The preview shows Div about to be written

Select the stub, from ready <- true down to its //AI line, and type Div's whole job: call safeDivide twice, reading the answer only after isOk(), and the error only after isError(). Saves when typed.

The EK9 IDE once this is done: Select the stub, from ready <- true down to its //AI line, and type Div's whole job

Step 4: Read Result's effective API

Select good, on the line that declares it, then choose Analyse ▸ Effective API: cursor-anchored, the same way Pipeline read chapter 12's stream before it ran.

The EK9 IDE once this is done: Select good, on the line that declares it, then choose Analyse ▸ Effective API

The pane draws Result of (Integer, String)'s own surface: its Constructors - Result() and Result(ok, error) - then its Methods and Operators, each section counted. Among the methods are the six safeDivide and Div called by name - asOk and asError, which fill the box, and isOk, isError, ok and error, which read it safely - alongside what every EK9 type carries, such as ? for set and $ for a string form.

Step 5: Run it

Choose Run > Run Program…

The EK9 IDE once this is done: Choose Run > Run Program…

The project's only program is already chosen. Choose Run.

Answer: 5, then Cannot: cannot divide by zero - the first call succeeded, the second was refused before it ever divided. Choose Close.

The EK9 IDE just before choosing Close: Answer

The result

Divide.ek9 now reads exactly as chapter 11 of Getting Started with EK9 shows it: a pure function that returns a Result rather than risk a division by zero, and a program that only ever reads it after checking which it got. Surround With folded the division itself under an if ... else head before the real condition and branches were typed in; Analyse ▸ Effective API then read Result of (Integer, String)'s own surface - the same surface ek9_query_effective_api answers for an AI assistant, a cursor position traded for a file and a line.

The EK9 IDE when the lesson has finished

Where this lesson comes from