How to enforce the fields to be inherited?


#1

hello, coming across StatsBase.AbstractWeights, I found it could enforce fields to be inherited. e.g.

import StatsBase

struct MyWeights1{S<:Real, T<:Real, V<:AbstractVector{T}} <: StatsBase.AbstractWeights{S, T, V}
    values::V
    sum::S
end

struct MyWeights2{S<:Real, T<:Real, V<:AbstractVector{T}} <: StatsBase.AbstractWeights{S, T, V}
    notvalues::V
    notsum::S
end

x = MyWeights1([1.0, 2.0], 3.0)
y = MyWeights2([1.0, 2.0], 3.0)

julia> x = MyWeights1([1.0, 2.0], 3.0)
2-element MyWeights1{Float64,Float64,Array{Float64,1}}:
 1.0
 2.0

julia> y = MyWeights2([1.0, 2.0], 3.0)
Error showing value of type MyWeights2{Float64,Float64,Array{Float64,1}}:
ERROR: type MyWeights2 has no field values

I tried to go thru the code, but not understand how could it be done :sweat:

could someone be nice enough to explain the trick? thanks.


#2

I don’t know the code but I assume that it just requires those fields to be defined for the methods to work. Have a look at show method which threw above error: @less show(stdout, MyWeights2([1.0, 2.0], 3.0)). I suspect it is defined on the AbstractWeights and accesses .value.


#3

oic! it does not enforce the field to inherit at all! The following call:

y = MyWeights2([1.0, 2.0], 3.0)

would make the REPL to show() the object, which in turn calls:

Base.size(wv::AbstractWeights) = size(wv.values)

that triggers an error.

Instead, if we suppress REPL from show()ing the object like:

julia> z = MyWeights2([1.0, 2.0], 3.0);

julia> fieldnames(MyWeights1)
(:values, :sum)

julia> fieldnames(MyWeights2)
(:notvalues, :notsum)

any field would be allowed: no enforcement of the fields to be inherited :slightly_frowning_face:


#4

Yes, that works. But presumably other functionality of StatsBase doesn’t work either with those fields. It should be described in the docs, what needs to be satisfied to hook into the functionality of StatsBase.


#5

actuall, it’s disappointing: seems like there’s no easy way to enforce the fields to be inherited.

any suggestions?


#6

Yep. Interfaces in Julia are informal. There are some packages which help with inheriting fields, there was an announcement recently on discourse.


#7

could you please give a link?


#8

#9

There is no trick. As @mauro3 explained, some code just assumes that when typeof(x) <: AbstractWeights, it has certain fields implemented.

It is not clear that StatsBase.AbstractWeights was meant to be extended outside the package. However, if you want to do this, and implement it with different fields, you should just implement the interface, which is probably something like this.