Val类型是怎么用的,有点看不懂


#1

Val类型是干什么的,doc里的解释没看不懂,来个大佬解释下

Return Val{c}(), which contains no run-time data. Types like this can be used to pass the information between functions through the value c, which must be an isbits value. The intent of this construct is to be able to dispatch on constants directly (at compile time) without having to test the value of the constant at run time.

#2

举一个并不恰当的例子,如果你有个函数要对向量行操作,若你给定一个参数2和3则对应着不同method。我们知道julia是根据类型进行分派,但2和3都是Int的值,julia不能对2和3进行分派。若要根据2和3进行分派的话,则需要用Val{2}和Val{3}的‘值类型’:

function test(a::Vector{Int}, ::Val{2})
    return 2 .* a
end

function test(a::Vector{Int}, ::Val{3})
    return a.^3
end

a = [1, 2, 3]
println(test(a, Val(2)))
println(test(a, Val(3)))

这样会输出:
[2, 4, 6]
[1, 8, 27]
这里需要注意Val{2}的类型是DateType, Val{2}()为Val{2}实例化。

Val(x) = Val{x}()

因此,Val(2)就是直接将Val{2}实例化。
这里的例子并无实际意义,只想表达如何根据2和3进行分派。


#3

谢谢大佬,这个example让我看明白了。:rofl:


#4

Julia 编译器支持 constant propagation 之后,使用 Val 的场景已经非常少了。


#5

这个 constant propagation 是啥情况。上面那个代码我感觉应用场景很多啊,难道现在最新的版本能够支持不加val 的 2或者3的 dispatch吗


#6

直接在function body里用 if-else,编译器会自动剔除dead branch, 无运行时性能损失.

X-ref: 24362, 24011

julia> f(x) = sin(x)
f (generic function with 1 method)

julia> g(x) = cos(x)
g (generic function with 1 method)

julia> h(x, y=1) = y == 1 ? f(x) : g(x) 
h (generic function with 2 methods)

julia> bar(x) = h(x, 2)
bar (generic function with 1 method)

julia> baz(x) = h(x, 1)
baz (generic function with 1 method)

julia> @code_typed bar(3)
CodeInfo(
1 ─ %1 = (Base.sitofp)(Float64, x)::Float64
│   %2 = invoke Base.Math.cos(%1::Float64)::Float64
└──      return %2
) => Float64

julia> @code_typed baz(3)
CodeInfo(
1 ─ %1 = (Base.sitofp)(Float64, x)::Float64
│   %2 = invoke Base.Math.sin(%1::Float64)::Float64
└──      return %2
) => Float64

#7

谢谢,非常感谢:pray:


#8

直接用if-else的话,扩展功能就要动旧代码了啊。所以Val还是有用,本身是种holy trait的use case


#9

:thinking: 典型场景应该比较少,暂时想不到,相对的:

  1. if-else 的典型场景:getproperty(value, name::Symbol) 没有设计成利用 Val 进行 dispatch
  2. trait-based dispatch: 通常会自定义trait type 而不是直接用 Val