2013-01-05 3 views
5

데이터 유형과 측정 단위에 대해 일반적인 함수를 정의 할 수 있습니까?f # 일반 유형에 대한 일반 단위

let inline dropUnit (x : 'a<_>) = x :> typeof(a) 

생각을 : 내가 할 싶지만, 컴파일되지 않습니다 (이 것 생각조차 측정 현재의 단위없이,하지만 난 내가하고 싶은 것을 전달 믿는) 무엇 예, 여기에 몇 가지 측정 단위를 정의했습니다. "kg"와 "L"과 dicriminated 조합 :

type Unit = 
    | Weight of float<kg> 
    | Volume of float <l> 

과 나는 같은 것을 할 싶습니다

let isValidUnitValue myUnit = 
    match myUnit with 
     | Weight(x) -> (dropUnit x) > 0. 
     | Volume(x) -> (dropUnit x) > 0. 

나는이 특별한 경우에 그냥 사용할 수 있다는 것을 알고를

let dropUnit (x : float<_>) = (float) x 

하지만 위의 글을 쓰는 동안 나는 일반적인 경우에 대해 궁금해하기 시작했습니다. 당신의 isValidUnitValue 함수를 작성하는 방법을 특정 질문에 대한

+0

다른 옵션은 인라인 함수를 사용하여 인터페이스를 위장하는 것입니다. –

답변

7

, 대답은 :

let inline isValidUnitValue myUnit = myUnit > LanguagePrimitives.GenericZero 

그래서 당신이 차별 연합 (EU)을 정의 할 필요가 없습니다.

원래 질문에 대해서는 데이터 유형과 측정 단위에 대해 일반적인 함수를 정의 할 수 있는지 여부는 dropUnit과 같이 짧습니다. 그러한 함수가 존재한다면 그것은 'a<'b> -> 'a과 같은 서명을 가지며 그것을 나타 내기 위해 타입 시스템은 더 높은 종류를 구현해야한다.

그러나 과부하 및 인라인 사용하는 트릭이있다 : 오버로드를 사용

1) (라 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 ... 


2), 공통의 비결은 다시 입력한다 :

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 

런타임에는 컴파일되지만 실패합니다.과부하 및 인라인 모두 사용


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'

이 방법의 또 다른 장점은 당신이 나를 정적을 정의하여 새로운 유형 나중에 확장 할 수 있다는 것입니다 다음과 같이 유형 정의에서 mber ($)를 입력하십시오.

type MyNumericType<[<Measure 'U>]> = 
    ... 
    static member dropUoM (x:MyNumericType<_>) : MyNumericType = ... 
    static member ($) (DropUnit, x:MyNumericType<_>) = MyNumericType.dropUoM(x) 
+0

위대한 답변, 감사합니다! – Bram

관련 문제