18 people like it.
Like the snippet!
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>
More information