Suppose I have a type parameter M
, which is a deterministic function of another parameter N
. I would like to save it in the type, but not sure how to make this type stable without @pure
. Is that even possible? MWE:
@inline calcM(x) = 2^x
struct MyType{N,M} end
function MyType{N}() where N
@assert N β₯ 0
M = calcM(N)
MyType{N,M}()
end
@code_warntype MyType{3}() # not inferred of course
2 Likes
I donβt have an answer to this, but this is definitely not an of course
.
If you just simplify the function to e.g.
@inline calcM(x) = x^2
then compiler decides to evaluate it during compile time:
julia> @code_warntype MyType{3}()
Variables
#self#::Type{MyType{3, M} where M}
M::Int64
Body::MyType{3, 9}
1 β Core.NewvarNode(:(M))
β %2 = ($(Expr(:static_parameter, 1)) β₯ 0)::Core.Const(true)
β %2
βββ goto #3
2 β Core.Const(:(Base.AssertionError("N β₯ 0")))
βββ Core.Const(:(Base.throw(%5)))
3 β (M = Main.calcM($(Expr(:static_parameter, 1))))
β %8 = Core.apply_type(Main.MyType, $(Expr(:static_parameter, 1)), M::Core.Const(9))::Core.Const(MyType{3, 9})
β %9 = (%8)()::Core.Const(MyType{3, 9}())
βββ return %9
Along those lines: You can nudge the compiler to make your example type stable by adding an additional type check.
julia> function MyType{N}() where N
@assert N β₯ 0
T = typeof(N)
M = calcM(N)::T
MyType{N,M}()
end
julia> @code_warntype MyType{3}()
Variables
#self#::Type{MyType{3, M} where M}
M::Int64
T::Type{Int64}
Body::MyType{3, 9}
1 β Core.NewvarNode(:(M))
β Core.NewvarNode(:(T))
β %3 = ($(Expr(:static_parameter, 1)) β₯ 0)::Core.Const(true)
β %3
βββ goto #3
2 β Core.Const(:(Base.AssertionError("N β₯ 0")))
βββ Core.Const(:(Base.throw(%6)))
3 β (T = Main.typeof($(Expr(:static_parameter, 1))))
β %9 = Main.calcM($(Expr(:static_parameter, 1)))::Core.Const(9)
β (M = Core.typeassert(%9, T::Core.Const(Int64)))
β %11 = Core.apply_type(Main.MyType, $(Expr(:static_parameter, 1)), M::Core.Const(9))::Core.Const(MyType{3, 9})
β %12 = (%11)()::Core.Const(MyType{3, 9}())
βββ return %12
1 Like
Thanks,
@inline calcM(x) = 1 << x
also works.
I am just not sure what the rules are. Is it because 2^x
goes to power_by_squaring
, which is recursive?
cgeoga
July 22, 2021, 3:15pm
4
I could be wrong here, but isnβt this a good use case for generated functions?
@generated function MyType{N}() where N
M = calcM(N)
return quote
MyType{N,$M}()
end
end
seems to work okay with inference.
3 Likes