What is Result<uint64,string>

I’m doing exercism, and in one problem they define the type of a function to be


I have no idea what that type is: could someone explain it, and or point me to somewhere in the docs that explains it.
I looked at other peoples solutions and they are returning something like

Ok aNumber

I don’t get that either …


Hi Jonathan, and welcome!
Result in F# is a way to communicate errors or failure via the return type, as an alternative to just throwing an Exception. Let’s look at the type definition of Result from FSharp.Core:

    type Result<'T,'TError> = 
      | Ok of ResultValue:'T 
      | Error of ErrorValue:'TError

If that syntax isn’t familiar to you, it’s a “discriminated union,” and this article describes the concept pretty well. This type definition means that a value of type Result<uint64, string> could be either an Ok of some uint64, or an Error of some string. So:

let a : Result<uint64, string> = Ok 5UL  // valid 
let b : Result<uint64, string> = Error "overflow" // also valid

If you have a Result, and you want to do something with it, the simplest thing to do is pattern match it:

match myResult with
| Ok i -> // do something here with i, which is a uint64
| Error e -> // do something here with e, which is a string

(Though the Result module has a few other helpful functions that can sometimes make working with Results more succinct, like Result.map and Result.mapError).
There’s some debate in the F# community over when it’s more appropriate to use Result to handle errors, and when it’s more appropriate to throw Exceptions for errors instead. There’s a popular concept among F#ers called “Railway Oriented Programming” that heavily features Result.

The Result<'t, 'u>type is a discriminated union that is defined in the FSharp.Core library. It is defined as:

type Result<'t, 'u> =

  | Ok of 't

  | Error of 'u

The idea of returning this type is that a caller needs to explicitly handle both the case of a successful call and the case of an error. In your case, it is Result<uint64, string>, so if the function is successful it will return Ok number where number is a uint64. If the function fails, it will return Error str where str is a string, propbably explaining why the function failed. Say, for example, you are trying to write a function to perform safe division. You want to return a number if the division works, but if the denominator is zero, you want to return an error message. You could implement that like so:

let saveDivision (numerator: uint64) (denominator: uint64): Result<uint64, string> =

  if denominator = 0UL then

    Error "Division by zero"


    Ok (numerator / denominator)

Then, a caller of that function can explicitly handle both cases:

let numerator = 10UL

let denominator = 0UL

let result = saveDivision numerator denominator

match result with

| Ok value -> printfn $"{numerator} / {denominator} = {value}"

| Error message -> printfn $"Error dividing {numerator} by {denominator}: {message}"