Last active
June 5, 2019 08:27
-
-
Save mwyrebski/cbd259a896aa39e83e89d0df22d581d4 to your computer and use it in GitHub Desktop.
[F#] Sequences validation demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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