How to skip type parameters in function signature

I have a type such as:

struct Foo{A,B}
    a::A
    b::B
end

And a method for foo where I want to use the type parameter A. Then I can write:

mymethod(x::Foo{A,B}) where {A,B} = ...

But since I don’t need B, I could just write:

mymethod(x::Foo{A}) where {A} = ...

Is there also a way of telling “I don’t need the first type parameter, but I do want the second”?

mymethod(x::Foo{_,B}) where B doesn’t work; it’s an “all-underscore identifier used as rvalue” syntax error.

That would be useful for more complex types with many type parameters.

You’re not skipping B there either, it’s a shorthand for a where clause: ::Foo{A,Bi} where Bi. Julia didn’t used to print shorthands of UnionAll types, so it’s worth checking with typeof now.

You can do something similar to omit the 1st parameter from the method’s where clause, but it must be made clear there is a type parameter there rather than some other type from the global scope: mymethod(x::Foo{Ai, B} where Ai) where {B}. Ai will not be available as a variable in the method body, it’s limited to the parametric type expression.

_ is just a variable, not some placeholder token, and all-underscore variables are intended solely for throwaway assignments, so they can only show up on the left hand side of =. If you replaced it with a variable Ai, you’d hit an undefined variable error because Ai isn’t assigned in the outer scope or specified in a where clause.

mymethod(x::Foo{<:Any,B}) where {B}
4 Likes

Worth pointing out that despite looking like the parameter must be a type, <:Any doesn’t actually check for subtyping, so this will also work for types (not Foo) with type parameters that are not types.

julia> Val{<:Any} == (Val{T} where {T<:Any}) == Val{T} where T
true

julia> 1<:Any
ERROR: TypeError: in <:, expected Type, got a value of type Int64
...
julia> Val{1} <: Val{<:Any} # 1<:Any is not checked
true

julia> Val{1} <: Val{<:Number} # 1<:Number is checked
false
1 Like

This seems more like a bug than a feature. I wouldn’t advocate relying on this behavior in your code.

Maybe, but it can’t be changed without breaking working code. Since it is here to stay in v1.x, might as well leverage it for using a where clause shorthand to its fullest.

Not that it matters for the thread, but my actual preference is putting only names in the parameter clause (Vector{T} where T<:Number instead of Vector{<:Number}) and avoiding nested where clauses (where {S,T<:S} instead of where T<:S where S) to mirror the syntax of structs. I’m not above writing unused method parameters to save writing where clauses for individual arguments; f(::Array{<:Any, N}) where N is not a different method from f(::Array{T, N}) where {T,N} and the extra method parameter doesn’t affect compilation. OP may have a different preference and Foo{<:Any,B} seems as brief of an individual where clause without introducing ambiguity.

… obligatory xkcd link.

If it’s a bug — if it’s an undocumented behavior that violates the documented behavior of <: — then any code that relies on it is broken, and it can be freely changed in 1.x.

2 Likes

I see someone linked an existing closed issue that recently still had active debate

I wouldn’t be against introducing :: as constraints for non-type type parameters, then a similar shorthand ::Any could replace <:Any, even for types. I’d prefer isa over :: because isa and <: are also operations that return true or false, but since <: isn’t behaving like that in where clauses, I wouldn’t be much uncomfortable with :: also behaving differently there.

1 Like