我一直在朱莉娅(Julia)中遇到生成的功能,并且遇到了一个奇怪的问题,我不完全理解:我的最终目标将涉及从生成的函数中调用宏(更具体地说 @tullio
) (执行一些取决于输入张量的张量收缩)。但是我一直在遇到问题,我缩小了从生成的函数中调用宏。
为了说明问题,让我们考虑一个非常简单的示例,也会失败:
macro my_add(a,b)
return :($a + $b)
end
function add_one_expr(x::T) where T
y = one(T)
return :( @my_add($x,$y) )
end
@generated function add_one_gen(x::T) where T
y = one(T)
return :( @my_add($x,$y) )
end
使用这些声明,我发现 eval(add_one_expr(2.0))
效果按预期和返回和表达式工作
:(@my_add 2.0 1.0)
,并正确评估 3.0
。
但是评估 add_one_gen(2.0)
返回以下错误:
MethodError: no method matching +(::Type{Float64}, ::Float64)
进行一些研究,我发现 @generated
实际上产生了两个代码,而在一个类型中,变量的类型只能被使用。我认为这是这里正在发生的事情,但我根本不了解发生了什么。它必须是宏和生成功能之间的一些怪异相互作用。
有人可以解释和/或提出解决方案吗?谢谢你!
I have been messing around with generated functions in Julia, and have come to a weird problem I do not understand fully: My final goal would involve calling a macro (more specifically @tullio
) from within a generated function (to perform some tensor contractions that depend on the input tensors). But I have been having problems, which I narrowed down to calling the macro from within the generated function.
To illustrate the problem, let's consider a very simple example that also fails:
macro my_add(a,b)
return :($a + $b)
end
function add_one_expr(x::T) where T
y = one(T)
return :( @my_add($x,$y) )
end
@generated function add_one_gen(x::T) where T
y = one(T)
return :( @my_add($x,$y) )
end
With these declarations, I find that eval(add_one_expr(2.0))
works just as expected and returns and expression
:(@my_add 2.0 1.0)
which correctly evaluates to 3.0
.
However evaluating add_one_gen(2.0)
returns the following error:
MethodError: no method matching +(::Type{Float64}, ::Float64)
Doing some research, I have found that @generated
actually produces two codes, and in one only the types of the variables can be used. I think this is what is happening here, but I do not understand what is happening at all. It must be some weird interaction between macros and generated functions.
Can someone explain and/or propose a solution? Thank you!
发布评论
评论(1)
我发现将生成的函数视为具有两个组件是有帮助的: body 和任何生成的代码(
Quote> Quote..end Quote..End
中的内容)。在编译时对身体进行评估,并且不“知道”值,而仅“知道”类型。因此,对于以X :: T
为参数的生成功能,人体中对x
的任何引用实际上都将指向类型t
。这可能会很困惑。为了使事情变得更清晰,我建议只有 指的是类型,而不是价值。这是一个示例:
插值
$ x
是指“从身体上拿出x
(涉及t
),然后将其拼写。您遵循永不参考身体中值的方法,您可以通过删除
@generated
来测试生成的功能,如下:看起来很合理,但是当我们测试时,我们会得到什么
,所以让我们看看什么宏给人我们
指向
main.x
,它不存在。代码>。I find it helpful to think of generated functions as having two components: the body and any generated code (the stuff inside a
quote..end
). The body is evaluated at compile time, and doesn't "know" the values, only the types. So for a generated function takingx::T
as an argument, any references tox
in the body will actually point to the typeT
. This can be very confusing. To make things clearer, I recommend the body only refer to types, never to values.Here's a little example:
The interpolated
$x
means "take thex
from the body (which refers toT
) and splice it in.If you follow the approach of never referring to values in the body, you can test generated functions by removing the
@generated
, like this:That looks reasonable, but when we test it we get
So let's see what the macro gives us
It's pointing to
Main.x
, which doesn't exist. The macro is being too eager, and we need to delay its evaluation. The standard way to do this is withesc
. So finally, this works: