Question on Idiomatic Julia, Traits, and Types

I’m trying to better learn and understand how to write idiomatic Julia. Please consider the following example. I have a some types defined as follows.

abstract type MyAbstractType end

Base.@kwdef struct MyStruct_1D <: MyAbstractType 
    # various fields that do not have a default value
    dim::Int = 1

Base.@kwdef struct MyStruct_2D <: MyAbstractType 
    # various fields that do not have a default value
    dim::Int = 2

These structs contain initial conditions, boundary conditions, physical parameters, and solutions on grids of either 1 or 2 dimensions, possibly 3 dimensions in the future. I want to define various methods that change behavior depending on the type. My naive approach then would be to do the following

function func1(s::MyStruct_1D)
# do 1D stuff

function func1(s::MyStruct_2D)
# do 2D stuff

Alternatively, I see traits talked about a lot. I don’t have much experience with them but I feel they are important and I should know how to implement them. I’m wondering if this is a good use case for them. I’m not certain it is, since the trait information is in the type name already (i.e., _1D). My naive first pass at implementing the above with traits would be to do something like this.

abstract type Dimension end
struct OneDimensional <: Dimension end
struct TwoDimensional <: Dimension end

Dimension(::Type{MyStruct_1D}) = OneDimensional()
Dimension(::Type{MyStruct_2D}) = TwoDimensional()

func1(s::T) where {T<:MyAbstractType} = func1(Dimension(T), s)

func1(::OneDimensional, s::T) where {T}
# do 1D stuff

func1(::TwoDimensional, s::T) where {T}
# do 2D stuff

I’m not sure which of the approaches is better. One last thing I thought about but I am not sure if its possible. Each struct has the dim field. Is it possible to somehow dispatch on that, using something like Val() to pass that information to the compiler?

I’d appreciate any thoughts on the above.

It’s not clear what the goal is here, perhaps you should give a more complete example, but one thing that I’m pretty sure of is that there’s no point to storing a field for dimension count inside a type that’s only ever intended to support 1D (or 2D, in the case of the other type). That’s redundant.

You could, for example, have a method dimensions:

dimensions(::Type{MyStruct_1D}) = 1
dimensions(::Type{MyStruct_2D}) = 2

That would make you dim field unnecessary.

Probably it would be even better if you could design a single type that would be parameterized by number of dimensions:

struct MyStruct_ND{dim} <: MyAbstractType ... end

Thanks for the response. The goal is stated below and I am just looking for advice on the best way to go about it.

I’m interested in your suggestion - but I’ve never implemented something like that before. I’d think something like

struct MyStruct_ND{dim} <: MyAbstractType
# various fields

I’d want to dispatch based on the type parameter, so maybe something like this? (though it does not work yet).

function func1(s::MyStruct_ND{dim}) where {dim} = 1
# Do 1-D stuff

I think you want this:

function func1(s::MyStruct_ND{1})
# Do 1-D stuff

Thank you! :slight_smile: I think this is the crux of what I was after. I knew there was a way to embed the dimensionality somehow into the type itself in a more elegant way then _1D or _2D.

1 Like

The prototype for encoding dimensionality into the type is AbstractArray{T,N} and Array{T,N}:

julia> const NArray{N} = Array{T,N} where T
Array{T, N} where {N, T}

julia> NArray{1}
Vector (alias for Array{T, 1} where T)

julia> NArray{2}
Matrix (alias for Array{T, 2} where T)

julia> NArray{3}
Array{T, 3} where T

julia> NArray{4}
Array{T, 4} where T

One advantage of the trait approach is that you can add your own dimension type to extend the method. For example

struct OneDFractal <: Dimension end
Dimension(::Type{MyStruct_Fractal}) = OneDFractal()
func1(::OneDFractal, s::T) where {T}
# do stuff

This is well explained in

Kwong, Tom. Hands-on Design Patterns and Best Practices with Julia: Proven Solutions to Common Problems in Software Design for Julia 1.x. Birmingham, UK: Packt Publishing, 2020.