>`, and `Flow`.
Use Bind directly with let!, do!, or return!. It marks the error adaptation for that bind and does not run the
source. If the source already uses the workflow's error type, bind it without Bind.
Assign an error
Bind.error failure source assigns failure to a source that has no error value of its own. It preserves a present
or successful value. It turns None, ValueNone, or Error () into the supplied workflow error when flow { }
binds the source.
type User = { Name: string }
type LoginError = UserNotFound | InvalidPassword
let tryGetUser username : Async<User option> =
async { return if username = "ada" then Some { Name = username } else None }
let checkPassword password =
if System.String.IsNullOrWhiteSpace password then Error () else Ok ()
let login username password =
flow {
let! user =
tryGetUser username
|> Bind.error UserNotFound
do!
checkPassword password
|> Bind.error InvalidPassword
return user
}
04-error-handling_01-bind.md_page.User
Name: string
04-error-handling_01-bind.md_page.LoginError
UserNotFound
InvalidPassword
tryGetUser: string -> Async<User option>
username: string
checkPassword: string -> Result<unit,unit>
password: string
System
login: string -> string -> Flow<'a,LoginError,User>
user: User
`Bind.error` accepts these source types:
Option<'value>
ValueOption<'value>
Result<'value, unit>
Flow<'env, unit, 'value>
Async<Option<'value>>, Task<Option<'value>>, and ValueTask<Option<'value>>
Async<ValueOption<'value>>, Task<ValueOption<'value>>, and ValueTask<ValueOption<'value>>
Async<Result<'value, unit>>, Task<Result<'value, unit>>, and ValueTask<Result<'value, unit>>
For a Boolean condition, first return a Result that states what failure looks like. Then bind that result directly
if it already uses the workflow error type, or apply Bind.error if it uses unit.
Map an error
Use Bind.mapError when the source has a meaningful error that must be translated to the surrounding workflow's
error type.
type AuthError = Denied of string
type TokenError = Expired of string
type LoginError = Unauthorized of AuthError | TokenFailed of TokenError
let authorize user : Async<Result<unit, AuthError>> =
async { return Error (Denied user) }
let createToken user : Result<string, TokenError> =
Error (Expired user)
let login user =
flow {
do!
authorize user
|> Bind.mapError Unauthorized
return!
createToken user
|> Bind.mapError TokenFailed
}
`Bind.mapError` accepts these source types:
Result<'value, 'error>
Flow<'env, 'error, 'value>
Async<Result<'value, 'error>>
Task<Result<'value, 'error>>
ValueTask<Result<'value, 'error>>
Use a marker only at a bind site
The value returned by Bind.error or Bind.mapError is a marker for the Flow computation expression. It is not a
general-purpose Result or Flow transformation. Keep it directly on the right side of let!, do!, or return!:
flow {
let! user = maybeUser |> Bind.error UserNotFound
return! createToken user |> Bind.mapError TokenFailed
}
Outside `flow { }`, use functions for the source type, such as `Result.mapError`, `Option.toResult`, or
`Flow.mapError`.
Raw Task and ValueTask values still require an explicit
Task interop adapter. Bind changes an error at a computation-expression
bind; it does not change when an operation starts.
Choose between Bind and Policy
Use Bind for a one-time error adaptation at a specific bind site. Use a
Policy for a named, reusable verification rule that can read the workflow environment,
compose with other rules, or be enabled by the environment.