Open adigitoleo opened 2 years ago
@adigitoleo I would like to work on this issue, can you please guide me to get started to contribute to Julia. Thanks!
@Preetham-Reddy-007 Thanks for your interest in contributing. Contributor guidelines are available, in particular see the checklist and the documentation section.
My idea when writing the issue was to improve the types page in the manual, focusing on the parametric types section. I don't yet completely understand the answers to all of the points I raised in the first post :smile:, so the first step would be to research Julia's parametric type implementation and come up with a concise overview.
Have a play around with the type system and see if you can understand it. Check for things that are surprising compared to what the manual says. For example,
EDIT: This was just me not getting it, Bar isa DataType
is what I should have checked here.
I just came across another package which once again defined an abstract container like (very simplified):
julia> struct Foo{T<:Real,N} <: AbstractArray{T,N}
a::Array{T,N}
b::String
end
which leads to problems like:
julia> Foo(transpose([1, 2, 3, 4]), "wtf")
ERROR: MethodError: no method matching Foo(::LinearAlgebra.Transpose{Int64, Vector{Int64}}, ::String)
Closest candidates are:
Foo(::Array{T, N}, ::String) where {T<:Real, N} at REPL[1]:2
Stacktrace:
[1] top-level scope
@ REPL[2]:1
The solution is:
julia> struct Foo{T<:AbstractArray}
a::T
b::String
end
julia> Foo(transpose([1, 2, 3, 4]), "yay")
Foo{LinearAlgebra.Transpose{Int64, Vector{Int64}}}([1 2 3 4], "yay")
However, it looks a bit like a::T
might be an abstract field type. There is valid reason to avoid abstract field types. This could scare people into using the first option. The good news is that the field type is not actually abstract in this case:
julia> foo = Foo(transpose([1, 2, 3, 4]), "yay")
Foo{LinearAlgebra.Transpose{Int64, Vector{Int64}}}([1 2 3 4], "yay")
julia> typeof(foo)
Foo{LinearAlgebra.Transpose{Int64, Vector{Int64}}}
julia> isconcretetype(typeof(foo.a))
true
I think this is one of the main things that deserves stronger demonstration. When people start using the first option, it leads down the rabbit hole of over-constraining of argument types to avoid a MethodError from the constructor.
I've found that some of the relevant information is in the constructors section, there's even a case study example. However, the focus is more on advanced constructor logic.
At the risk of overloading this thread, I'll also mention that the last example from the constructors section suffers from the same problem:
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
end
julia> SummedArray([1; 2; 3], 6)
SummedArray{Int64, Int64}([1, 2, 3], 6)
julia> SummedArray(transpose([1; 2; 3]), 6)
ERROR: MethodError: no method matching SummedArray(::LinearAlgebra.Transpose{Int64, Vector{Int64}}, ::Int64)
Closest candidates are:
SummedArray(::Vector{T}, ::S) where {T<:Number, S<:Number} at REPL[5]:2
Stacktrace:
[1] top-level scope
@ REPL[7]:1
I'm using transpose
a lot, for demonstration, there are plenty of other methods which return SomeType <: AbstractArray
where SomeType
is not Array
or Vector
. Especially when you start to use packages.
You seem to have a pretty clear idea for how you want that section to look like, would you you like to just propose a PR yourself? I do think this could definitely be useful.
@simeonschaub Sure, I'll have a go. Might take another day or two for me to grok all the details and come up with something concise. Wouldn't want to upload something that's misleading.
@adigitoleo thanks for the precise details. I am a beginner and just started with Julia, might take some time.
@Preetham-Reddy-007 these issues might be simpler to start with, they are also about improving documentation: #30469, #28712
I want to work on this. If the issue is open, please assign it to me.
@MashiatK Same as with the other issues, just open a PR and we can go from there, assignment isn't really necessary ;)
I just rebased onto master if there is still interest. The linked PR (#43891) was my attemt to clarify some of the documentation on parametric types, but feel free to open a new PR if you have other ideas.
After a discussion in IRC today I realised that there were a few things about parametric types that aren't easily understood. It seems that the manual could help a bit more in introducing parametric types. I'm opening this issue to outline some of the things I think could be documented better, and also to gather ideas or examples that might help to write a more hands-on parametric types section in the manual. Not sure if this belongs on the forum but I am thinking of linking a PR at some point.
For the parametric type
Foo
and an instancefoo
, I think the docs could clarifyisabstracttype(Foo)
andisconcretetype(Foo)
return false for the parametric typeFoo
, i.e. that parametric types are neither abstract nor concreteFoo
typeof(foo) <: Foo
is true, howeversupertype(typeof(foo)) == Foo
is false (it'sAny
)Foo isa DataType
is falseand (more visibly) inform
that parametric types cannot be subtypedthey can, parametric abstract types are a thingtypeof(foo) != Foo
wherefoo
is an instance ofFoo
foo isa Foo
must be used to test iffoo
is an instance ofFoo
I also think there should be one example on how to set up a simple but complete type hierarchy (without going into all of the gritty details of the Advanced Types section). Something like the comment by wasshin in #4935.
There might be some other things I've forgotten, feel free to suggest related improvements. I feel that parametric types are one of the major features of Julia's type system, however they can be confusing when coming from languages with more traditional/simple OOP systems. Understanding parametric types should help to reduce common antipatterns like over-constraining argument types, and help people to leverage the dispatch system (what are all those
<:
needed for anyway?)