I have problems documenting an abstract type hierarchy when subtyping multiple multiple abstract type. I’m using Documenter.jl’s native :html generation (i.e. don’t use mkdocs).
Example in case:
"Doc A"
abstract A
"Doc B"
abstract B <: A # no documentation problems so far
"Doc C"
abstract C
"Doc D"
abstract D <: A, C # Problem: "WARNING: replacing docs for C..."
The produced html shows the following anomalies:
Documentation of types subtyped from is being replaced by D’s documentation during julia compilation.
Only the first and not all types D is subtyping from are mention in D’s documentation.
There is no multiple inheritance in Julia. Note that this:
julia> abstract D <: A, C
(nothing,C)
returns a tuple, i.e. it defines the type D which returns nothing, and puts that into a tuple with C. So, no subtyping with C happens. Now for some reason the docs get attached to C, thus the warning.
Arguably, abstract D <: A, Cshould be a syntax error (although that might not be possible).
In a complex mixed-feature type hierarchy, what’s the right julia idom combining features in types? Only traits? But traits are based on abstract types so must every feature set (i.e. possible combination of features) then (overwhelmingly…) have its own abstract type…?
Stop worrying so much about types. The OOP mindset is horribly wasteful and inelegant. If you find yourself focused on problems like “well… a TA is both a Studentand a Teacher… I probably need the diamond pattern”, stop, just do.
abstract Person
type TA <: Person end
type Professor <: Person end
teach(::TA) = ...
teach(::Professor) = ...
If your example is so big and complex that this doesn’t work well, then 1) consider if you’re making it more complex than it needs to be, or 2) use traits.
I’ve written a lot of Julia code (and way more C++/Python code), and this approach has yet to limit what I can do.
Another approach you might want to consider is to use parameters. Here’s an example:
julia> abstract BoolType
julia> type TRUE <: BoolType end
julia> type FALSE <: BoolType end
julia> abstract Person{TEACH<:BoolType, LEARN<:BoolType}
julia> type TA <: Person{TRUE,TRUE} end
julia> type Professor <: Person{TRUE,FALSE} end
julia> teach(x) = error("$x can't teach!")
teach (generic function with 1 method)
julia> teach(x::Person{TRUE}) = "what a great teacher!"
teach (generic function with 2 methods)
julia> teach(TA())
"what a great teacher!"
julia> teach(Professor())
"what a great teacher!"
julia> type Student <: Person{FALSE,TRUE} end
julia> teach(Student())
ERROR: Student() can't teach!
in teach(::Student) at ./REPL[7]:1
@tbreloff Yet with a growing number of abstract parameters (traits) we’re going to see an explosion of methods. Just add this:
learn(x) = error("$x can't learn!")
learn(x::Person{TRUE,TRUE}) = "what a great student!"
learn(x::Person{FALSE,TRUE}) = "what a great student!"
learn(Student())
where we need to address both TEACH-ing possibilities. (Not to think of having >3 different boolean trait choices…).
@mauro3’s SimpleTrait/multitrait would solve this…
julia> learn{T<:BoolType}(x::Person{T,TRUE}) = "what a great student!"
learn (generic function with 2 methods)
julia> learn(Student())
"what a great student!"