'making type aliases non interchangeable in F#

Here is an example:

type T =
    TimeSpan
    
let x (y: T) =
    printfn $"{y}"

let a:TimeSpan = TimeSpan.FromSeconds(3)
let b:T        = TimeSpan.FromSeconds(3)

let a' = x a
let b' = x b

In this case, I want to make a type which is identical to TimeSpan but is NOT TimeSpan. I would like the ability to cast between one another, but not be equivalent when it comes to function signatures.

For example, the x function takes a type T, so the line:

let a' = x a

shouldn't compile because it passes a TimeSpan

but the line:

let b' = x b

is passing a type T and should compile.

Is there some simple and elegant way to achieve this? the goal for this is to be able to use TimeSpan, but constrain the options to some specific values.

Right now I'm using an enum that needs to be convert to TimeSpan and then TimeSpans get reconvert to the enum if they exist in the enum. It's rather ugly.

f#


Solution 1:[1]

I want to make a type which is identical to TimeSpan but is NOT TimeSpan. I would like the ability to cast between one another, but not be equivalent when it comes to function signatures.

That's not possible in F#, but the usual way to accomplish something like it is with single-case unions, such as:

type T = T of TimeSpan

let x (T y) =
    printfn "%A" y   // y is the inner TimeSpan value

let a:TimeSpan = TimeSpan.FromSeconds(3.0)
let b:T        = TimeSpan.FromSeconds(3.0) |> T

let a' = x a   // doesn't compile
let b' = x b

The general issue I think you're dealing with here is called "primitive obsession".

Solution 2:[2]

You can either use a single cased DU, or you can use units of measure to do this. UMX specifically supports timespans.

https://github.com/fsprojects/FSharp.UMX

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 VoronoiPotato