# Parametric abstract types

**URL:** <https://discourse.julialang.org/t/parametric-abstract-types/25651>\
**Category:** New to Julia\
**Tags:** parametric-types\
**Created:** [June 25, 2019, 1:25pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651 "2019-06-25T13:25:29Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [June 25, 2019, 1:25pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/1 "2019-06-25T13:25:29Z")

</div>

I have an abstract type `MyAbstractType` and there’re several parametric concrete types “under” it. e.g.,

```julia
MyConcreteType1 <: MyAbstractType
MyConcreteType2 <: MyAbstractType
MyConcreteType3 <: MyAbstractType
...

```

Now, wanna define a function that accepts `x` and `y`: they’re of the same concrete type, but with (possibly) different type parameters. e.g. `fun(x, y)` where `x` is a `MyConcreteType2{Float16}` and `y` is a `MyConcreteType2{Float64}`.

any idea how to solve the following error? thanks.

```julia
function fun(x::S{U}, y::S{V}) where {S <: MyAbstractType,
                                        U <: AbstractFloat,
                                        V <: AbstractFloat}
    println(S)
    println(U)
    println(V)
end

ERROR: TypeError: in Type{...} expression, expected UnionAll, got TypeVar

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [June 25, 2019, 1:47pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/2 "2019-06-25T13:47:21Z")

</div>

You should make the type parameter part of the abstract type. e.g.

```julia
abstract type MyAbstractType{T} end
struct MyConcreteType1{T} <: MyAbstractType{T}
    ...
end

```

then you can do

```julia
function fun(x::MyAbstractType{U}, y::MyAbstractType{V}) where {U<:AbstractFloat, V<:AbstractFloat}
end

```

and it will match instances of your concrete type.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [June 25, 2019, 2:18pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/3 "2019-06-25T14:18:06Z")

</div>

unfortunately it does not work:

```julia
abstract type MyAbstractType{T <: AbstractFloat} end
struct MyConcreteType1{T} <: MyAbstractType{T} end
struct MyConcreteType2{T} <: MyAbstractType{T} end

function fun(x::MyAbstractType{U}, y::MyAbstractType{V}) where {U<:AbstractFloat, V<:AbstractFloat}
    println(typeof(x) )
    println(typeof(y) )
end

julia> x = MyConcreteType1{Float16}()
MyConcreteType1{Float16}()
julia> y = MyConcreteType2{Float32}()
MyConcreteType2{Float32}()
julia> z = MyConcreteType2{Float64}()
MyConcreteType2{Float64}()

julia> fun(x, y)
MyConcreteType1{Float16}
MyConcreteType2{Float32}

julia> fun(y, z)
MyConcreteType2{Float32}
MyConcreteType2{Float64}

julia> fun(x, z)
MyConcreteType1{Float16}
MyConcreteType2{Float64}

```

what I want is `fun(x, y)` and `fun(x, z)` being **invalid** (different concrete types) , while `fun(y, z)` is valid (same concrete type).

I need to do so because there’re a lot of concrete types under the abstract type, and I don’t want to define the function for each concrete types …

how to do?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [June 25, 2019, 2:49pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/4 "2019-06-25T14:49:05Z")

</div>

> [@tomtom](#):
>
> what I want is `fun(x, y)` and `fun(x, z)` being **invalid** (different concrete types) , while `fun(y, z)` is valid (same concrete type).

I don’t know whether this is possible?

> [@tomtom](#):
>
> I need to do so because there’re a lot of concrete types under the abstract type, and I don’t want to define the function for each concrete types …

If you can write generic code (i.e. a single `fun` definition) for `x::T{S}` and `x::T{U}` for any `T<:MyAbstractType`, but not for two different subtypes, then maybe you need to re-think why they have a common abstract type in the first place? What is the point of the abstract type if subtypes can’t share code? Can you give a more specific example?

See also the [Conversion and Promotion](https://docs.julialang.org/en/latest/manual/conversion-and-promotion/) section of the manual for one approach to handling large number of combinations of types.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [June 25, 2019, 3:10pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/5 "2019-06-25T15:10:03Z")

</div>

> [@stevengj](#):
>
> If you can write generic code (i.e. a single `fun` definition) for `x::T{S}` and `x::T{U}` for any `T<:MyAbstractType` , but not for two different subtypes, then maybe you need to re-think why they have a common abstract type in the first place? What is the point of the abstract type if subtypes can’t share code? Can you give a more specific example?

a bit complicated… I’m defining something like a “tensor” (the abstract type), which could take different “shapes”. Each shape is then represented by a parametric (floating point representation) concrete type.

Now, for operations like “addition”, we need _same_ shapes. What’s why I want the function described above (actually that function would do the promotion inside).

However, for operations like “multiplication”, _some_ shape could do with _another_ shape (that I’ve defined explicitly).

After all, there’re a lot of operations that are **common** to all shapes, e.g. function to get and set the dimensions. Maintaining an abstract type `Tensor` would be necessary to avoid defining very-similar functions across different shapes.

> [@stevengj](#):
>
> I don’t know whether this is possible?

sigh… if it’s the case, I would just go to define the functions for each concrete type then.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 25, 2019, 4:01pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/6 "2019-06-25T16:01:36Z")

</div>

Yes, what you initially wanted to do isn’t possible directly. To make this concrete, let’s consider `AbstractArray{T}`, with the concrete types `Array{T,1}` and `UnitRange{T}`. The syntax you wanted is this:

```julia
function fun(x::S{U}, y::S{V}) where {S <: AbstractArray, U, V}

```

But that doesn’t work because Julia needs to know exactly what the `S` is before it knows what `S{U}` or `S{V}` means. Now, yes, the `T` in `Array{T}` and `UnitRange{T}` _happen_ to mean the same thing as the first parameter in their supertype, but that needn’t be the case. What would this function do when presented with `BitArray`s, for example? The first parameter of a `BitArray` is _not_ its element type but rather its dimensionality! Would that then mean that it’d only accept `BitArray`s of the same dimensionality?

In other words, what you want to do doesn’t have a well-defined meaning. Instead of relying upon method errors, just check the cases in code and throw `ArgumentErrors` instead.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [June 25, 2019, 4:43pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/7 "2019-06-25T16:43:13Z")

</div>

> [@tomtom](#):
>
> I’m defining something like a “tensor” (the abstract type), which could take different “shapes”. Each shape is then represented by a parametric (floating point representation) concrete type.

You could have the shape as part of the type parameter, instead of as distinct subtypes. e.g. you could parameterize by a tuple, much like how the `SArray` type in [StaticArrays](https://github.com/JuliaArrays/StaticArrays.jl) works.

> [@tomtom](#):
>
> Now, for operations like “addition”, we need _same_ shapes. What’s why I want the function described above (actually that function would do the promotion inside).

Do you really need the shape check to be done at compile time? When you add two matrices, the shape check is done at runtime. Compile-time checks of sizes are mainly important for tiny arrays, i.e. something like the StaticArrays package.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 25, 2019, 4:47pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/8 "2019-06-25T16:47:48Z")

</div>

> [@stevengj](#):
>
> Do you really need the shape check to be done at compile time? When you add two matrices, the shape check is done at runtime. Compile-time checks of sizes are mainly important for tiny arrays, i.e. something like the StaticArrays package.

Also note that encoding something into a single method signature is _not_ the only way to do computation at compile time. If the sizes are encoded in the type domain, then the checks you write against them — be they in the top-level method signature or not — can be computed at compile time.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [June 25, 2019, 5:01pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/9 "2019-06-25T17:01:50Z")

</div>

> [@mbauman](#):
>
> But that doesn’t work because Julia needs to know exactly what the `S` is before it knows what `S{U}` or `S{V}` means

I mean, it may be difficult to implement, but I think it would be nice to be included in future versions of Julia. After all, a “human” is able to “parse” the following **without ambiguity** :

> [@tomtom](#):
>
> fun(x::S{U}, y::S{V}) where {S \<: MyAbstractType, U \<: AbstractFloat, V \<: AbstractFloat}

also, the following, in my understanding, represents that we **need to re-think** the syntax and meaning of parametric type. I think a good language should be as **intuitive** as possible.

> [@mbauman](#):
>
> but that needn’t be the case. What would this function do when presented with `BitArray` s, for example? The first parameter of a `BitArray` is _not_ its element type but rather its dimensionality! Would that then mean that it’d only accept `BitArray` s of the same dimensionality?

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [June 25, 2019, 5:13pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/10 "2019-06-25T17:13:14Z")

</div>

> [@mbauman](#):
>
> Instead of relying upon method errors, just check the cases in code and throw `ArgumentErrors` instead.

how? e.g.

```julia
function fun(x::MyAbstractType{S}, y::MyAbstractType{T} ) where {S <: AbstractFloat, T <: AbstractFloat}
    # ??? how to check if x and y are of the same concrete type (but parameters S and T can be different) ???
end

```

in other words, how to get the “concrete type without parameter”?  
using `typeof()`, e.g. `typeof(randn(3) )` gives `Array{Float64, 1}`. But is there any function that would return `Array` simply???

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 25, 2019, 5:26pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/11 "2019-06-25T17:26:24Z")

</div>

Guess I’m not “human”. Bummer.

The point is simply that the concrete types are the ones that decide what its type parameters mean and how to report to the supertype what the supertype’s parameters are. Thus no-one is able to say what `S{V}` means unless they know what `S` is. That’s just the way things work. Reconsidering this design would introduce quite the limitation.

When I was talking about doing the check at compile time, I was thinking more in terms of the computation on the “shape” parameters — it’s indeed much easier to yank out the parameters than it is to ask for the type. One simple way, though, is to simply ask if the names are the same: `nameof(typeof(x)) === nameof(typeof(y)))` does its work at compile time but of course will fail if you have two subtypes with the same name but in different modules.

---

<div class="post-metadata">

**Author:** ![hanand](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hanand/32/34094_2.png) [@hanand](https://discourse.julialang.org/u/hanand)\
**Post date:** [February 23, 2022, 3:32pm UTC](https://discourse.julialang.org/t/parametric-abstract-types/25651/12 "2022-02-23T15:32:52Z")

</div>

How can I specify the T in MyAbstractType to be of Number or Float etc?
