# Using parametric type inside outer parametric constructor

**URL:** <https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388>\
**Category:** General Usage\
**Tags:** linearalgebra, parametric-types, potential-bug\
**Created:** [January 15, 2021, 3:37pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388 "2021-01-15T15:37:33Z")\
**Posts on this page:** 14\
**Page:** 2

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 15, 2021, 10:05pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/21 "2021-01-15T22:05:46Z")

</div>

Uhm… Well, this is even minimal:

```julia
julia> struct Foo{T,S<:T} end

julia> Foo{T}(x) where T = Foo{T,T}()

julia> Foo{Float64}(1)
ERROR: UndefVarError: T not defined

```

> [@Henrique\_Becker](#):
>
> with Julia 1.5.3, your original example works without any changes except removing `<: AbstractArray{T, 4}`

Not here:

```julia-repl
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.5.3 (2020-11-09)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |

julia> struct Foo{T<:Real, S<:AbstractMatrix{<:T}} 
               data::S
               n::Int

               function Foo{T, S}(data, n) where {T<:Real, S<:AbstractMatrix{<:T}}
                       Base.require_one_based_indexing(data)
                       new{T, S}(data, n)
               end
       end

julia> function Foo(A::AbstractMatrix{T}, n::Int) where {T<:Real}
               return Foo{T, typeof(A)}(A, n)
       end
Foo

julia> function Foo{T}(::UndefInitializer, n::Int) where {T<:Real}
               d = fld(n * (n + 1), 2)
               A = Matrix{T}(undef, d, d)
               return Foo(A, n)
       end

julia> Foo{Float64}(undef, 2)
ERROR: UndefVarError: T not defined
Stacktrace:
 [1] Foo{Float64,S} where S<:(AbstractArray{var"#s1",2} where var"#s1"<:Float64)(::UndefInitializer, ::Int64) at ./REPL[3]:3
 [2] top-level scope at REPL[4]:1

julia>

```

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 15, 2021, 10:11pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/22 "2021-01-15T22:11:13Z")

</div>

Yeah, I also ran the original code on 1.4.0, but with the AbstractArray inheritance removed, and I still got the `UndefVarError`.

> [@lmiq](#):
>
> Uhm… Well, this is even minimal:
> 
> ```julia
> julia> struct Foo{T,S<:T} end
> 
> julia> Foo{T}(x) where T = Foo{T,T}()
> 
> julia> Foo{Float64}(1)
> ERROR: UndefVarError: T not defined
> 
> ```

Wow, nice example! That really boils it down.

I have no idea what’s going on anymore. 😂

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 15, 2021, 10:26pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/23 "2021-01-15T22:26:33Z")

</div>

Slightly more minimal:

```julia
julia> struct Foo{T, S <: T} end

julia> Foo{T}() where {T} = T

julia> Foo{Integer}()
ERROR: UndefVarError: T not defined
Stacktrace:
 [1] Foo{Integer,S} where S<:Integer() at ./REPL[2]:1
 [2] top-level scope at REPL[3]:1

```

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 15, 2021, 10:29pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/24 "2021-01-15T22:29:27Z")

</div>

You see, the function gets specialized to `Foo{Integer,S}`, but then `T` in the function is not defined. The problem is the `T` inside, which looses its meaning when the method is created:

```julia
julia> struct Foo{T,S<:T} end

julia> Foo{T}() where T = 1

julia> Foo{Integer}()
1

julia> Foo{T}() where T = x

julia> Foo{Integer}()
ERROR: UndefVarError: x not defined

```

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 15, 2021, 10:37pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/25 "2021-01-15T22:37:07Z")

</div>

I’m inclined to think this is a bug. I don’t understand why this works:

```julia
julia> struct Bar{T, S} end

julia> Bar{T}() where {T} = T

julia> Bar{Integer}()
Integer

```

but this doesn’t:

```julia
julia> struct Foo{T, S <: T} end

julia> Foo{T}() where {T} = T

julia> Foo{Integer}()
ERROR: UndefVarError: T not defined
Stacktrace:
 [1] Foo{Integer,S} where S<:Integer() at ./REPL[2]:1
 [2] top-level scope at REPL[3]:1

```

---

<div class="post-metadata">

**Author:** ![schneiderfelipe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schneiderfelipe/32/18004_2.png) [@schneiderfelipe](https://discourse.julialang.org/u/schneiderfelipe)\
**Post date:** [January 15, 2021, 11:24pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/26 "2021-01-15T23:24:21Z")

</div>

I think @Henrique_Becker gave a nice explanation: in your second example, `Foo{Integer, Int}` would be allowed, but then there’s no way to know what both `T` and `S` means within `Foo{T}() where {T} = ...`. In order words, `T` and `S` are “connected” in your first example, but “disconnected” in the second. Or am I missing something?

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 15, 2021, 11:24pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/27 "2021-01-15T23:24:33Z")

</div>

I think it is a type-inference problem:

```julia
julia> @code_typed Foo{Int}()
CodeInfo(
1 ─ return $(Expr(:static_parameter, 1))
) => Type{T} where T<:(Union{Int64, S} where S<:Int64)

```

Of course `T` is `Int64` if `T<:Union{Int64,S}` and `S<:Int64`. (I don’t quite get why there is the `T<:Union{Int64,S}`, really… is that right?).

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [January 15, 2021, 11:55pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/28 "2021-01-15T23:55:39Z")

</div>

My mistake, I copied the code and forgot to change the commented lines.

It seems clear to me that what [I pointed](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/15) in [your original MWE](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/6) cannot be _exactly_ same that affected [OP’s MWE](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/15). In OP’s MWE the problem happens because somehow in the first function called `T` is not recognized within the body of the first function called.

@schneiderfelipe Than for your kind words, but while I think I got it right for @lmiq MWE, I do not think I solved your original problem.

Now… this is funny:

```julia
julia> struct Foo{T<:Real, S<:AbstractMatrix{<:T}}
               data::S
               n::Int
       
               function Foo{T, S}(data, n) where {T<:Real, S<:AbstractMatrix{<:T}}
                   println("Foo{T, S}(data, n)")
                   Base.require_one_based_indexing(data)
                   new{T, S}(data, n)
               end
       end
       
       function Foo(A::AbstractMatrix{T}, n::Int) where {T<:Real}
           println("Foo(A, n)")
           return Foo{T, typeof(A)}(A, n)
       end
       
       # Changed the signature below to take both T and S even if S is not used
       function Foo{T, S}(::UndefInitializer, n::Int) where {T<:Real, S <: AbstractMatrix{<:T}}
           println("Foo{T, S}(undef, n)")
           d = fld(n * (n + 1), 2)
       
           # The next two lines should substitute each other,
           # but the commented one throws an error.
       
           A = Matrix{T}(undef, d, d) # UndefVarError: T not defined
           # A = zeros(d, d)
       
           return Foo(A, n)
       end

julia> Foo{Float64, Matrix{Float64}}(undef, 2)
Foo{T, S}(undef, n)
Foo(A, n)
Foo{T, S}(data, n)
Foo{Float64,Array{Float64,2}}([0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0], 2)

```

Why? Why the hell?

My guess now is that, maybe, just maybe, we are not allowed to define an outer constructor taking less type parameters than the `struct` itself?

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 16, 2021, 12:02am UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/29 "2021-01-16T00:02:38Z")

</div>

It seems to me that the behavior of the MWE is determined by some code lowering and/or other implementation details that are not specified in the manual. I still feel that the current behavior of the MWE is incorrect, but of course I could be wrong.

We might have to file a Github issue and have the experts weigh in.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 16, 2021, 12:53am UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/30 "2021-01-16T00:53:32Z")

</div>

> [@CameronBieganek](#):
>
> experts weigh in.

or just mark one of them here?

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 16, 2021, 1:11am UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/31 "2021-01-16T01:11:33Z")

</div>

I’m not sure I want to tag a bunch of people here… 😂

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 16, 2021, 2:05am UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/32 "2021-01-16T02:05:31Z")

</div>

[https://github.com/JuliaLang/julia/issues/39280](https://github.com/JuliaLang/julia/issues/39280)

---

<div class="post-metadata">

**Author:** ![lhnguyen-vn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lhnguyen-vn/32/15449_2.png) [@lhnguyen-vn](https://discourse.julialang.org/u/lhnguyen-vn)\
**Post date:** [January 16, 2021, 2:15am UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/33 "2021-01-16T02:15:32Z")

</div>

This feels like a plausible explanation. I vaguely recall that static parameters are only ever assigned by the compiler if they are uniquely identified. The compiler needs to find, if possible, a unique `T` in order for `T` to be available in the method body. Somehow the constraint on `S` introduces a weird edge case and the compiler just gives up. Ironically, it is smart enough to know that

```julia
julia> (Union{Int64,S} where {S<:Int64}) == Int64
true

```

but not smart enough to conclude the static type must be `Int64`. I think Jeff mentioned this issue in [What’s bad about Julia?](https://www.youtube.com/watch?v=TPuJsgyu87U&t=1085s) where types can have extents that just drive the compiler crazy.

---

<div class="post-metadata">

**Author:** ![schneiderfelipe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schneiderfelipe/32/18004_2.png) [@schneiderfelipe](https://discourse.julialang.org/u/schneiderfelipe)\
**Post date:** [January 16, 2021, 5:10am UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/34 "2021-01-16T05:10:34Z")

</div>

Thanks for opening the issue @CameronBieganek!

[Previous page](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388.md?page=1)
