Open MasonProtter opened 3 years ago
Very fancy, but definitely not weird at all! This has certainly come up before, and would be really cool. I think it does make it even easier to write lots of ambiguities, but that's never really stopped us before.
I think the first interesting thing that comes up is that we don't get a way to represent the intersection of these "for free". I.e. let All{A} == Tuple{Vararg{A}}
, and Any{A}
be the new thing, then
All{A} ∪ All{B} = Union{All{A}, All{B}} # since we have union types anyway
All{A} ∩ All{B} = All{A ∩ B} # can compute it recursively
Any{A} ∪ Any{B} = Any{A ∪ B}
Any{A} ∩ Any{B} = uhoh!
So we might have to add explicit intersection types at that point too...
TensorFlow.jl wanted this for example.
(If any argument is a TensorFlow.Tensor
then all other arguments need to be a converted to TensorFlow.constant
s).
As a work around we just unrolled 3-5 arguments and handled each.
Could probably make a macro for it
Are intersection types something that we'd want in the type system? I certainly think I'd want it, but I can imagine that it'd be unwanted because it's yet another avenue for writing non-terminating type-level manipulations.
@MasonProtter do the non terminations matter in practice though, GHC triggers plenty of opportunities for them and seems to do fine with a timeout.
As I said, I think I want it. I was more wondering if the language devs could explain if there's some terrible downside to this.
I recall GHC uses only the much more limited HM-type-inference. Non-terminations do occur relatively frequently in Julia already at the inference level, but don't happen in the type system. It seems unlikely that explicit intersection type representations would make this non-terminating, since it is already very similar to existing expressions. (Specifically, I think it is already quite close to being expressed by the constraints implied by repeated TypeVars such as typeintersect(Tuple{T, T} where T, Tuple{A, B} where {A, B})
)
Currently, the only real way to do this is with a trait like mechanism, e.g.
As discussed in #42455, you can't in general accomplish this in Julia right now if you are extending someone else's method, without resorting to type piracy.
Vararg
type, which doesn't require parser changes. Your example above would become:f(args::VarargOne{T,Any,N}) where {N} = ...
(The VarargOne
name can be bikeshedded, of course. We could even just call it Vararg{Any,N,T}
, for that matter, where Vararg{S,N,T}
would denote a tuple of N
elements of type S
, at least one of which must be of type T
, but I'm not sure if adding an optional parameter to Vararg
would be considered a breaking change or too punny.)
Regarding intersections, you could have
VarargSome{S,N,(T1,)} ∩ VarargSome{S,N,(T2,)} = VarargSome{S,N,(T1,T2)}
if you allowed VarargSome{S,N,(T...)}
to be parameterized by a tuple of types T
, each of which must appear in the tuple at least once.
Yes, this seems like a great way to get the thing that I wanted (it seems we also have very similar use cases).
The name can be bikeshed, but I do like that this doesn’t require any funny parser changes or anything like that.
Just to say this would be useful for Convex.jl, which currently pirates hcat
, vcat
, and hvcat
to support when at least one argument is a Convex AbstractExpr
. It is a much worse api to require users use a function like e.g. Convex.hcat
because that doesn't hook into the special [A B]
hcat syntax (and similarly for vcat
and hvcat
). (But it seems we can downgrade our piracy to the point the package will at least precompile on 1.10, so maybe there isn't urgency).
That PR seems right for the current state of affairs for vcat
(waiting on https://github.com/JuliaLang/julia/issues/2326). You are currently permitted to add your own types to hcat
, as long as they do not subtype Number or AbstractArray; that is not piracy. For example, LinearAlgebra
demonstrates how to add AbstractQ
and UniformScaling
to the set of joinable objects.
You are currently permitted to add your own types to hcat, as long as they do not subtype Number or AbstractArray; that is not piracy
We have aliases
const Value = Union{Number,AbstractArray}
const AbstractExprOrValue = Union{AbstractExpr,Value}
so I think Base.hcat(args::AbstractExprOrValue...)
violates that rule, right?
LinearAlgebra demonstrates how to add AbstractQ and UniformScaling to the set of joinable objects
Are you saying using these methods? https://github.com/JuliaLang/julia/blob/f0a28e9a45a34f1c524b3cf02cbbac1351f1da81/stdlib/LinearAlgebra/src/abstractq.jl#L133-L142
Maybe that can work. If there is any AbstractExpr
in the arguments, we have to promote the whole thing to an AbstractExpr
(which is a symbolic object, not really an actual array), so we need some way to ensure Convex's methods win out.
edit: it looks like currently only the first object matters for cat_similar
, so this wouldn't work as-is. If Base had some promotion mechanism to promote among the cat_similar
types for each argument, maybe that could work.
Not those, it defines new vcat methods very similar to that PR, which is a super type of AbstractArray
Okay, so this is a bit of a weird feature request, but there are some circumstances, for example when writing a CAS, where you want to dispatch on whether some type
T
occurs anywhere in an args list. Currently, the only real way to do this is with a trait like mechanism, e.g.I think it would be quite useful if there was a built-in dispatch mechanism for this. Perhaps a syntax like
or even
where
args_right
is allowed to have instances ofT
in it?I get that this is probably too complicated and niche a thing to put in the dispatch system, but I figured I should bring it up in case anyone else and thoughts on this and I didn't see an issue for it.