How I can define a non zero integer in F# that rise a compile time error when assigning zero value?
My question comes by watching this Scott Wlaschin video https://www.youtube.com/watch?v=E8I19uA-wGY&t=960s in minute 16:00
I have found another answers to this question in SO but all refers to a dynamic checking(throwing exception at creation time) but this approach is not a big deal and can be done in any OO and not OO languages. What I'm looking is something like: type NonZeroInteger = int except 0
or something like that.
In F# there aren't really compile time contracts for what you want to do. The F# way to deal with this would be to have a type NonZeroInteger
with a private constructor and a function that returns an Option<NonZeroInteger>
. This will assure that you never have Some(0)
.
What this does is basically forcing the developer who uses your code to account for the possibility that he might not have a NonZeroInteger
after constructing one, when given the wrong integer value.
In your code you can then always safely assume that NonZeroIntegers are in fact non-zero.
open Option
module A =
type NonZeroInteger =
private | NonZeroInteger of int
static member Create (v : int) : Option<NonZeroInteger> =
if v = 0 then
None
else
Some(NonZeroInteger(v))
member this.Get : int =
this |> fun (NonZeroInteger v) -> v
member this.Print =
this |> fun (NonZeroInteger v) -> printfn "%i" v
printfn "%A" (A.NonZeroInteger(0)) // error FS1093: The union cases or fields of the type 'NonZeroInteger' are not accessible from this code location
let wrong = A.NonZeroInteger.Create(0) // None
let right = A.NonZeroInteger.Create(-1) // Some
wrong |> Option.iter (fun x -> x.Print) // Doesn't print anything
right |> Option.iter (fun x -> x.Print) // Prints -1
The private
constructor prevents anyone outside your module to construct a NonZeroInteger
without going through your Create function.
This makes the code quite verbose and slow, but safe. So there's definitely a tradeoff here.