Skip to content
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

Type instabilities when applying a parametric type constructor to a collection using map #50903

Closed
ffevotte opened this issue Aug 13, 2023 · 2 comments

Comments

@ffevotte
Copy link
Contributor

Hi,

I'm filing this issue following a discourse thread, in case it actually is a bug. Please feel free to close it if this behavior is actually intended.

Consider the following (minimal?) example, demonstrating a type-instable application of map to the constructor of a parametric type:

julia> struct A{N}
           A(::NTuple{N,Int}) where {N} = new{N}()
       end

julia> t = (1,2)
(1, 2)

julia> T = typeof(t)
Tuple{Int64, Int64}

julia> using Test

julia> @inferred A(t) # the constructor is type-stable and correctly inferred
A{2}()

julia> @inferred map(A, [t]) # ... but map is not type-stable
ERROR: return type Vector{A{2}} does not match inferred return type Union{Vector{Any}, Vector{A{2}}}

julia> map(A, T[]) # indeed, on an empty vector it returns Any[]
Any[]

This behavior can be observed with Julia v1.9 as well as v1.10(beta).


As uncovered by @Seelengrab in the aforementioned discourse thread, an easy workaround consists in applying map to a standard function instead of a constructor, e.g.:

julia> @inferred map(x->A(x), T[])
A{2}[]

I'm paraphrasing / summarizing below what they uncovered while trying to understand this; the discourse thread contains much more details.

It seems that this issue arises in Base.@default_eltype: because the applied function (A) is a type, the element type of the resulting array is determined to be A itself, even though it is a union-all type and it would be possible to infer the more precise element type (A{2} in this case). A possible fix would therefore consist in handling union-all types like regular functions, rather than special-casing them like the "regular" types. But doing this might have unintended consequences, so I'm not sure what the best way forward would be.

@Seelengrab
Copy link
Contributor

Seelengrab commented Aug 13, 2023

The additional thing with this example in particular is that you can't construct an A through A{2}; it needs to go through A, so there's no way to map A directly and have it be type stable, in the current implementation.

@jishnub
Copy link
Contributor

jishnub commented Aug 14, 2023

Duplicate of #46331 I think, although in that case the parameter was a type rather than a value

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants