Documenting abstract types & Documenter.jl

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:

  1. Documentation of types subtyped from is being replaced by D’s documentation during julia compilation.
  2. Only the first and not all types D is subtyping from are mention in D’s documentation.

Any ideas?

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).

2 Likes

Hm…, coming from Python …

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…?

“You must unlearn what you have learned”

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 Student and 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.

5 Likes

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

@mauro3 Yes indeed… Have started to reimplement the desing with your SimpleTraits (nice, thanks). Multitraits is not functional yet, is it?

@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!"

@dpsanders Thanks for showing how to capitalize on Julia’s builtin strength.

@tbreloff Thanks for showing the elegance of parameterized abstract types.