# 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:** 20\
**Page:** 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, 3:37pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/1 "2021-01-15T15:37:33Z")

</div>

Hi,

I can’t figure out how to use a parametric type inside a constructor. Namely, the following single commented line can’t be used:

```julia
struct Foo{T<:Real, S<:AbstractMatrix{<:T}} <: AbstractArray{T, 4}
	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

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

function Foo{T}(::UndefInitializer, n::Int) where {T<:Real}
	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

```

The function `Foo{T}(::UndefInitializer, n::Int) where {T<:Real}` works as long as `T` is not mentioned anywhere in its body. Otherwise, a `UndefVarError: T not defined` is thrown:

```julia
# ; is needed because I'm omitting the definitions of size and
# getindex, which are complicated and required by the REPL to
# show AbstractArrays

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

```

Version information:

```julia
julia> versioninfo()
Julia Version 1.4.1
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-8.0.1 (ORCJIT, broadwell)
Environment:
  JULIA_NUM_THREADS = 4

```

What am I doing wrong?

---

<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, 4:28pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/2 "2021-01-15T16:28:21Z")

</div>

Perhaps the easiest is to do:

```julia
julia> struct Foo{T}
         x :: Vector{T}
       end

julia> Foo(T::DataType,n) = Foo(Vector{T}(undef,n))
Foo

julia> Foo(Float64,3)
Foo{Float64}([0.0, 0.0, 0.0])

```

Understanding the error I think is more complicated (at least for me). But part of the problem is that functions are not parametric. That is, you cannot write something like:

```julia
julia> f{T}(x) = T(x)
ERROR: UndefVarError: f not defined

```

if `f` is not previously a data type.

Thus, when you are defining `Foo{T}(x)` as a constructor, you are actually writting a function like object, which instantiates `Foo` with type `T`, but `T` is not a parameter of that function, such that it is not defined inside it. Something like that.

---

<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, 4:41pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/3 "2021-01-15T16:41:20Z")

</div>

Thanks that is an excellent answer.

What still bothers me is that there are examples in the Julia codebase that work as I wished my code would! See, for example (from `@edit Array{Float64}(undef, 2)`, see file [here](https://github.com/JuliaLang/julia/blob/b9f115f4f15965044239abe8854dba19763fd8ac/base/boot.jl#L465)),

```julia
...
# type but not dimensionality specified
Array{T}(::UndefInitializer, m::Int) where {T} = Array{T,1}(undef, m)
...

```

How come this works there but not here? How is that different from my example?

Two similarity points that I would like to emphasize:

- Both are definitions of a parametric constructor that require a **single** parameter, but both types have **two**.
- Both definitions are like `TypeName{T}(...no T here...) where {T} = ...`

---

<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, 4:45pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/4 "2021-01-15T16:45:00Z")

</div>

Yes, that is very strange. As you said, this sort of thing normally works just fine. Here’s a simpler example that works as expected:

```julia
struct A{T}
    x::T
end

function A{T}() where T
    A(zero(T))
end

```

```julia
julia> A{Float64}()
A{Float64}(0.0)

```

I don’t want to jump to conclusions, but this almost seems like a bug in 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, 4:56pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/5 "2021-01-15T16:56:08Z")

</div>

My best guess is that there is some kind of ambiguity in the various constructors that you’ve defined, but I can’t put my finger on what that would be.

---

<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, 5:08pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/6 "2021-01-15T17:08:44Z")

</div>

Removing the `<:` from the type definition makes it work:

This works:

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

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

julia> Foo{T}(::UndefInitializer,n) where {T<:Real} = Foo(Matrix{T}(undef,n,n),n)

julia> Foo{Float64}(undef,2)
Foo{Float64,Array{Float64,2}}([0.0 0.0; 0.0 0.0], 2)

julia>

```

This does not:

```julia
julia> struct Foo{T<:Real, S<:AbstractMatrix{<:T}} # the difference is the <: here
               data::S
               n::Int
       end

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

julia> Foo{T}(::UndefInitializer,n) where {T<:Real} = Foo(Matrix{T}(undef,n,n),n)

julia> Foo{Float64}(undef,2)
ERROR: UndefVarError: T not defined

```

Minimal example:

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

julia> Foo{T}(::UndefInitializer) where T = Foo(Vector{T}(undef,2))

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

```

---

<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, 5:17pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/7 "2021-01-15T17:17:59Z")

</div>

Awesome finding! That is subtle but definitely solves my issue.

But now, two questions:

1. Do you know why this is so? What constitutes the error?
2. Is there a fundamental reason why this `AbstractMatrix{<:T}` idiom is used in the Julia codebase, e.g., in LinearAlgebra? In fact, I wrote the first part of my example based on the definition of `Hermitian` (see [here](https://github.com/JuliaLang/julia/blob/f813257d24b4e6ca6ee94559aae826a6a7aca008/stdlib/LinearAlgebra/src/symmetric.jl#L85)), and the last part of it looking into outer constructors of `Array{T, 2}` (already mentioned, link [here](https://github.com/JuliaLang/julia/blob/master/base/boot.jl#L465)).

---

<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, 5:31pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/8 "2021-01-15T17:31:44Z")

</div>

No, I don’t. But those things mean different things.

In one you can do:

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

julia> Foo{Real,Vector{Float64}}(zeros(2))
Foo{Real,Array{Float64,1}}([0.0, 0.0])

```

In the other you can’t:

```julia
julia> struct Foo2{T,S<:Vector{T}}
         x :: S
       end

julia> Foo2{Real,Vector{Float64}}(zeros(2))
ERROR: TypeError: in Foo2, in S, expected S<:Array{T,1}, got Type{Array{Float64,1}}

```

Interestingly, in the first case, with `<:`, you cannot build the struct with mixed-type arrays, and we get the same error as you:

```julia
julia> Foo(Union{Float64,Int64}[1,1.0])
ERROR: UndefVarError: T not defined

```

(Maybe this gives a hint on what is going on)

In the second case, without the `<:`, you can:

```julia
julia> Foo2(Union{Float64,Int64}[1,1.0])
Foo2{Union{Float64, Int64},Array{Union{Float64, Int64},1}}(Union{Float64, Int64}[1, 1.0])

```

---

<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, 5:41pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/9 "2021-01-15T17:41:15Z")

</div>

Nice! I think I have a better understanding of it now.

But that’s still odd that everything “works” when I omit the `A = Matrix{T}(undef, d, d)` in the last function of my example (that is, Julia doesn’t complain if `T` is not mentioned, even though the function signature is the same). Of course, I would rather _not_ `data` to zeros though…

---

<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, 5:42pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/10 "2021-01-15T17:42:11Z")

</div>

I’m still a bit confused. The suggestion from @lmiq made me wonder about a related question that seems odd to me. I’ve opened a separate thread for that specific question:

> [@If parametric types are invariant, why is Vector{Int} a subtype of AbstractVector{Int}?](https://discourse.julialang.org/t/if-parametric-types-are-invariant-why-is-vector-int-a-subtype-of-abstractvector-int/53394):
>
> If parametric types are invariant, why is Vector{Int} a subtype of AbstractVector{Int}? julia\> Vector{Int} \<: AbstractVector{Int} true A somewhat more complete and self-contained example: julia\> abstract type Animal{T} end julia\> struct Cat{T} \<: Animal{T} end julia\> Int \<: Real true julia\> Cat{Int} \<: Animal{Real} false julia\> Int \<: Int true julia\> Cat{Int} \<: Animal{Int} true Since parametric types are supposed to be invariant, it seems like there should be no subtype relationship be…

---

<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, 5:47pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/11 "2021-01-15T17:47:33Z")

</div>

Thanks, @CameronBieganek! This seems to be an issue as well, but [here](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/9) I’ve mentioned that there should be more than that in the present case.

In fact, by changing a single line in the function _body_ (same _signature_, same parameters, etc.), everything “works” as expected.

---

<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, 5:49pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/12 "2021-01-15T17:49:52Z")

</div>

Yes, it has something to do with the undef initialization.

---

<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, 6:07pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/13 "2021-01-15T18:07:10Z")

</div>

> [@schneiderfelipe](#):
>
> But that’s still odd that everything “works” when I omit the `A = Matrix{T}(undef, d, d)` in the last function of my example (that is, Julia doesn’t complain if `T` is not mentioned, even though the function signature is the same).

Well, that part I can explain. It’s perfectly possible to define functions that refer to variables that don’t exist. Julia doesn’t throw an error until you run the function and it encounters a variable that has not been defined yet:

```julia
julia> function foo()
           local x
           return x
       end
foo (generic function with 1 method)

julia> foo()
ERROR: UndefVarError: x not defined

```

EDIT: As I commented below, I should probably replace “run the function” with “call the function” in order to clarify the difference between _defining_ a function and _calling_ a function.

---

<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, 8:16pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/14 "2021-01-15T20:16:17Z")

</div>

The point is that, in my example, _running_ the function does not throw an error. In the context of my example, the following throws an error,

```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

Foo{Float64}(undef, 2);
# => UndefVarError: T not defined

```

but the following does not:

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

Foo{Float64}(undef, 2);
# works fine

```

---

<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, 9:06pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/15 "2021-01-15T21:06:55Z")

</div>

The relevant section of the manual is [this one](https://docs.julialang.org/en/v1/manual/constructors/#Parametric-Constructors). As @lmiq pointed out, parametric constructors are kinda special, they are the only kinda of function (or function-like?) that can be passed explicit types (without them being a positional argument).

As @lmiq also pointed out, you can put type restrictions directly in the `struct` definition (this is covered by the link to the manual I provided, and the manual explains how this is equivalent to automatically defining a constructor with a `where` clause).

Carefully reading this section I noticed one thing. No example in the whole section involves defining an outer constructor (i.e., a constructor outside the `struct` scope) that had the `{T, ...}` clause between the function name and the opening parenthesis. The only examples of constructor definitions with this syntax are of inner constructors.

However, as was pointed out in this thread, there is code that defines outer constructors that receive explicit type parameters and work anyway. I agree with @CameronBieganek we can only guess is that there is some kind of ambiguity problem that arises in some situations but not in others.

I would bet on the following: on the minimal example provided by @lmiq the constructor has no way to guess what is the type `T`. `Foo{T}(::UndefInitializer)` calls `Foo(Vector{T}(undef,2))`, what `Foo` (with no type parameters) will guess to be `T`? It received a `Vector{T}`, the `struct` has only a field `x :: S` but it is parametrized as `Foo{T,S <: Vector{<:T}}`, the `Vector{T}` allow us to assert that `S == Vector{T}`, but we cannot guess what `T` (the `struct` parameter) is, because if it is a `Vector{Int}` that is being passed (i.e., `S == Vector{Int}`), we have that `Int <: T` and, therefore, `T` can be many things, like `Integer` or `Number`. The problem does not happen if `T` is fixed (instead of being any supertype) because then it is clear that `T` can only be `Int`.

---

<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, 9:07pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/16 "2021-01-15T21:07:41Z")

</div>

I think our terminology is different. When I said _running_ a function, I meant _calling_ a function. Where you said _running_ a function, I said _defining_ a function. 🙂

This behavior is necessary so that you can define your functions in any order, and so that you can define mutually recursive functions. For example, you can define functions “out of order” like this:

```julia
julia> foo() = bar()
foo (generic function with 1 method)

julia> foo()
ERROR: UndefVarError: bar not defined

julia> bar() = 1
bar (generic function with 1 method)

julia> foo()
1

```

The order doesn’t matter because Julia doesn’t check whether internal variables exist yet when you first define a function.

---

<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, 9:19pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/17 "2021-01-15T21:19:27Z")

</div>

Yeah, I mixed up some words there! Thanks for pointing that out 😆 😅.

---

<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, 9:20pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/18 "2021-01-15T21:20:16Z")

</div>

No worries. I should probably stick to “call” rather than “run”, since it’s more precise.

---

<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, 9:26pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/19 "2021-01-15T21:26:38Z")

</div>

Excellent explanation! Now I get it!

I basically disconnected `T` and `S` when I defined `Foo`, and now my constructor requires `T` to be retrieved from `S`. That was hard to get my head around that 😅

---

<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, 9:56pm UTC](https://discourse.julialang.org/t/using-parametric-type-inside-outer-parametric-constructor/53388/20 "2021-01-15T21:56:24Z")

</div>

Good recapitulation, I was worried that it was not intelligible because `T` is used both in the `struct` parameter and in the constructors with different meaning (on that MWE).

However, this is only relevant for the MWE, because for me, with Julia 1.5.3, your original example works without any changes except removing `<: AbstractArray{T, 4}` from the `struct` (because your inheritance makes a specialized `Base.show` trigger and it expects that you had defined a `Base.size` for your type to be able to print it). So I think your original code does not make the same mistake intentionally created by @lmiq to trigger the same error message. Julia 1.4.1 probably had a bug that triggered the same error on situations where it did not apply.

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