3 people like it.

Generalized Units of Measure Revisited (using method overloading)

An approach to using annotated types in F# using overloaded methods.

Core Definition

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
#nowarn "42"

open System

[<MeasureAnnotatedAbbreviation>] type bool<[<Measure>] 'm> = bool
[<MeasureAnnotatedAbbreviation>] type uint64<[<Measure>] 'm> = uint64
[<MeasureAnnotatedAbbreviation>] type Guid<[<Measure>] 'm> = Guid
[<MeasureAnnotatedAbbreviation>] type string<[<Measure>] 'm> = string
[<MeasureAnnotatedAbbreviation>] type TimeSpan<[<Measure>] 'm> = TimeSpan
[<MeasureAnnotatedAbbreviation>] type DateTime<[<Measure>] 'm> = DateTime
[<MeasureAnnotatedAbbreviation>] type DateTimeOffset<[<Measure>] 'm> = DateTimeOffset

module private Unsafe =
    let inline cast<'a, 'b> (a : 'a) : 'b = (# "" a : 'b #)

type UnitOfMeasure =

    static member inline tag<[<Measure>]'m> (x : bool) : bool<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : int) : int<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : int64) : int64<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : uint64) : uint64<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : float) : float<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : decimal) : decimal<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : Guid) : Guid<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : string) : string<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : TimeSpan) : TimeSpan<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : DateTime) : DateTime<'m> = Unsafe.cast x
    static member inline tag<[<Measure>]'m> (x : DateTimeOffset) : DateTimeOffset<'m> = Unsafe.cast x

    static member inline untag<[<Measure>]'m> (x : bool<'m>) : bool = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : int<'m>) : int = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : int64<'m>) : int64 = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : uint64<'m>) : uint64 = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : float<'m>) : float = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : decimal<'m>) : decimal = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : Guid<'m>) : Guid = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : string<'m>) : string = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : TimeSpan<'m>) : TimeSpan = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : DateTime<'m>) : DateTime = Unsafe.cast x
    static member inline untag<[<Measure>]'m> (x : DateTimeOffset<'m>) : DateTimeOffset = Unsafe.cast x

    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : bool<'m1>) : bool<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : int<'m1>) : int<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : int64<'m1>) : int64<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : uint64<'m1>) : uint64<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : float<'m1>) : float<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : decimal<'m1>) : decimal<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : Guid<'m1>) : Guid<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : string<'m1>) : string<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : TimeSpan<'m1>) : TimeSpan<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : DateTime<'m1>) : DateTime<'m2> = Unsafe.cast x
    static member inline cast<[<Measure>]'m1, [<Measure>]'m2> (x : DateTimeOffset<'m1>) : DateTimeOffset<'m2> = Unsafe.cast x

Examples

1: 
2: 
3: 
4: 
5: 
6: 
[<Measure>] type m
[<Measure>] type n

let x = UnitOfMeasure.tag<m> "string"
let y = UnitOfMeasure.cast<m,n> x
let z = UnitOfMeasure.untag y
namespace System
Multiple items
type MeasureAnnotatedAbbreviationAttribute =
  inherit Attribute
  new : unit -> MeasureAnnotatedAbbreviationAttribute

Full name: Microsoft.FSharp.Core.MeasureAnnotatedAbbreviationAttribute

--------------------
new : unit -> MeasureAnnotatedAbbreviationAttribute
Multiple items
type bool = Boolean

Full name: Microsoft.FSharp.Core.bool

--------------------
type bool<'m> = bool

Full name: Script.bool<_>
Multiple items
type MeasureAttribute =
  inherit Attribute
  new : unit -> MeasureAttribute

Full name: Microsoft.FSharp.Core.MeasureAttribute

--------------------
new : unit -> MeasureAttribute
Multiple items
val uint64 : value:'T -> uint64 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.uint64

--------------------
type uint64 = UInt64

Full name: Microsoft.FSharp.Core.uint64

--------------------
type uint64<'m> = uint64

Full name: Script.uint64<_>
Multiple items
type Guid =
  struct
    new : b:byte[] -> Guid + 4 overloads
    member CompareTo : value:obj -> int + 1 overload
    member Equals : o:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member ToByteArray : unit -> byte[]
    member ToString : unit -> string + 2 overloads
    static val Empty : Guid
    static member NewGuid : unit -> Guid
    static member Parse : input:string -> Guid
    static member ParseExact : input:string * format:string -> Guid
    ...
  end

Full name: System.Guid

--------------------
type Guid<'m> = Guid

Full name: Script.Guid<_>

--------------------
Guid()
Guid(b: byte []) : unit
Guid(g: string) : unit
Guid(a: int, b: int16, c: int16, d: byte []) : unit
Guid(a: uint32, b: uint16, c: uint16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
Guid(a: int, b: int16, c: int16, d: byte, e: byte, f: byte, g: byte, h: byte, i: byte, j: byte, k: byte) : unit
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = String

Full name: Microsoft.FSharp.Core.string

--------------------
type string<'m> = string

Full name: Script.string<_>
Multiple items
type TimeSpan =
  struct
    new : ticks:int64 -> TimeSpan + 3 overloads
    member Add : ts:TimeSpan -> TimeSpan
    member CompareTo : value:obj -> int + 1 overload
    member Days : int
    member Duration : unit -> TimeSpan
    member Equals : value:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member Hours : int
    member Milliseconds : int
    member Minutes : int
    ...
  end

Full name: System.TimeSpan

--------------------
type TimeSpan<'m> = TimeSpan

Full name: Script.TimeSpan<_>

--------------------
TimeSpan()
TimeSpan(ticks: int64) : unit
TimeSpan(hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int) : unit
Multiple items
type DateTime =
  struct
    new : ticks:int64 -> DateTime + 10 overloads
    member Add : value:TimeSpan -> DateTime
    member AddDays : value:float -> DateTime
    member AddHours : value:float -> DateTime
    member AddMilliseconds : value:float -> DateTime
    member AddMinutes : value:float -> DateTime
    member AddMonths : months:int -> DateTime
    member AddSeconds : value:float -> DateTime
    member AddTicks : value:int64 -> DateTime
    member AddYears : value:int -> DateTime
    ...
  end

Full name: System.DateTime

--------------------
type DateTime<'m> = DateTime

Full name: Script.DateTime<_>

--------------------
DateTime()
   (+0 other overloads)
DateTime(ticks: int64) : unit
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : unit
   (+0 other overloads)
Multiple items
type DateTimeOffset =
  struct
    new : dateTime:DateTime -> DateTimeOffset + 5 overloads
    member Add : timeSpan:TimeSpan -> DateTimeOffset
    member AddDays : days:float -> DateTimeOffset
    member AddHours : hours:float -> DateTimeOffset
    member AddMilliseconds : milliseconds:float -> DateTimeOffset
    member AddMinutes : minutes:float -> DateTimeOffset
    member AddMonths : months:int -> DateTimeOffset
    member AddSeconds : seconds:float -> DateTimeOffset
    member AddTicks : ticks:int64 -> DateTimeOffset
    member AddYears : years:int -> DateTimeOffset
    ...
  end

Full name: System.DateTimeOffset

--------------------
type DateTimeOffset<'m> = DateTimeOffset

Full name: Script.DateTimeOffset<_>

--------------------
DateTimeOffset()
DateTimeOffset(dateTime: DateTime) : unit
DateTimeOffset(ticks: int64, offset: TimeSpan) : unit
DateTimeOffset(dateTime: DateTime, offset: TimeSpan) : unit
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, offset: TimeSpan) : unit
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, offset: TimeSpan) : unit
DateTimeOffset(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, calendar: Globalization.Calendar, offset: TimeSpan) : unit
val private cast : a:'a -> 'b

Full name: Script.Unsafe.cast
val a : 'a
type UnitOfMeasure =
  static member cast : x:bool<'m1> -> bool<'m2>
  static member cast : x:int<'m1> -> int<'m2>
  static member cast : x:int64<'m1> -> int64<'m2>
  static member cast : x:uint64<'m1> -> uint64<'m2>
  static member cast : x:float<'m1> -> float<'m2>
  static member cast : x:decimal<'m1> -> decimal<'m2>
  static member cast : x:Guid<'m1> -> Guid<'m2>
  static member cast : x:string<'m1> -> string<'m2>
  static member cast : x:TimeSpan<'m1> -> TimeSpan<'m2>
  static member cast : x:DateTime<'m1> -> DateTime<'m2>
  ...

Full name: Script.UnitOfMeasure
static member UnitOfMeasure.tag : x:bool -> bool<'m>

Full name: Script.UnitOfMeasure.tag
val x : bool
module Unsafe

from Script
static member UnitOfMeasure.tag : x:int -> int<'m>

Full name: Script.UnitOfMeasure.tag
val x : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
static member UnitOfMeasure.tag : x:int64 -> int64<'m>

Full name: Script.UnitOfMeasure.tag
val x : int64
Multiple items
val int64 : value:'T -> int64 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int64

--------------------
type int64 = Int64

Full name: Microsoft.FSharp.Core.int64

--------------------
type int64<'Measure> = int64

Full name: Microsoft.FSharp.Core.int64<_>
static member UnitOfMeasure.tag : x:uint64 -> uint64<'m>

Full name: Script.UnitOfMeasure.tag
val x : uint64
static member UnitOfMeasure.tag : x:float -> float<'m>

Full name: Script.UnitOfMeasure.tag
val x : float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
static member UnitOfMeasure.tag : x:decimal -> decimal<'m>

Full name: Script.UnitOfMeasure.tag
val x : decimal
Multiple items
val decimal : value:'T -> decimal (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.decimal

--------------------
type decimal = Decimal

Full name: Microsoft.FSharp.Core.decimal

--------------------
type decimal<'Measure> = decimal

Full name: Microsoft.FSharp.Core.decimal<_>
static member UnitOfMeasure.tag : x:Guid -> Guid<'m>

Full name: Script.UnitOfMeasure.tag
val x : Guid
static member UnitOfMeasure.tag : x:string -> string<'m>

Full name: Script.UnitOfMeasure.tag
val x : string
static member UnitOfMeasure.tag : x:TimeSpan -> TimeSpan<'m>

Full name: Script.UnitOfMeasure.tag
val x : TimeSpan
static member UnitOfMeasure.tag : x:DateTime -> DateTime<'m>

Full name: Script.UnitOfMeasure.tag
val x : DateTime
static member UnitOfMeasure.tag : x:DateTimeOffset -> DateTimeOffset<'m>

Full name: Script.UnitOfMeasure.tag
val x : DateTimeOffset
static member UnitOfMeasure.untag : x:bool<'m> -> bool

Full name: Script.UnitOfMeasure.untag
val x : bool<'m>
static member UnitOfMeasure.untag : x:int<'m> -> int

Full name: Script.UnitOfMeasure.untag
val x : int<'m>
static member UnitOfMeasure.untag : x:int64<'m> -> int64

Full name: Script.UnitOfMeasure.untag
val x : int64<'m>
static member UnitOfMeasure.untag : x:uint64<'m> -> uint64

Full name: Script.UnitOfMeasure.untag
val x : uint64<'m>
static member UnitOfMeasure.untag : x:float<'m> -> float

Full name: Script.UnitOfMeasure.untag
val x : float<'m>
static member UnitOfMeasure.untag : x:decimal<'m> -> decimal

Full name: Script.UnitOfMeasure.untag
val x : decimal<'m>
static member UnitOfMeasure.untag : x:Guid<'m> -> Guid

Full name: Script.UnitOfMeasure.untag
val x : Guid<'m>
static member UnitOfMeasure.untag : x:string<'m> -> string

Full name: Script.UnitOfMeasure.untag
val x : string<'m>
static member UnitOfMeasure.untag : x:TimeSpan<'m> -> TimeSpan

Full name: Script.UnitOfMeasure.untag
val x : TimeSpan<'m>
static member UnitOfMeasure.untag : x:DateTime<'m> -> DateTime

Full name: Script.UnitOfMeasure.untag
val x : DateTime<'m>
static member UnitOfMeasure.untag : x:DateTimeOffset<'m> -> DateTimeOffset

Full name: Script.UnitOfMeasure.untag
val x : DateTimeOffset<'m>
static member UnitOfMeasure.cast : x:bool<'m1> -> bool<'m2>

Full name: Script.UnitOfMeasure.cast
val x : bool<'m1>
static member UnitOfMeasure.cast : x:int<'m1> -> int<'m2>

Full name: Script.UnitOfMeasure.cast
val x : int<'m1>
static member UnitOfMeasure.cast : x:int64<'m1> -> int64<'m2>

Full name: Script.UnitOfMeasure.cast
val x : int64<'m1>
static member UnitOfMeasure.cast : x:uint64<'m1> -> uint64<'m2>

Full name: Script.UnitOfMeasure.cast
val x : uint64<'m1>
static member UnitOfMeasure.cast : x:float<'m1> -> float<'m2>

Full name: Script.UnitOfMeasure.cast
val x : float<'m1>
static member UnitOfMeasure.cast : x:decimal<'m1> -> decimal<'m2>

Full name: Script.UnitOfMeasure.cast
val x : decimal<'m1>
static member UnitOfMeasure.cast : x:Guid<'m1> -> Guid<'m2>

Full name: Script.UnitOfMeasure.cast
val x : Guid<'m1>
static member UnitOfMeasure.cast : x:string<'m1> -> string<'m2>

Full name: Script.UnitOfMeasure.cast
val x : string<'m1>
static member UnitOfMeasure.cast : x:TimeSpan<'m1> -> TimeSpan<'m2>

Full name: Script.UnitOfMeasure.cast
val x : TimeSpan<'m1>
static member UnitOfMeasure.cast : x:DateTime<'m1> -> DateTime<'m2>

Full name: Script.UnitOfMeasure.cast
val x : DateTime<'m1>
static member UnitOfMeasure.cast : x:DateTimeOffset<'m1> -> DateTimeOffset<'m2>

Full name: Script.UnitOfMeasure.cast
val x : DateTimeOffset<'m1>
[<Measure>]
type m

Full name: Script.m
[<Measure>]
type n

Full name: Script.n
val x : string<m>

Full name: Script.x
static member UnitOfMeasure.tag : x:bool -> bool<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:int -> int<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:int64 -> int64<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:uint64 -> uint64<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:float -> float<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:decimal -> decimal<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:Guid -> Guid<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:string -> string<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:TimeSpan -> TimeSpan<'m>
   (+0 other overloads)
static member UnitOfMeasure.tag : x:DateTime -> DateTime<'m>
   (+0 other overloads)
val y : string<n>

Full name: Script.y
static member UnitOfMeasure.cast : x:bool<'m1> -> bool<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:int<'m1> -> int<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:int64<'m1> -> int64<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:uint64<'m1> -> uint64<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:float<'m1> -> float<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:decimal<'m1> -> decimal<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:Guid<'m1> -> Guid<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:string<'m1> -> string<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:TimeSpan<'m1> -> TimeSpan<'m2>
   (+0 other overloads)
static member UnitOfMeasure.cast : x:DateTime<'m1> -> DateTime<'m2>
   (+0 other overloads)
val z : string

Full name: Script.z
static member UnitOfMeasure.untag : x:bool<'m> -> bool
   (+0 other overloads)
static member UnitOfMeasure.untag : x:int<'m> -> int
   (+0 other overloads)
static member UnitOfMeasure.untag : x:int64<'m> -> int64
   (+0 other overloads)
static member UnitOfMeasure.untag : x:uint64<'m> -> uint64
   (+0 other overloads)
static member UnitOfMeasure.untag : x:float<'m> -> float
   (+0 other overloads)
static member UnitOfMeasure.untag : x:decimal<'m> -> decimal
   (+0 other overloads)
static member UnitOfMeasure.untag : x:Guid<'m> -> Guid
   (+0 other overloads)
static member UnitOfMeasure.untag : x:string<'m> -> string
   (+0 other overloads)
static member UnitOfMeasure.untag : x:TimeSpan<'m> -> TimeSpan
   (+0 other overloads)
static member UnitOfMeasure.untag : x:DateTime<'m> -> DateTime
   (+0 other overloads)

More information

Link:http://fssnip.net/7UH
Posted:5 years ago
Author:Eirik Tsarpalis
Tags: units of measure