Is it possible to create a discriminatory union across a tag's unit of measure in F #?
Is it possible to create a discriminatory join type across a tag unit in F #?
I want to write sth. eg:
type DomainObject =
| Pixel of int
| ScaledPixel of int
| Centimeter of float
| Unset
let var1 = 10<px> // should equal: let var1 = Pixel(10)
let var2 = 0<us> // should equal: let var2 = Unset
let process sth =
match sth with
| Pixel(p) -> ...
| Centimeter(c) -> ...
// etc.
With NumericLiterals, such things are possible. But then only a small number of literals can be used, such as Neil P. showed .
source to share
As I said in the comment, the simple answer is no.
In a sense, you are trying to use a single F # function (units) to emulate a function that might exist in other languages (suffix operators), which is probably a bad thing, because (even if it was possible) the resulting code will be pretty confusing.
If you just want to change the order of the arguments so that the number comes before the device name, you can use the pipe operator and write:
let var1 = 10 |> Pixel
let var2 = Unset
This essentially gives you the ability to write "suffix operators", but using standard F # idioms.
source to share
I don't think this special combination is possible, but you can go with smart constructors if you like:
module Domain =
[<Measure>] type px
[<Measure>] type spx
[<Measure>] type cm
// ...
type DomainObject =
| Pixel of float<px>
| ScaledPixel of float<spx>
| Centimeter of float<cm>
| Unset
let inline pixel f = Pixel <| float f * 1.0<px>
let inline scaledPixel f = ScaledPixel <| float f * 1.0<spx>
let unset = Unset
// ...
let var1 = pixel 10
let var2 = unset
let process sth =
match sth with
| Pixel(p) -> ...
| Centimeter(c) -> ...
// etc.
I think this is reasonably close - if you want, you can make the constructors private and add active templates (to re-match the templates) or accessories to completely encapsulate the implementation details.
If you're interested, you can even add (+)
, (-)
...
PS: a built-in function should do functions that work on all kinds of numeric values;)
PPS: I played around a bit, and the problem is really (as mentioned in the link you gave) that you can only have a very limited set of "suffixes", namely Q, R, Z, I, N, and G
) - for example this kind works:
module NumericLiteralQ =
open Domain
let inline FromZero() = Pixel 0.0<px>
let inline FromOne() = Pixel 1.0<px>
let inline FromString (s:string) =
System.Double.Parse s * 1.0<px> |> Pixel
let inline FromInt32 (n:int) =
1.0<px> * float n |> Pixel
let inline FromInt64 (n:int64) =
1.0<px> * float n |> Pixel
but i think it is very difficult to write
let p = 5Q
instead
let p = pixel 5
or
let p = 5 |> pixel
source to share