我找到:
abs -10
abs -10L
Run Code Online (Sandbox Code Playgroud)
都工作.所以我想知道F#是如何实现这个并在源代码中进行搜索的:
type AbsDynamicImplTable<'T>() =
let AbsDynamic x = AbsDynamicImplTable<_>.Result x
[<CompiledName("Abs")>]
let inline abs (x: ^T) : ^T =
AbsDynamic x
when ^T : ^T = absImpl x
Run Code Online (Sandbox Code Playgroud)
我对这些感到困惑.
正如我所知,在函数中abs
,我们必须将输入与0进行比较,并且对于不同的类型有不同的0.
谢谢.
Tom*_*cek 13
为了向ChaosPandion发布的代码添加一些解释,F#函数的问题abs
是它们可以使用任何数字类型.有没有办法来表达这只是使用F#/.NET泛型-唯一支持的约束是类型参数实现某个接口或有一个构造函数,但对于数值类型没有限制.
因此,F#还支持静态约束(类型参数^a
代替通常'a
),并且这些在编译时使用内联进行处理(这也解释了函数必须的原因inline
).您可以使用内置函数编写具有静态约束的函数,LanguagePrimitives
其中包含许多需要一些约束的有用函数:
> let inline half (num: ^a) : ^a =
LanguagePrimitives.DivideByInt< (^a) > num 2
;;
val inline half : ^a -> ^a
when ^a : (static member DivideByInt : ^a * int -> ^a)
> half 42.0;;
val it : float = 21.0
> half 42.0f;;
val it : float32 = 21.0f
Run Code Online (Sandbox Code Playgroud)
需要注意的是约束推断- DivideByInt
要求该类型有DivideByInt
成员,所以我们的函数需要同样的事情(和它会用自己的类型的工作,如果它有这个成员也是如此,这是非常有用的!).
除此之外,实现abs
使用仅在F#库中允许的两个附加技巧 - 它为不同类型(使用when ^a:int = ....
)指定不同的代码(在内联时使用)和使用Abs
成员的后备情况,因此它将使用任何明确列出的类型或具有Abs
成员的类型.另一个技巧是retype
"更改类型" 的函数,但不包含任何代码 - 唯一的目的是进行代码类型检查,但这可能非常不安全 - 因此仅在F#库中使用.
实际上,Abs表会称之为:
let inline abs_impl (x: ^a) : ^a =
(^a: (static member Abs : ^a -> ^a) (x))
when ^a : int32 = let x : int32 = retype x in if x >= 0 then x else -x
when ^a : float = let x : float = retype x in if x >= 0.0 then x else -x
when ^a : float32 = let x : float32 = retype x in if x >= 0.0f then x else -x
when ^a : int64 = let x : int64 = retype x in if x >= 0L then x else -x
when ^a : nativeint = let x : nativeint = retype x in if x >= 0n then x else -x
when ^a : int16 = let x : int16 = retype x in if x >= 0s then x else -x
when ^a : sbyte = let x : sbyte = retype x in if x >= 0y then x else -x
when ^a : decimal = System.Math.Abs(retype x : decimal)
Run Code Online (Sandbox Code Playgroud)