Skip to content

Instantly share code, notes, and snippets.

@mwyrebski
Last active June 5, 2019 08:27
Show Gist options
  • Select an option

  • Save mwyrebski/cbd259a896aa39e83e89d0df22d581d4 to your computer and use it in GitHub Desktop.

Select an option

Save mwyrebski/cbd259a896aa39e83e89d0df22d581d4 to your computer and use it in GitHub Desktop.
[F#] Sequences validation demo
// Short demonstration of sequences validation in F#.
// Sequences, unlike Lists allow null values. Assuming that valid input is a
// non-empty collection, we have to validate against nulls when using
// sequences as inputs (assuming nulls can be provided to our functions).
// Let's assume each function returns Result type which can be either
// Error with "Invalid input" message or Ok with processed values as list.
// ------------------------------------------------------------
let normalize (s : string) = s.Trim()
let test f =
Error "Invalid input" = f null
&& Error "Invalid input" = f Seq.empty
&& Error "Invalid input" = f []
&& Ok ["a"] = f ["a"]
&& Ok ["a"; "b"] = f [" a"; "b "]
&& Ok ["a"; "b"] = f (seq { yield " a" ; yield "b " })
// ------------------------------------------------------------
// 0. Using procedural approach.
let fn0 (strings : string seq) =
if strings = null
then
Error "Invalid input"
else
let list = List.ofSeq strings
if list.IsEmpty
then
Error "Invalid input"
else
let normalized = List.map normalize list
Ok normalized
test fn0 // -> true
// ------------------------------------------------------------
// 1. Using match for empty list.
let fn1 (strings : string seq) =
if strings = null
then
Error "Invalid input"
else
let list = Seq.toList strings
match list with
| [] -> Error "Invalid input"
| x -> x |> List.map normalize |> Ok
test fn1 // -> true
// ------------------------------------------------------------
// 2. Using match for input sequence converted to list option.
let fn2 (strings : string seq) =
let opt = strings
|> Option.ofObj // convert input to option (will be None if strings = null)
|> Option.map Seq.toList // map the underlying sequence to list (only if option is Some)
match opt with
| None -> Error "Invalid input"
| Some [] -> Error "Invalid input" // handle empty list separately
| Some s -> s |> List.map normalize |> Ok
test fn2 // -> true
// ------------------------------------------------------------
// 3. Shorter version of the previous function with use of two patterns
// combined with "OR".
let fn3 (strings : string seq) =
let opt = strings
|> Option.ofObj
|> Option.map Seq.toList
match opt with
| None | Some [] -> Error "Invalid input" // combined patterns
| Some s -> s |> List.map normalize |> Ok
test fn3 // -> true
// ------------------------------------------------------------
// 4. Null and empty inputs can also be treated as None.
// Converting empty input to None can be done with Option.fold.
let fn4 (strings : string seq) =
let opt = strings
|> Option.ofObj
|> Option.map Seq.toList
|> Option.fold (fun _ x -> if x = [] then None else Some x) None
match opt with
| None -> Error "Invalid input" // validation relies only on None and Some
| Some s -> s |> List.map normalize |> Ok
test fn4 // -> true
// ------------------------------------------------------------
// 5. Simpler version of the previous.
// Here the convertsion from empty list is done with Option.bind.
let fn5 (strings : string seq) =
let opt = strings
|> Option.ofObj
|> Option.map Seq.toList
|> Option.bind (fun x -> if x = [] then None else Some x)
match opt with
| None -> Error "Invalid input" // validation relies only on None and Some
| Some s -> s |> List.map normalize |> Ok
test fn5 // -> true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment