-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
remove dependence on Julia internal Core.Compiler.return_type
#127
Conversation
OK, thanks. Just trying to get my head around everything. Is using |
Currently, this method depends on `code_typed`. | ||
""" | ||
function return_type(f, types) | ||
return last(only(code_typed(f, types; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is worse than just calling Core.Compiler.return_type
. It still trespasses on the internals of the Julia implementation, as it's not documented that last
may be used here, and I guess it's slower than Core.Compiler.return_type
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nsajko It's calling last
on a Pair
-- how is that undocumented behavior? It's also only calling public API (an function that appears in the manual). I guess I could also access the rettype
property of the CodeInfo
that's the first element of that pair, but I still need to access the pair.
It will almost definitely be slower than calling Core.Compiler.return_type
, because I'm guessing that Core.Compiler.return_type
is called somewhere in the implementation of code_typed
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, sorry.
Probably not, but That said, using internals is also risky because their behavior can change or be dropped at any point.
No(ish) and yes. There are two changes here:
The latter change in particular fixes a major downstream Julia 1.10 compatibility issue: MakieOrg/AlgebraOfGraphics.jl#472
I agree, but the IMHO legitimate problem in the new handling of |
Shouldn't this rather be fixed in AoG? Assuming it doesn't get fixed in Julia. |
AoG is calling things, but the actual error occurs in Dictionaries.jl code. |
Creating julia> Indices(Union{}[])
0-element Indices{Union{}} This PR adds an explicit check to one special case, where The change of |
This is patently incorrect -- I do agree that the In terms of the workaround here for |
Sorry, maybe I formulated it too strongly :) It very slightly reduces the internals dependence, because the actual return value of This change from # Core return_type()
julia> @btime Core.Compiler.return_type(sin, Tuple{Float64})
0.791 ns (0 allocations: 0 bytes)
Float64
# return_type() from this PR
julia> @btime return_type(sin, Tuple{Float64})
48.333 μs (1232 allocations: 69.22 KiB)
Float64 Wouldn't be great to pay this overhead on every Dictionary/Indices construction, especially given that it doesn't remove the internals dependence.
The |
# is used. For an empty iterator, we actually don't know what the | ||
# True(tm) eltype is, so the top of the type hierarchy (Any) is | ||
# just as reasonable as the bottom (Union{}) | ||
I = typeof(iter) == Tuple{} ? Any : eltype(iter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're constructing an Index from an empty tuple, you do not know what the original element type is, so Any IMHO makes as much sense as Union{}.
Small point: Any
is technically correct (in a trivial sense), but less precise than Union{}
. The empty union is the best possible (most accurate) type for the empty set of values. The possible breakage on the Julia side, however, is another thing...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small note: typeof(iter) == Tuple{}
isn't very pretty/Julian. Here are two alternatives that are prettier in my opinion (although all of them seem equally performant):
f(it) = (typeof(it) == Tuple{}) ? Any : eltype(it)
g(it) = (it == ()) ? Any : eltype(it)
h(it) = (it isa Tuple{}) ? Any : eltype(it)
I vote for it == ()
(in case this change turns out to be necessary), seems like the nicest option.
EDIT: I think there's actually a style guide or something in Julia's manual that advises not to compare types with ==
. In that light another option is typeof(it) <: Tuple{}
, although the typeof
is still unnecessary with that form.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it === ()
or it isa Tuple{}
are the correct forms. (Note: the latter form might invoke a slower subtyping algorithm when the LHS isn't inferred to a concrete type and/or the RHS is an abstract type; the former is always quite straightforward).
Okay, this has really opened a can of worms :) There are few things going on here.
And yes, this package relies on Julia internals. It always has - originally this was "a feature not a bug" because it gave great performance + ergonomics. Traditionally we update the package to work well with each new version of Julia (but this hasn't been necessary in recent years). The plan was always to use something "recommended" once Julia became more mature. I actually have an minimal example for the logic we follow: x = map(sin, Int64[]) What is the type (concrete, runtime type) of In general, I feel like swapping We do however need to get this package working well with Julia 1.10, and any improvements to handling tables with zero rows or with zero columns are extremely valuable and welcome. Is there something more minimal we could try that will at least get us unstuck in the short term? |
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #127 +/- ##
==========================================
- Coverage 79.35% 79.15% -0.20%
==========================================
Files 21 21
Lines 2349 2351 +2
==========================================
- Hits 1864 1861 -3
- Misses 485 490 +5 ☔ View full report in Codecov by Sentry. |
And julia> map(length, Symbol[])
Union{}[] Also when making an array out of an empty tuple - the exact same scenario as discussed here with julia> collect(())
Union{}[] So the consistent way, following Base, is to continue returning Would be nice if those affected by this 1.10 issue chimed in the JuliaLang/julia#52385 discussion! I think popularity does affect the priority different bugs get (and that's totally sensible). |
Note that the issue is labeled with |
It looks like the |
Thanks for following up on this @palday |
see also JuliaLang/julia#52385
There's also another problem that arises on Julia 1.10 when
Indices
are constructed from an empty tuple, the resultantIndices{Union{}}
create all sorts of problems. To get around this, I've added a check for empty tuples and changes the indices eltype to beAny
. See also MakieOrg/AlgebraOfGraphics.jl#472