F# GreaterThanZero passing int or decimal
I want to create a function that check if the passed value is greater than zero.
The passed value can be an int or a decimal (ideally a "numeric value").
In the immediate I just started with this:
type number =
| I of int
| D of decimal
type Checker () =
member this.Validate value =
match value with
| I x when x > 0 -> "ok"
| D x when x > 0m -> "ok"
| _ -> "error"
let a = 1f
let b = 1m
//let a_IsValid = Checker().Validate(a) // does not compile, expect number (not int)
//let b_IsValid = Checker().Validate(b) // does not compile, expect number (not decimal)
Found not immediate to pass a "number" so tried something different... I found this article (http://tomasp.net/blog/fsharp-generic-numeric.aspx/) and I thought "static member constraint" is the perfect solution for me. A basic example works as expected:
let inline divideByTwo value =
LanguagePrimitives.DivideByInt value 2
divideByTwo 1f |> ignore
divideByTwo 1m |> ignore
but a different scenario found me very surprised:
type Calculator () =
let divideByTwo value =
LanguagePrimitives.DivideByInt value 2
member this.DivideByTwo value =
LanguagePrimitives.DivideByInt value 2
member this.ValidateGeneric value =
match LanguagePrimitives.GenericGreaterThan value 0m with
| true -> "ok"
| _ -> "error"
//let half = Calculator().DivideByTwo(1) // DivideByInt does not support int !!
// cannot use both the following, the first one will "force" the type, and the other will not work
let a_half = Calculator().DivideByTwo(1f) // ok if used before the "decimal" version
let b_half = Calculator().DivideByTwo(1m) // ok only if comment the previous one
It seems not to work when I want to use more than one type for the passing value.
More than that, the function I need (GenericGreaterThan
) seems to have another "limitation", explained below. The example in the article use DivideByInt
and, as the name said, it divide the passed value by an int, a well defined type. LanguagePrimitives.GenericGreaterThan
needs 2 parameters, a passed value and a fixed one to compare to. The signature of the function as only one generic type for both, so if you pass a type 'T it expect the second one to be 'T too.
I just wants to compare with zero without passing it, but using "0" forced my value to be an int and using "0m" force the value to be a decimal.
There is a simple way to have a function that check if a "numeric" value is greater than "zero" ? Should I use obj and box it .... or use cast ... or stop trying and just use a different function for every type I need ?
[UPDATE]
I tried to use the LanguagePrimitives.GenericZero
as suggested but still not able to have a working solution for my particular scenario.
The real scenario involves an interface and it does not allow me to use inline
. I haven't mentioned because I created a simplest use case as possible, didn't imagined that using a class and/or an interface makes the difference here.
type IValidationCheck =
abstract member Validate: unit -> Result<unit, string>
type NumberIsPositiveCheck (property:string, value) =
interface IValidationCheck with
member (*inline*) this.Validate () = // does not allow me to use "inline"
if value > LanguagePrimitives.GenericZero then Ok() // fail to compile: the type IComparable does not have a get_Zero operator
else Error $"{property} must be greater than zero"
I'm not able to figure out the way to use an inline function inside the class.
Comments
Post a Comment