What is the difference between abstract, composite, and concrete types.

Can someone help me understand the difference? I understand the concept from object oriented languages, but there are a few things from the documentation I don’t understand.

For example

struct Point{T}

This is a composite type, but to me its also “abstract” since you can create a Point object of any primitive type. Soon after the documentation talks about

abstract type Pointy{T} end
struct Point{T} <: Pointy{T}

I dont understand why this is neccessary. Is it performance reasons? Should I be creating an abstract type for every concrete type I want to use? What if its a mutable struct?


Please read the manual, and ask clarifying questions after:

I think @affans was implying that he had, but was still confused about some things.

In your example above, Point is indeed an (implicitly defined) abstract type, while Point{T} are instances of it (i.e. Point{T} is a “subtype” of Point). Note here that Point and Point{T} should be thought of as distinct types.

You wouldn’t necessarily need to define Pointy. If Point provides all the functionality you need, you are done. Your example Pointy{T} would be like a parent class in an OO language. You would have the option of having types other than Point inherit from it, and dispatch functions on Pointy when desired (in which case those functions would work for all T <: Pointy types, depending on type parameters). One difference between Pointy{T} and Point is that Pointy{T} is explicitly defined, while the existence of Point is implied by the existence of Point{T}.

So to explicitly answer your question, no you don’t necessarily need to create an abstract type for every concrete type you use. It doesn’t matter whether it’s a mutable struct or a struct.


The Pointy{T} example is just showing that it’s possible to have an abstract type with a parameter, and that you can have a concrete parameterized type Point{T} that shares that parameter. There’s no inherent reason to do this (it neither helps nor hurts performance on its own), but it can be extremely useful.

For example, the abstract type that I refer to most often is probably AbstractVector{T} (which is, itself, just an alias for AbstractArray{T, 1}, i.e. a 1-dimensional array). The fact that the abstract type has a T parameter makes it very easy to do something like:

function foo(x::AbstractVector{Int})
  # Do something with x

and have my function foo() work with any kind of vector of Ints (like a regular Vector{Int} or a vector from StaticArrays.jl or something else). If the abstract type didn’t have the T parameter, then I wouldn’t be able to express this behavior as easily.

This fits into a general philosophy in Julia that abstract types are primarily for controlling and organizing method dispatch (that is, deciding which method to call for a particular type).


Abstract types are for dispatching on ideas and interfaces when you don’t care about implementation. AbstractArrays are a great example of this. I want the user to give me some vector v and I want them to give me a matrix A so that I can compute A*v. I don’t need to restrict it to Vector and Matrix since these can conceivably be SparseMatrixCSC and some weird OffsetArray: my algorithm will still work if * is defined correctly.

So, instead of setting dispatch on Vector and Matrix, I dispatch on AbstractVector and AbstractMatrix, basically saying “give me anything that acts like a vector and that acts like a matrix, regardless of implementation”.

What are some nice things that come out of this? GPUArrays are an alternative implementation of abstract arrays, and so the algorithm I describe could do well on the GPU without modification simply by having the user pass in a GPUArray. That kind of generic programming is powerful.


@ChrisRackauckas Dont you lose performance over this?

No, functions auto-specialize.

1 Like

That link is now out of date (dead), see the following: