18 people like it.

Extending units of measure to arbitrary types

Continuing from https://twitter.com/JKPappas/status/558339587719045120, here's an approach on extending units of measure to arbitrary types.

 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: 
let inline (++) (w : ^W when ^W : (static member IsMeasureAbbrev : ^tm * ^t -> unit)) (t : ^t) = (# "" t : ^tm #)
let inline (--) (w : ^W when ^W : (static member IsMeasureAbbrev : ^tm * ^t -> unit)) (tm : ^tm) = (# "" tm : ^t #)

open System

[<MeasureAnnotatedAbbreviation>]
type Guid<[<Measure>] 'Measure> = Guid

[<MeasureAnnotatedAbbreviation>]
type string<[<Measure>] 'Measure> = string

// use member constraints to statically establish a measure relationship between types
type UoM = | UoM 
with
    static member IsMeasureAbbrev(_ : Guid<'Measure>, _ : Guid) = ()
    static member IsMeasureAbbrev(_ : string<'Measure>, _ : string) = ()

[<Measure>]
type processId = class end

[<Measure>]
type taskId = class end

type Entry = { ProcessId : Guid<processId> ; TaskId : Guid<taskId> }
with
    static member New() = { ProcessId = UoM ++ Guid.NewGuid() ; TaskId = UoM ++ Guid.NewGuid() }

match Entry.New () with
| { ProcessId = pid ; TaskId = tid } -> 
    { ProcessId = pid ; TaskId = tid }
//    { ProcessId = pid ; TaskId = pid } // uncomment for type error
val w : 'W (requires member IsMeasureAbbrev)
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val t : 't
val tm : 'tm
namespace System
Multiple items
type MeasureAnnotatedAbbreviationAttribute =
  inherit Attribute
  new : unit -> MeasureAnnotatedAbbreviationAttribute

Full name: Microsoft.FSharp.Core.MeasureAnnotatedAbbreviationAttribute

--------------------
new : unit -> MeasureAnnotatedAbbreviationAttribute
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<'Measure> = 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
type MeasureAttribute =
  inherit Attribute
  new : unit -> MeasureAttribute

Full name: Microsoft.FSharp.Core.MeasureAttribute

--------------------
new : unit -> MeasureAttribute
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<'Measure> = string

Full name: Script.string<_>
Multiple items
union case UoM.UoM: UoM

--------------------
type UoM =
  | UoM
  static member IsMeasureAbbrev : Guid<'Measure> * Guid -> unit
  static member IsMeasureAbbrev : string<'Measure> * string -> unit

Full name: Script.UoM
static member UoM.IsMeasureAbbrev : Guid<'Measure> * Guid -> unit

Full name: Script.UoM.IsMeasureAbbrev
static member UoM.IsMeasureAbbrev : string<'Measure> * string -> unit

Full name: Script.UoM.IsMeasureAbbrev
[<Measure>]
type processId

Full name: Script.processId
[<Measure>]
type taskId

Full name: Script.taskId
type Entry =
  {ProcessId: Guid<processId>;
   TaskId: Guid<taskId>;}
  static member New : unit -> Entry

Full name: Script.Entry
Entry.ProcessId: Guid<processId>
Entry.TaskId: Guid<taskId>
static member Entry.New : unit -> Entry

Full name: Script.Entry.New
Guid.NewGuid() : Guid
static member Entry.New : unit -> Entry
val pid : Guid<processId>
val tid : Guid<taskId>
Raw view Test code New version

More information

Link:http://fssnip.net/pm
Posted:9 years ago
Author:Eirik Tsarpalis
Tags: units of measure