liveBook · Manning

Result type

As explained in F# in Action: A Revised Edition of Get Programming with F#

A result type represents either a successful value or an error value, allowing an operation to return validation failures without raising an exception.

A Result represents an operation that can either succeed with a value or fail with information about what went wrong. It is useful when absence alone is not enough: validation may reject input for a specific reason, and later code may need to choose a response based on that reason. In F#, Result is a bundled type for modeling these ordinary, expected outcomes as return values. section 9.0 section 9.2

Result has two cases: Ok for success and Error for failure. Both cases carry payloads, and their payload types may differ. A function with the shape RawCustomer -> Result<Customer, string>, for example, either produces a validated Customer or a string explaining why conversion failed. This preserves the successful value while making failure explicit in the type. section 9.2.1

Listing 9.2 Using results instead of options for validation
let validateCustomer (rawCustomer:RawCustomer) =
    let customerId =
        if rawCustomer.CustomerId.StartsWith "C" then
            Ok (CustomerId(int rawCustomer.CustomerId[1..]))            #1
        else
            Error $"Invalid Customer Id '{rawCustomer.CustomerId}'."    #2
    let country =
        match rawCustomer.Country with
        | "" -> Error "No country supplied"                             #2
        | "USA" -> Ok Domestic                                          #1
        | other -> Ok (Foreign other)                                   #1
 
    match customerId, country with
    | Ok customerId, Ok country ->                                      #3
        Ok ... // body omitted for clarity
    | Error err, _                                                      #4
    | _, Error err ->
        Error err

This validation function returns Ok values for valid customer fields and Error messages for invalid ones, then pattern matches on both results to construct a customer or return the encountered error. section 9.2.1

Use Result rather than Option when callers must know why an operation failed. Option models possible absence with Some or None; Result adds a payload-bearing error channel. Pattern matching handles the final success or failure decision, while map and bind let code transform or continue successful values without first removing them from the Result container. Result.mapError similarly transforms error values. section 9.2.1 section 9.2.6

Choose an error-handling approach by the kind of outcome being modeled:

  • Use Option for data that may simply be absent, where no failure explanation is needed. section 9.2.6
  • Use Result for pass-or-fail operations whose errors must be retained, inspected, or converted into a response. section 9.2.6
  • Use exceptions for genuinely exceptional, fail-fast situations rather than regular control flow. section 9.2.6

A Result need not stop at one error. Set its error type to a collection such as string list to return several validation messages in one failure value. This makes the error channel part of the domain design: a simple string can be enough initially, while a custom discriminated union can model error cases more tightly when the application needs structured decisions. section 9.2.2 section 9.2.6

Validation at an HTTP boundary

Map raw JSON into a record of simple primitive values, then attempt to convert it into a rich domain type with a Result-returning validation function. Send validation messages from Error to an HTTP 4xx response; send the Ok value into application handling such as calculation or database writing, then return success. Reusable composable validation functions can replace a large validation framework for many cases. section 13.2.7

How F# in Action: A Revised Edition of Get Programming with F# covers it

How deep it goes
Defines Ok and Error payloads, demonstrates customer validation, and extends failures to multiple error values.
Examples
Uses raw-customer conversion, aggregated validation messages, and JSON-to-domain validation for web endpoints.
What it assumes
Builds on discriminated unions while introducing Result as a bundled F# type for domain modeling.
What you can do after
Apply it via F# Result values to return validation errors, branch on outcomes, and map failures to HTTP responses.
How it connects
Connects Result with Option, exceptions, discriminated unions, pattern matching, map, bind, and HTTP responses.

Also explained in

Compare how 8 books explain result type →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of F# in Action: A Revised Edition of Get Programming with F#. The section links point to the parts of the book it draws on.