isValidUnitValue
関数の書き方に関する具体的な質問に対する答えは次のとおりです。
let inline isValidUnitValue myUnit = myUnit > LanguagePrimitives.GenericZero
したがって、DiscriminatedUnionを定義する必要はありません。
dropUnit
元の質問に関しては、データ型に対して一般的であり、短い答えのように測定単位の両方である関数を定義することが可能かどうかはノーです。そのような関数が存在する場合、それはのような署名を持ち、 'a<'b> -> 'a
それを表すために型システムはより高い種類を実装する必要があります。
ただし、オーバーロードとインラインを使用するトリックがあります。
- オーバーロードの使用(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
しかし、これは実際にはジェネリック関数ではなく、その上にジェネリックなものを書くことはできません。
> let inline dropUnitAndAdd3 x = UnitDropper.drop x + 3 ;;
-> error FS0041: A unique overload for method 'drop' could not be determined ...
- インラインを使用する一般的なトリックは、再入力です。
let inline retype (x:'a) : 'b = (# "" x : 'b #)
[<Measure>] type m
let x = retype 2<m> + 3
let inline dropUnitAndAdd3 x = retype x + 3
問題はそれretype
があまりにも一般的であるということです、それはあなたが書くことを可能にするでしょう:
let y = retype 2.0<m> + 3
これはコンパイルされますが、実行時に失敗します。
- オーバーロードとインラインの両方を使用する:このトリックは、中間型を介したオーバーロードを使用することで両方の問題を解決します。これにより、コンパイル時のチェックが両方とも取得され、ジェネリック関数を定義できるようになります。
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
最後の行で、コンパイル時エラーが発生します。FS0001: The type 'int' does not match the type 'float'
このアプローチのもう1つの利点は、次のように型定義で静的メンバー($)を定義することにより、後で新しい型で拡張できることです。
type MyNumericType<[<Measure 'U>]> =
...
static member dropUoM (x:MyNumericType<_>) : MyNumericType = ...
static member ($) (DropUnit, x:MyNumericType<_>) = MyNumericType.dropUoM(x)
- いくつかの一般的な制約を利用する:
let inline retype (x: 'T) : 'U = (# "" x: 'U #)
let inline stripUoM (x: '``Num<'M>``) =
let _ = x * (LanguagePrimitives.GenericOne : 'Num)
retype x :'Num
これは2)に似ていますが、型注釈は必要ありません。制限は、数値タイプでのみ機能することですが、通常はUoMのユースケースです。