Nemocas / AbstractAlgebra.jl

Generic abstract algebra functionality in pure Julia (no C dependencies)
https://nemocas.github.io/AbstractAlgebra.jl/dev/index.html
Other
172 stars 63 forks source link

Map interface question/issue #1116

Open thofma opened 2 years ago

thofma commented 2 years ago

For maps, the documentation says that MyMap should belong to an abstract type Map{D, C,...} but D and C need not be the type of the domain and codomain respectively. I guess with "belong", it is meant that the definition should be struct MyMap <: Map{D, C,...}. The documentation is here: https://github.com/Nemocas/AbstractAlgebra.jl/blob/39af1b44e32511fe5049e07b4279e474c22f3f1c/docs/src/map.md#L27-L30

I think this makes the definition https://github.com/Nemocas/AbstractAlgebra.jl/blob/39af1b44e32511fe5049e07b4279e474c22f3f1c/docs/src/map.md#L134-L136 wrong, since this signature does not do what one needs.

We have been using maps for a while now and this type mismatch has always been a bit of a pain, see for example https://github.com/oscar-system/Oscar.jl/pull/1048, where we have maps inside maps and assuming C === typeof(codomain(f)) would come in handy.

Maybe we could reconsider this part of the design? I have not understood why this is necessary.

Here is another thing which is impossible with the current design and is counterintuitive: There is now no way to write a function f accepting maps with domain of type FPGroup. I cannot write f(::Map{Group}), since this is too broad. I cannot write f(::Map{FPGroup}) since there might be map types that have domain FPGroup but for which the implementor chose to subtype <: Map{Group} for whatever reason.

CC: @fingolfin since he also looked at maps some time ago.

wbhart commented 2 years ago

It seems like one does want C to be the type of the codomain etc., but the Map type can still be constructed (for the purposes of functions, not type definitions) to take abstract types. I don't see any specific reason why the restriction should be there.

I assume the second problem is a symptom of the first.

thofma commented 2 years ago

Yes, Map{Group, Group} can still be constructed. If we insist that D and C really are the types of the domain and codomain (which we should), I think believe the type Map{Group, Group} will serve no purpose. There won't be any object with this type not even any object f with typeof(f) <: Map{Group, Group}.

Unfortuntately domain_type === D is already violated for exampe here: https://github.com/Nemocas/AbstractAlgebra.jl/blob/39af1b44e32511fe5049e07b4279e474c22f3f1c/src/generic/GenericTypes.jl#L1196

So it would be a bit of work. But I think it would make the system a bit more sound.

wbhart commented 2 years ago

Does Map{<:Group, <:Group} work?

thofma commented 2 years ago

Yeah, sure, one can and should then use Map{<: Group, <: Group} for maps between groups in function signatures. I probably did not correctly understand your comment "[...] but the Map type can still be constructed (for the purposes of functions, not type definitions) to take abstract types [...]".

HechtiDerLachs commented 2 years ago

See this post for an implementation of the Map type which is not working properly, but was nevertheless allowed.

wbhart commented 2 years ago

Essentially I have no objection to your proposal @thofma if it works in practice (i.e. tests continue to pass).

I am not 100% sure that all the features like map cache and so on still work with your proposal, but also don't immediately see something that wouldn't work. And that probably doesn't even matter since it is mainly user types you are concerned about here. It's not as if we have to enforce it internally so long as things continue to work.

fingolfin commented 2 years ago

I support requiring (and enforcing) C === typeof(codomain(f)) and D === typeof(domain(f)). We then could then also define these handy helpers:

domain_type(f::Map{D,C}) where D, C = D
domain_type(::Type{Map{D,C}}) where D, C = D
codomain_type(f::Map{D,C}) where D, C = C
codomain_type(::Type{Map{D,C}}) where D, C = C

While at it: I also still don't really understand the other two type parameters of Map. I wonder: is there any code outside of AA (e.g. in Nemo, Hecke, Singular, Oscar) that actually uses it?

wbhart commented 2 years ago

I previously tried to explain the other parameters for Map. They are required if you want an inheritance hierarchy for maps due to the fact that if C1 <: C2 and D1 <: D2 then Map{D1, C1} does not belong to Map{D2, C2} in Julia.

One of the parameters gives maps a "class" (for which there is an inheritance hierarchy). This enables functions to be written for all maps in the class, which might include things like formal compositions of your original map class, identity maps for the domain type, maps with section or retraction or inverse, or cached map types, all of which will have a different map type than your original map type, but which you want to behave as though they had the same type as your original map type.

The other parameter provides a way to write functions for your specific map type, since map types are not uniquely identified by their domain and codomain.

The four parameter type was intended to provide a way to do homological algebra, complexes, spectral sequences and the like, eventually, and at some point in the distant future, perhaps category theory.

You can of course propose your own solution to the inheritance problem if you like and redesign maps entirely. I don't mind if you come up with something. But I don't like your chances of solving the issues in a simpler way that doesn't push against the language. I'll certainly admit maps are a difficult thing to type in Julia, and there are multiple compromises in my solution.