Per la vostra domanda specifica come scrivere la vostra funzione isValidUnitValue
, la risposta è:
let inline isValidUnitValue myUnit = myUnit > LanguagePrimitives.GenericZero
Quindi non c'è bisogno di definire un'Unione Discriminazione in.
Riguardo alla domanda iniziale se è possibile definire una funzione che è sia generica su tipo di dati e unità di misura come dropUnit
la risposta breve è no. Se tale funzione esiste, avrebbe una firma come 'a<'b> -> 'a
e per rappresentarlo il sistema di tipi dovrebbe implementare tipi più alti.
Tuttavia ci sono trucchi che utilizzano il sovraccarico e in linea:
1) Utilizzando i sovraccarichi (a la C#)
type UnitDropper =
static member drop (x:sbyte<_> ) = sbyte x
static member drop (x:int16<_> ) = int16 x
static member drop (x:int<_> ) = int x
static member drop (x:int64<_> ) = int64 x
static member drop (x:decimal<_>) = decimal x
static member drop (x:float32<_>) = float32 x
static member drop (x:float<_> ) = float x
[<Measure>] type m
let x = UnitDropper.drop 2<m> + 3
Ma questo non è davvero una funzione generica, non è possibile scrivere qualcosa di generico su in alto.
> let inline dropUnitAndAdd3 x = UnitDropper.drop x + 3 ;;
-> error FS0041: A unique overload for method 'drop' could not be determined ...
2) Uso di linea, un trucco comune è ridigitare:
let inline retype (x:'a) : 'b = (# "" x : 'b #)
[<Measure>] type m
let x = retype 2<m> + 3
let inline dropUnitAndAdd3 x = retype x + 3
Il problema è che retype
è troppo generico, che vi permetterà di scrivere:
let y = retype 2.0<m> + 3
Che compila ma non riuscirà in fase di esecuzione.
3) Usando entrambe sovraccarichi e in linea: questo trucco risolverà entrambi i problemi di utilizzo sovraccarico attraverso un tipo intermedio, in questo modo si ottiene sia in fase di compilazione controlli e sarete in grado di definire funzioni generiche :
type DropUnit = DropUnit with
static member ($) (DropUnit, x:sbyte<_> ) = sbyte x
static member ($) (DropUnit, x:int16<_> ) = int16 x
static member ($) (DropUnit, x:int<_> ) = int x
static member ($) (DropUnit, x:int64<_> ) = int64 x
static member ($) (DropUnit, x:decimal<_>) = decimal x
static member ($) (DropUnit, x:float32<_>) = float32 x
static member ($) (DropUnit, x:float<_> ) = float x
let inline dropUnit x = DropUnit $ x
[<Measure>] type m
let x = dropUnit 2<m> + 3
let inline dropUnitAndAdd3 x = dropUnit x + 3
let y = dropUnit 2.0<m> + 3 //fails at compile-time
nell'ultima riga si otterrà un errore di compilazione: FS0001: The type 'int' does not match the type 'float'
un altro vantaggio di questo approccio è che è possibile estendere in un secondo momento con nuovi tipi definendo un me statica mber ($) nella definizione del tipo:
type MyNumericType<[<Measure 'U>]> =
...
static member dropUoM (x:MyNumericType<_>) : MyNumericType = ...
static member ($) (DropUnit, x:MyNumericType<_>) = MyNumericType.dropUoM(x)
Un'altra opzione consiste nell'utilizzare una funzione inline per simulare un'interfaccia. –