JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.72k stars 5.48k forks source link

Convenient function that shows the type hierarchy #24741

Closed tk3369 closed 3 years ago

tk3369 commented 6 years ago

I'm learning Julia and thought it would be great to see the type hierarchy easily. The subtypes function is great but it only shows one level. So I created a simple function that prints the entire subtree. Would it be good to add to Base?

julia> function subtypetree(t, level=1, indent=4)
           level == 1 && println(t)
           for s in subtypes(t)
             println(join(fill(" ", level * indent)) * string(s))
             subtypetree(s, level+1, indent)
           end
       end
subtypetree (generic function with 3 methods)

julia> subtypetree(Number)
Number
    Complex
    Real
        AbstractFloat
            BigFloat
            Float16
            Float32
            Float64
        Integer
            BigInt
            Bool
            Signed
                Int128
                Int16
                Int32
                Int64
                Int8
            Unsigned
                UInt128
                UInt16
                UInt32
                UInt64
                UInt8
        Irrational
        Rational
tk3369 commented 6 years ago

will need to write a little bit of error handling code to be production ready

fredrikekre commented 6 years ago

Also see: https://github.com/JuliaLang/julia/blob/999a4ff5df016b3f0791b6ed703bb03d83d72de3/base/show.jl#L321-L329

StefanKarpinski commented 6 years ago

I do sometimes want to see this kind of tree – could potentially go in Meta?

nalimilan commented 6 years ago

Would be even nicer using Unicode box-drawing characters to print a tree.

vtjnash commented 6 years ago
julia> dump(Number)
Number
  Complex{T<:Real}
  Base.Complex128 = Complex{Float64}
  Base.Complex32 = Complex{Float16}
  Base.Complex64 = Complex{Float32}
  Base.FastMath.ComplexTypes = Union{Complex{Float32}, Complex{Float64}}
  Base.HWNumber = Union{Float32, Float64, Int16, Int32, Int64, Int8, UInt16, UInt32, UInt64, UInt8, Complex{#s55} where #s55<:Union{Float32, Float64, Int16, Int32, Int64, Int8, UInt16, UInt32, UInt64, UInt8}, Rational{#s54} where #s54<:Union{Float32, Float64, Int16, Int32, Int64, Int8, UInt16, UInt32, UInt64, UInt8}}
  Base.LinAlg.BlasComplex = Union{Complex{Float32}, Complex{Float64}}
  Base.LinAlg.BlasFloat = Union{Complex{Float32}, Complex{Float64}, Float32, Float64}
  Base.ScalarIndex = Real
  Base.ViewIndex = Union{Real, AbstractArray}
...
~/julia$ ./julia examples/typetree.jl 
+- Any << abstract immutable >>
.  +- AbstractArray{T,2} << abstract immutable >>
.  .  +- Core.AbstractArray{T, N} = AbstractArray 
.  .  +- Core.Inference.AbstractMatrix{T} = AbstractArray{T,2} where T 
.  .  +- Base.AbstractMatrix{T} = AbstractArray{T,2} where T 
.  .  +- Base.LinAlg.AbstractQ{T} << abstract immutable >>
.  .  .  +- Base.LinAlg.AbstractQ{T} = Base.LinAlg.AbstractQ 
.  .  .  +- Base.LinAlg.QRCompactWYQ{S,M<:(AbstractArray{T,2} where T)} << concrete immutable >>
.  .  .  .  +- Base.LinAlg.QRCompactWYQ{S, M<:(AbstractArray{T,2} where T)} = Base.LinAlg.QRCompactWYQ 
.  .  .  +- Base.LinAlg.QRPackedQ{T,S<:(AbstractArray{T,2} where T)} << concrete immutable >>
.  .  .  .  +- Base.LinAlg.QRPackedQ{T, S<:(AbstractArray{T,2} where T)} = Base.LinAlg.QRPackedQ 
.  .  +- AbstractRange{T} << abstract immutable >>
.  .  .  +- Base.AbstractRange{T} = AbstractRange 
.  .  .  +- LinSpace{T} << concrete immutable >>
.  .  .  .  +- Base.LinSpace{T} = LinSpace 
.  .  .  +- OrdinalRange{T,Int64} << abstract immutable >>
...

Yes, I agree these aren't exactly discoverable though.

StefanKarpinski commented 6 years ago

Those are sufficiently undiscoverable and under-document that I think we should keep this issue open to track having/documenting a way to do this. In particular, I really do not think this should be part of dump and it seems like this is a specific enough thing to warrant its own function.

s-celles commented 6 years ago

I wonder if such a feature shouldn't be in a standalone package.

Maybe we should have a function which output a tree datastructure and have function for several kind of display (ASCII, Unicode with box drawing char, Graphviz tree using GraphViz.jl, Tikz tree drawing using TikzPictures.jl or using any graph/tree drawing library that some contributors could suggest here...)

AbstractTrees.jl from @Keno have ASCII formatting functions for tree

Maybe being able to output this kind of drawing automatically could be a nice feature to have https://en.wikibooks.org/wiki/Introducing_Julia/Types#Type_hierarchy

Maybe D3Trees.jl from @sisl could be considered for drawing tree.

First task is probably to create a tree from this Depth First Search (DFS) traversal. I have never achieved this kind of work previously and I don't know if there is ever a Julia package to do it simply or if it should be done. Any idea?

carstenbauer commented 6 years ago

Would love to have this feature somewhere! Maybe a simple version (like above) in Meta and a more fancy version in a separate package.

karlwessel commented 5 years ago

Just for completeness, with Kenos AbstractTrees this is a three liner:

julia> using AbstractTrees

julia> AbstractTrees.children(x::Type) = subtypes(x)

julia> print_tree(Number)
Number
├─ Complex
└─ Real
   ├─ AbstractFloat
   │  ├─ BigFloat
   │  ├─ Float16
   │  ├─ Float32
   │  └─ Float64
   ├─ AbstractIrrational
   │  └─ Irrational
   ├─ Integer
   │  ├─ Bool
   │  ├─ Signed
   │  │  ├─ BigInt
   │  │  ├─ Int128
   │  │  ├─ Int16
   │  │  ├─ Int32
   │  │  ├─ Int64
   │  │  └─ Int8
   │  └─ Unsigned
   │     ├─ UInt128
   │     ├─ UInt16
   │     ├─ UInt32
   │     ├─ UInt64
   │     └─ UInt8
   └─ Rational
cnaak commented 3 years ago

I've written my own function with similar functionality a while ago. Today I've felt the need for that again and searched to check whether such a package exist and came across this discussion.

I've then adapted my old code into a standalone, modest package (which will likely remain plain and simple), which is now in the process of registration, so that others can discover, easily ] add and use.

The registration PR (which contains link to the original repo) is HERE.

Usage example:

julia> using TypeTree

julia> tt(Number, concrete=false) # filters off concrete types
7-element Vector{String}:
 "Number\n"
 " └─ Real\n"
 "     ├─ AbstractFloat\n"
 "     ├─ AbstractIrrational\n"
 "     └─ Integer\n"
 "         ├─ Signed\n"
 "         └─ Unsigned\n"

Moreover, join and print can be used as to produce a pretty print:

julia> print(join(tt(Signed), "")) # includes all concrete/abstract subtypes
Signed
 ├─ BigInt
 ├─ Int128
 ├─ Int16
 ├─ Int32
 ├─ Int64
 └─ Int8

And Unicode can be avoided with:

julia> print(join(tt(Unsigned, uni=false), ""))
Unsigned
 +-- UInt128
 +-- UInt16
 +-- UInt32
 +-- UInt64
 \-- UInt8
KristofferC commented 3 years ago

With a package existing that perfectly implements the functionality requested here, I think this can be closed (again).

FedeClaudi commented 2 years ago

A bit late to the party here, but Term now provides functionality very much like the one discussed above, see [here]: https://fedeclaudi.github.io/Term.jl/stable/ren/tree/#TypeTree

image
cnaak commented 2 years ago

Thank you, Frederico, and nice package!

Best regards,

StefanKarpinski commented 2 years ago

@cnaak, please be careful when posting to GitHub via email with a signature—you are effectively posting all your personal details on the internet. I have edited your comment to remove the signature.

cnaak commented 2 years ago

@cnaak, please be careful when posting to GitHub via email with a signature—you are effectively posting all your personal details on the internet. I have edited your comment to remove the signature.

Thank you @StefanKarpinski , the edit was so kind of you! Really appreciated!!