Header menu logo CodecMapper

How To Model A Validated Wrapper

Use this pattern when the wire value is simple but the in-memory model should enforce a smart-constructor rule.

open CodecMapper.Schema

type UserId = UserId of int

module UserId =
    let create value =
        if value > 0 then Ok(UserId value)
        else Error "UserId must be positive"

    let value (UserId value) = value

type Account = { Id: UserId; Name: string }
let makeAccount id name = { Id = id; Name = name }

let userIdSchema =
    int
    |> tryMap UserId.create UserId.value

let accountSchema =
    define<Account>
    |> construct makeAccount
    |> fieldWith "id" _.Id userIdSchema
    |> field "name" _.Name
    |> build

Extract the tryMap pipeline into a named schema when the same wrapper rule appears across multiple contracts.

Multiple items
union case UserId.UserId: int -> UserId

--------------------
type UserId = | UserId of int
type UserId = | UserId of int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val create: value: int -> Result<UserId,string>
val value: int
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val value: UserId -> int
type Account = { Id: UserId Name: string }
Multiple items
union case UserId.UserId: int -> UserId

--------------------
module UserId from HOWTOMODELAVALIDATEDWRAPPER

--------------------
type UserId = | UserId of int
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val makeAccount: id: UserId -> name: string -> Account
val id: UserId
val name: string
val userIdSchema: obj
val accountSchema: obj

Type something to start searching.