BindError
This page shows how to assign or map a source error immediately before flow {} binds that source.
flow {} can bind many source shapes directly. When the source already has the right error type, bind it directly. Use BindError only when the bind source needs a different error at the call site.
Assign an Error
Use BindError.withError when the source fails with option/value-option absence or a unit error.
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 login username password =
flow {
let! user =
tryGetUser username
|> BindError.withError UserNotFound
do!
password
|> Check.notBlank
|> BindError.withError InvalidPassword
return user
}
BindError.withError works on:
Option<'value>ValueOption<'value>Result<'value, unit>Flow<'env, unit, 'value>Async<Option<'value>>,Task<Option<'value>>,ValueTask<Option<'value>>Async<ValueOption<'value>>,Task<ValueOption<'value>>,ValueTask<ValueOption<'value>>Async<Result<'value, unit>>,Task<Result<'value, unit>>,ValueTask<Result<'value, unit>>
For boolean predicates, make the predicate explicit first:
do!
isValid
|> Check.isTrue
|> BindError.withError InvalidPassword
Map an Error
Use BindError.map when the source already carries a meaningful error, but it is not the error type of the surrounding flow.
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
|> BindError.map Unauthorized
return!
createToken user
|> BindError.map TokenFailed
}
BindError.map works on:
Result<'value, 'error>Flow<'env, 'error, 'value>Async<Result<'value, 'error>>Task<Result<'value, 'error>>ValueTask<Result<'value, 'error>>
When Not To Use It
Do not use BindError as a general Result helper. In pure code, use Check.withError, Result.mapError, or Validation.mapError.
let validateName name =
name
|> Check.whenNotBlank
|> Check.withError "Name required"
Inside flow {}, direct binding is still the default. Reach for BindError only when the source error must be assigned or mapped before the bind.