# Dispatching on a new composite type that extends an old composite type

I have a situation where I want to check if two 2D vectors are neighbours or not according to some criteria. The initial criteria I considered was to check whether both vectors are within some fixed distance `radius` of one another. I did this as follows:

``````using LinearAlgebra

end

``````

I now want to consider a new criteria that still performs this distance check, but also checks that the vectors do not face in “opposite” directions. More specifically I want to check whether the vector `y` forms an angle between `π - θ` and `π + θ` with the vector `x`, where `theta` is allowed to vary. To do this, I implemented a new composite type to dispatch `neighbours` on.

``````struct AngleMask<:Mask
theta::Float64
end

angle = acos(dot(x, y) / (abs(x)*abs(y)))
angle_check = angle >= π + mask.theta ||  theta <= π - mask.theta
end
``````

The problem I have with this approach is code duplication. More specifically, `radius_check` is a reimplementation of `neighbours` for `RadialMask`. In an OOP language one gets around this by making `AngleMask` a subclass of `RadialMask`. This would also remove the need to declare a `radius` variable in the definition of `AngleMask`. Is there a neat design pattern to avoid this kind of code duplication in Julia? More generally, one can consider the following scenario.

``````struct Foo
# foo variables
end

struct Bar
# foo variables
# bar variables
end

function f(element::Foo) = # do some stuff based on foo variables

function f(element::Bar)
# do some stuff based on foo variables
# do some stuff based on bar variables
end
``````

I don’t know if this fully addresses your problem, but one way to reduce code duplication is to break your functions into smaller functions and compose them.

``````check_radius(mask::Mask, x, y) = norm(x-y) < mask.radius

angle = acos(dot(x, y) / (abs(x)*abs(y)))
return angle >= π + mask.theta ||  theta <= π - mask.theta
end

2 Likes

Decoupling is good, but that’ll hit a `ERROR: MethodError: no method matching check_radius(::AngleMask, ...`

Julia has supertypes, you just can’t instantiate them. You can implement methods for the supertype `Mask`, or if there isn’t a neat supertype, a Holy trait that both `AngleMask` and `RadialMask` have. Taking Christopher_Fisher’s example assuming the original type definitions, all you would need for reuse is this edit: `check_radius(mask::Mask, x, y) = norm(x-y) < mask.radius`. Personally, I would decouple `check_radius` even further from `Mask` because it really only needs to check a floating point value:

``````check_radius(radius, x, y) = norm(x-y) < radius

``````

The problem with inheriting fields along with methods is that you don’t necessarily want the fields or treat them the same way, so it’s easy to end up with memory bloat or silent bugs when some method isn’t overridden properly. Instead of doing that by default, you opt into fields reuse with composition. Here you only reuse a `radius` field, so it’s probably overkill to do that. If you ever choose to represent `radius` differently (can’t imagine how) from a single field in a type, you could make a method `radius` to make the access call the same across all types. This would also be useful with composition, like:

``````radius(m::Mask) = m.radius # general fallback

This was also the first thought I had regarding a solution. The main benefit here is that a single change to `check_radius` ensures that both masks are updated. There seems to be no way to tell Julia that the operations on each of the structs as defined should be related without having them rely jointly on some auxiliary function like this (to the best of my knowledge at least).