This is very subjective, but I’d like to get some thoughts on this before I put this into practice. On the one hand, I’m proud I got this to compile…but is it too convoluted?
I’m trying to validate user input and:
- retain valid / remove invalid values from input
- print a list of all validation errors
Looking at other validation patterns like using applicatives, they only give me the error list.
type Input =
{name : string option; dob : DateTime option; address : string option}
type InputValid = {name : string; dob : DateTime; address : string}
let errorFun args errors =
printfn "%O" args //refresh form with only valid args
errors |> List.iter (fun s -> printfn "%s" s) //print errors up top
let successFun argsV =
printfn "%O" argsV //do stuff
The idea is to have a validation function that takes the input and returns either an “InputValid” record or the valid part of “Input” * the invalid part in “string list”.
And this is what I came up with:
let inline bindResult f acc =
match acc with
| Ok (args, successFun) ->
match f args with
| Ok param -> Ok (args, (successFun param))
| Error (args, ex) -> Error (args, ex)
| Error (args, ex) ->
match f args with
| Ok _ -> Error (args, ex)
| Error (args, ex') ->
Error (args, (List.concat [ex'; ex]))
let validateName (args: Input) =
match args.name with
| Some n when n.Length > 3 -> Ok n
| Some _ -> Error ( {args with name = None}, ["no bob and toms allowed"])
| None -> Error (args, ["name is required"])
let validateDob (args: Input) =
match args.dob with
| Some dob when dob > DateTime.Now.AddYears(-12) -> Ok dob
| Some _ -> Error ({args with dob = None}, ["get off my lawn"])
| None -> Error (args, ["dob is required"])
let validateAddress (args: Input) =
match args.address with
| Some a -> a |> Ok
| None -> Error (args, ["add1 is required"])
let validate (args : Input) =
let createValid n d a =
{InputValid.name = n; dob = d ; address = a}
Ok (args, createValid)
|> bindResult validateName
|> bindResult validateDob
|> bindResult validateAddress
|> function
| Ok (_, valid) -> successFun valid
| Error (args, ex) -> errorFun args ex