liveBook · Manning

Validation

As explained in The Joy of JavaScript

A computation or data structure that represents success or failure and supports propagating results through the successful branch while preserving failure context.

Validation represents a result as exactly one of two states: Success contains a value; Failure contains an error. It keeps validation rules separate from the work they guard. Instead of returning a Boolean and leaving callers to reconstruct what went wrong, a check returns a result that can carry its failure through later steps. section 5.5.3 section 5.1

The successful branch is where work happens. `map` transforms its contained value; on Failure, `map` does nothing and preserves the result. Use `flatMap` when the next function itself returns a Validation: it passes along a successful value without nesting results, or skips the remaining checks after a failure. A function must return Failure explicitly for a rejected condition; these chains do not, by themselves, make every operation exception-safe. section 5.5.4 section 5.5.5

A check with a useful failure

`checkTampering(block)` returns `Success.of(block)` when the block’s stored hash matches its calculated hash, or `Failure.of('Block hash is invalid')` otherwise. Changing the block data makes the same check report failure. A caller can inspect `isSuccess` or `isFailure` and retain the reason rather than receiving only `false`. section 5.5.3 section 5.5.5

Listing 5.21 Block validation with flatMap
class Block {
   ...
 
   isValid() {
      const {
         index: previousBlockIndex,  
         timestamp: previousBlockTimestamp
       } = this.#blockchain.lookUp(this.previousHash);
      
       return Validation.of(this)
         .flatMap(checkTimestamps(previousBlockTimestamp))    #1
         .flatMap(checkIndex(previousBlockIndex))             #1
         .flatMap(checkTampering);                            #1
   }
}

Follow the block-validation chain: `Validation.of(this)` starts the successful path, and each `flatMap` runs another check only if the preceding check succeeded. section 5.5.5

The same interface scales beyond one block. `composeM` sequences Validation-returning functions in a point-free pipeline, whereas ordinary `compose` is for functions returning unwrapped values. A recursive `validate` method can start with an object’s own `isValid()` result, then reduce its children through `flatMap`; a failure stops later child validations. Shared `map` and `flatMap` contracts also let a custom Validation work with Ramda. Like a Promise chain, its successful path continues while an error falls through, though a Promise also models pending asynchronous work. section 5.5.6 section 5.5.7 section 5.5.8 section 8.2.2

How The Joy of JavaScript covers it

How deep it goes
Builds Success and Failure classes, assigns mapping and chaining behavior, and applies them to recursive validation.
Examples
Uses block hash and timestamp checks, nullable strings, file reading, recursive blockchain validation, and safe division.
What it assumes
Assumes familiarity with JavaScript function composition and develops the choice-type implementation from Success and Failure branches.
What you can do after
Build it from scratch and apply it to chain checks that stop on Failure while retaining an error message.
How it connects
Connects algebraic data types, functors, monads, flatMap, function composition, mixins, and Promises.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Joy of JavaScript. The section links point to the parts of the book it draws on.