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 .

+3


source to share


2 answers


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.

+5


source


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

      

+3


source







All Articles