mih*_*mek 8 types function julia
考虑下面的代码:
julia> function foo(x::Float64)::Float64
return 2x
end
foo (generic function with 1 method)
julia> typeof(foo)
typeof(foo)
Run Code Online (Sandbox Code Playgroud)
一定有一个原因为什么typeof(foo)不返回更有意义的东西,例如(Float64 -> Float64)。它是什么?
我在看Zygote代码时发现了这个。
当你写作时,你是在询问函数typeof(foo)的类型。该函数可以有多个方法(类型是否稳定——但这是另一个问题),并且这些方法具有不同的签名(参数类型),对于其中一些方法,编译器可能能够推断出返回类型,而对于其他方法,编译器可能能够推断出返回类型它可能不会(并且你不应该依赖它AFAICT——只是假设大多数时候编译器都做正确的工作)。 foo
举个例子,考虑一下这段代码(1 个函数,2 个方法):
\njulia> f1(::Int) = 1\nf1 (generic function with 1 method)\n\njulia> f1(::Bool) = 2\nf1 (generic function with 2 methods)\n\njulia> typeof(f1)\ntypeof(f1)\n\njulia> methods(f1)\n# 2 methods for generic function "f1":\n[1] f1(::Bool) in Main at REPL[21]:1\n[2] f1(::Int64) in Main at REPL[20]:1\nRun Code Online (Sandbox Code Playgroud)\n现在参考您所写的返回值规范:
\nf(x)::Float64 = x\nRun Code Online (Sandbox Code Playgroud)\n它们不仅仅是断言。朱莉娅实际上做了两件事:
\n这与我的功能相关f上面的函数中您可以编写:
julia> f(1)\n1.0\n\njulia> f(true)\n1.0\nRun Code Online (Sandbox Code Playgroud)\n您可以看到发生了转换(不仅仅是断言)。
\n这种风格与类型不稳定的代码非常相关(例如,当使用DataFrame类型不稳定的数据结构时),因为这样的断言可以帮助“打破代码中的类型不稳定链”(如果代码中的某些部分不是类型稳定的) 。
编辑
\n\n\n难道不能通过使用来解决吗
\nUnion{Int, Bool} -> Int?例如f1,对于?对于类型不稳定性也同样如此?我想这将需要针对所有输入类型编译代码,从而失去 JIT 的优势?
但是如果 for IntanInt被返回并且 for BoolaBool被返回呢?考虑例如一个identity函数。
还可以举下面的例子:
\njulia> f(x) = ("a",)[x]\nf (generic function with 1 method)\n\njulia> @code_warntype f(2)\nVariables\n #self#::Core.Compiler.Const(f, false)\n x::Int64\n\nBody::String\n1 \xe2\x94\x80 %1 = Core.tuple("a")::Core.Compiler.Const(("a",), false)\n\xe2\x94\x82 %2 = Base.getindex(%1, x)::String\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 return %2\n\njulia> Base.return_types(f)\n1-element Array{Any,1}:\n Any\n\nRun Code Online (Sandbox Code Playgroud)\n@code_warntype正确地识别出如果此函数返回某些内容,则保证是String,但是,return_types正如 Przemys\xc5\x82aw 建议的那样告诉您它是Any。所以你可以看到这是一件很难的事情,你不应该盲目依赖它 - 只需假设由编译器来决定它可以推断什么。特别是 - 出于性能原因 - 编译器可能会放弃进行推理,即使理论上这是可能的。
在你的问题中,你可能指的是例如Haskell提供的内容,但在Haskell中,有一个限制,即函数的返回值可能不依赖于传递的参数的运行时值,而仅依赖于参数的类型。Julia 中没有这样的限制。
\n让我给你展示一些东西来进行比较:
julia> struct var"typeof(foo)" end
julia> const foo = var"typeof(foo)"()
var"typeof(foo)"()
julia> (::var"typeof(foo)")(x::Float64) = 2x
julia> (::var"typeof(foo)")(x::String) = x * x
julia> foo(0.2)
0.4
julia> foo("sdf")
"sdfsdf"
julia> typeof(foo)
var"typeof(foo)"
Run Code Online (Sandbox Code Playgroud)
这大约是内部发生的情况。当您编写 时function foo,编译器会生成类似匿名单例结构的内容,其中包含内部名称、typeof(foo)和实例foo的匿名单例结构之类的内容。然后,所有方法或“调度组合”都在其类型上注册。
你看,给出像or 这样的foo类型是没有意义的——函数Float -> FloatString -> String只是单例值,其类型是结构的类型。该结构对其方法一无所知(在真正的编译器内部,它当然知道,但不是以类型系统可访问的方式)。
理论上,您可以设计一个系统,其中函数的所有方法都收集在某种联合类型中,但由于函数类型在其共域中是协变的,而在其域中是逆变的,所以这会变得非常庞大且复杂。所以它还没有完成。话虽这么说,人们正在讨论引用方法实例本身类型的语法,这正是您所想到的。