Implementing interface on custom types

Is it possible to implement an interface on a simple custom type so that many common functions will work on it? I’m trying to improve my knowledge of Julia. Let’s say we have the following simple custom type.

mutable struct interfaceTest
	value::Int
	x::Bool
	theAnswer::Bool
	
	function interfaceTest(val::Float64)
		interfaceTest(Int(val))
	end
	
	function interfaceTest(val::Int)
		value = val
		x = val > 10
		theAnswer = val == 42
		
		new(value,x,theAnswer)
	end
end

x = [3,5,48,30,2,2,2,42,21,15,12]
y = interfaceTest.(x)

y.value is the important factor, the rest are derived values. I would like to use simple functions like mean, unique, minimum, and maximum. It’s simple enough to extend each function but it would be very practical to have one way of doing it all at once.

Using the interfaces section of the manual I tried this:

function Base.iterate(test::interfaceTest, state=1)
	state > 1 ? nothing : (test.value, state+1)
end

Testing unique resulted in the same result, even though there are duplicate values in the original data set, it returned every of the interfaceTest values as unique.

That is because these “duplicate values” are actually unique, i.e., they are not equal to each other:

julia> a = interfaceTest(2)
interfaceTest(2, false, false)

julia> b = interfaceTest(2)
interfaceTest(2, false, false)

julia> a == b
false

This is because == falls back to === which returns false because they have different addresses in memory. You need to either implement a custom == method or turn interfaceTest into a non-mutable struct to fix this.

In order to get the other functions (like maximum) to work, you would need to implement max, +, etc. Iteration does not help here.

Maybe something like this is what you want?

julia> struct A
          x::Float64
       end

julia> using Statistics

julia> Statistics.mean(avec::AbstractVector{A}) = mean(a.x for a in avec)

julia> avec = A.(x)
3-element Vector{A}:
 A(1.0)
 A(2.0)
 A(3.0)

julia> mean(avec)
2.0

To get mean to work without defining it explicitly, you would need to define the operations:

julia> import Base:+,-,/

julia> +(x::A,y::A) = x.x + y.x
+ (generic function with 191 methods)

julia> /(x::A,y) = x.x/y
/ (generic function with 108 methods)

julia> +(x,y::A) = x + y.x
+ (generic function with 192 methods)

julia> mean(avec)
2.0


Too bad it’s not as easy as I’d hoped. The difference with mutable is good to know.

@lmiq That is what I had meant about extending each function but it is helpful to see the actual implementations.