gpt4 book ai didi

f# 泛型上的泛型单位

转载 作者:行者123 更新时间:2023-12-04 18:11:07 25 4
gpt4 key购买 nike

是否可以定义一个对数据类型和度量单位都通用的函数?例如,我想做什么,但不编译(尽管即使没有度量单位也不会,但我相信我传达了我想做的事情):

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

这里的想法是我已经定义了一些度量单位,例如"kg"和 "l"以及一个有区别的联合:
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函数,答案是:

let inline isValidUnitValue myUnit = myUnit > LanguagePrimitives.GenericZero
所以你不需要定义一个歧视联盟。
关于最初的问题,是否可以定义一个函数,它对数据类型和度量单位都是通用的,如 dropUnit最简洁的答案是不。如果存在这样的函数,它将具有类似 'a<'b> -> 'a 的签名。并且为了表示它,类型系统应该实现更高的种类。
但是有使用重载和内联的技巧:
  • 使用重载(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
    但这并不是一个真正的通用函数,你不能在它上面写一些通用的东西。
    > 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'这种方法的另一个优点是,您可以稍后通过在类型定义中定义静态成员 ($) 来使用新类型扩展它,如下所示:
        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 的用例。

    关于f# 泛型上的泛型单位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14168536/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com