I’d like to better understand how to ensure type stability when applying a reduce operation on a collection. In the example below, the CrossProd
type maintains a collection of objects that represent a “cross product”, which are assembled using the cross
method:
abstract type Atom end
struct Cd <: Atom
w::Int64
end
struct CrossProd{T<:Tuple{Vararg{Atom}}} <: Atom
a::T
end
cross(A::Atom, B::Atom) = CrossProd(tuple(A,B))
cross(A::CrossProd, B::Atom) = CrossProd(tuple(A.a..., B))
cross(A::Atom, B::CrossProd) = CrossProd(tuple(A, B.a...))
cross(A::CrossProd, B::CrossProd) = CrossProd(tuple(A.a..., B.a...))
If a fixed number of atoms is collected into a tuple, the result is type stable:
function testcross()
nc = (Cd(1), Cd(2))
nc2 = reduce(cross, nc)
end
julia> @code_warntype testcross()
Variables
#self#::Core.Compiler.Const(testcross, false)
nc::Tuple{Cd,Cd}
nc2::CrossProd{Tuple{Cd,Cd}}
Body::CrossProd{Tuple{Cd,Cd}}
1 ─ %1 = Main.Cd(1)::Core.Compiler.Const(Cd(1), false)
│ %2 = Main.Cd(2)::Core.Compiler.Const(Cd(2), false)
│ (nc = Core.tuple(%1, %2))
│ %4 = Main.reduce(Main.cross, nc::Core.Compiler.Const((Cd(1), Cd(2)), false))::Core.Compiler.Const(CrossProd{Tuple{Cd,Cd}}((Cd(1), Cd(2))), false)
│ (nc2 = %4)
└── return %4
Now suppose we wish to collect an arbitrary number of atoms. I might do this as follows, which isn’t type stable:
function testcross(n)
nc = ntuple(i->Cd(i), n)
nc2 = reduce(cross, nc)
end
julia> @code_warntype testcross(2)
Variables
#self#::Core.Compiler.Const(testcross, false)
n::Int64
#3::var"#3#4"
nc::Tuple{Vararg{Cd,N} where N}
nc2::Union{Cd, CrossProd}
Body::Union{Cd, CrossProd}
1 ─ (#3 = %new(Main.:(var"#3#4")))
│ %2 = #3::Core.Compiler.Const(var"#3#4"(), false)
│ (nc = Main.ntuple(%2, n))
│ %4 = Main.reduce(Main.cross, nc)::Union{Cd, CrossProd}
│ (nc2 = %4)
└── return %4
A few questions:
- Why is the type of variable
nc
indeterminate? Couldn’t the compiler infer the length of the tuple from the function argument? - Why does
nc2
have the typeUnion{Cd, CrossProd}
? I had expected it to be of typeCrossProd{Tuple{Cd,Cd}}
, as in the first example. - Can the definition of
cross
be changed so thatreduce
can infer the right type?
Thanks!