Skip to content

Commit

Permalink
Deprecate cat(dim, Xs...) to cat(Xs..., dim=dim) (#27163)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbauman authored and JeffBezanson committed May 22, 2018
1 parent 0961ee3 commit a916cbc
Show file tree
Hide file tree
Showing 18 changed files with 95 additions and 91 deletions.
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ Deprecated or removed
`dims` keyword argument. This includes the functions `sum`, `prod`, `maximum`,
`minimum`, `all`, `any`, `findmax`, `findmin`, `mean`, `varm`, `std`, `var`, `cov`,
`cor`, `median`, `mapreducedim`, `reducedim`, `sort`, `accumulate`, `accumulate!`,
`cumsum`, `cumsum!`, `cumprod`, `cumprod!`, `flipdim`, and `squeeze` ([#25501]).
`cumsum`, `cumsum!`, `cumprod`, `cumprod!`, `flipdim`, `squeeze`, and `cat` ([#25501], [#26660], [#27100]).

* `indices(a)` and `indices(a,d)` have been deprecated in favor of `axes(a)` and
`axes(a, d)` ([#25057]).
Expand Down
52 changes: 27 additions & 25 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ promote_eltype() = Bottom
promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...))

#TODO: ERROR CHECK
cat(catdim::Integer) = Vector{Any}()
_cat(catdim::Integer) = Vector{Any}()

typed_vcat(::Type{T}) where {T} = Vector{T}()
typed_hcat(::Type{T}) where {T} = Vector{T}()
Expand Down Expand Up @@ -1310,19 +1310,20 @@ _cs(d, a, b) = (a == b ? a : throw(DimensionMismatch(
dims2cat(::Val{n}) where {n} = ntuple(i -> (i == n), Val(n))
dims2cat(dims) = ntuple(in(dims), maximum(dims))

cat(dims, X...) = cat_t(dims, promote_eltypeof(X...), X...)
_cat(dims, X...) = cat_t(promote_eltypeof(X...), X...; dims=dims)

function cat_t(dims, T::Type, X...)
@inline cat_t(::Type{T}, X...; dims) where {T} = _cat_t(dims, T, X...)
@inline function _cat_t(dims, T::Type, X...)
catdims = dims2cat(dims)
shape = cat_shape(catdims, (), map(cat_size, X)...)
A = cat_similar(X[1], T, shape)
if T <: Number && count(!iszero, catdims) > 1
fill!(A, zero(T))
end
return _cat(A, shape, catdims, X...)
return __cat(A, shape, catdims, X...)
end

function _cat(A, shape::NTuple{N}, catdims, X...) where N
function __cat(A, shape::NTuple{N}, catdims, X...) where N
offsets = zeros(Int, N)
inds = Vector{UnitRange{Int}}(undef, N)
concat = copyto!(zeros(Bool, N), catdims)
Expand Down Expand Up @@ -1376,7 +1377,7 @@ julia> vcat(c...)
4 5 6
```
"""
vcat(X...) = cat(Val(1), X...)
vcat(X...) = cat(X...; dims=Val(1))
"""
hcat(A...)
Expand Down Expand Up @@ -1418,13 +1419,13 @@ julia> hcat(c...)
3 6
```
"""
hcat(X...) = cat(Val(2), X...)
hcat(X...) = cat(X...; dims=Val(2))

typed_vcat(T::Type, X...) = cat_t(Val(1), T, X...)
typed_hcat(T::Type, X...) = cat_t(Val(2), T, X...)
typed_vcat(T::Type, X...) = cat_t(T, X...; dims=Val(1))
typed_hcat(T::Type, X...) = cat_t(T, X...; dims=Val(2))

"""
cat(dims, A...)
cat(A...; dims=dims)
Concatenate the input arrays along the specified dimensions in the iterable `dims`. For
dimensions not in `dims`, all input arrays should have the same size, which will also be the
Expand All @@ -1434,27 +1435,28 @@ a single number, the different arrays are tightly stacked along that dimension.
an iterable containing several dimensions, this allows one to construct block diagonal
matrices and their higher-dimensional analogues by simultaneously increasing several
dimensions for every new input array and putting zero blocks elsewhere. For example,
`cat([1,2], matrices...)` builds a block diagonal matrix, i.e. a block matrix with
`cat(matrices...; dims=(1,2))` builds a block diagonal matrix, i.e. a block matrix with
`matrices[1]`, `matrices[2]`, ... as diagonal blocks and matching zero blocks away from the
diagonal.
"""
cat(catdims, A::AbstractArray{T}...) where {T} = cat_t(catdims, T, A...)
@inline cat(A...; dims) = _cat(dims, A...)
_cat(catdims, A::AbstractArray{T}...) where {T} = cat_t(T, A...; dims=catdims)

# The specializations for 1 and 2 inputs are important
# especially when running with --inline=no, see #11158
vcat(A::AbstractArray) = cat(Val(1), A)
vcat(A::AbstractArray, B::AbstractArray) = cat(Val(1), A, B)
vcat(A::AbstractArray...) = cat(Val(1), A...)
hcat(A::AbstractArray) = cat(Val(2), A)
hcat(A::AbstractArray, B::AbstractArray) = cat(Val(2), A, B)
hcat(A::AbstractArray...) = cat(Val(2), A...)

typed_vcat(T::Type, A::AbstractArray) = cat_t(Val(1), T, A)
typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(Val(1), T, A, B)
typed_vcat(T::Type, A::AbstractArray...) = cat_t(Val(1), T, A...)
typed_hcat(T::Type, A::AbstractArray) = cat_t(Val(2), T, A)
typed_hcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(Val(2), T, A, B)
typed_hcat(T::Type, A::AbstractArray...) = cat_t(Val(2), T, A...)
vcat(A::AbstractArray) = cat(A; dims=Val(1))
vcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(1))
vcat(A::AbstractArray...) = cat(A...; dims=Val(1))
hcat(A::AbstractArray) = cat(A; dims=Val(2))
hcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(2))
hcat(A::AbstractArray...) = cat(A...; dims=Val(2))

typed_vcat(T::Type, A::AbstractArray) = cat_t(T, A; dims=Val(1))
typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(T, A, B; dims=Val(1))
typed_vcat(T::Type, A::AbstractArray...) = cat_t(T, A...; dims=Val(1))
typed_hcat(T::Type, A::AbstractArray) = cat_t(T, A; dims=Val(2))
typed_hcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(T, A, B; dims=Val(2))
typed_hcat(T::Type, A::AbstractArray...) = cat_t(T, A...; dims=Val(2))

# 2d horizontal and vertical concatenation

Expand Down
2 changes: 1 addition & 1 deletion base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1514,7 +1514,7 @@ function vcat(arrays::Vector{T}...) where T
return arr
end

cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x)))
_cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x)))

## find ##

Expand Down
4 changes: 2 additions & 2 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1718,11 +1718,11 @@ function vcat(A::BitMatrix...)
end

# general case, specialized for BitArrays and Integers
function cat(dims::Integer, X::Union{BitArray, Bool}...)
function _cat(dims::Integer, X::Union{BitArray, Bool}...)
catdims = dims2cat(dims)
shape = cat_shape(catdims, (), map(cat_size, X)...)
A = falses(shape)
return _cat(A, shape, catdims, X...)
return __cat(A, shape, catdims, X...)
end

# hvcat -> use fallbacks in abstractarray.jl
Expand Down
11 changes: 9 additions & 2 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,17 @@ end
@deprecate fill_to_length(t, val, ::Type{Val{N}}) where {N} fill_to_length(t, val, Val(N)) false
@deprecate literal_pow(a, b, ::Type{Val{N}}) where {N} literal_pow(a, b, Val(N)) false
@eval IteratorsMD @deprecate split(t, V::Type{Val{n}}) where {n} split(t, Val(n)) false
@deprecate cat(::Type{Val{N}}, A::AbstractArray...) where {N} cat(Val(N), A...)
@deprecate cat_t(::Type{Val{N}}, ::Type{T}, A, B) where {N,T} cat_t(Val(N), T, A, B) false
@deprecate cat(::Type{Val{N}}, A::AbstractArray...) where {N} cat(A..., dims=Val(N))
@deprecate cat_t(::Type{Val{N}}, ::Type{T}, A, B) where {N,T} cat_t(T, A, B, dims=Val(N)) false
@deprecate reshape(A::AbstractArray, ::Type{Val{N}}) where {N} reshape(A, Val(N))

# Issue #
@deprecate cat(dims, As...) cat(As..., dims=dims)
@deprecate cat_t(dims, ::Type{T}, As...) where {T} cat_t(T, As...; dims=dims) false
# Disambiguate
cat_t(::Type{T}, ::Type{S}, As...; dims=dims) where {T,S} = _cat_t(T, S, As...; dims=dims)


@deprecate read(s::IO, x::Ref) read!(s, x)

@deprecate read(s::IO, t::Type, d1::Int, dims::Int...) read!(s, Array{t}(undef, d1, dims...))
Expand Down
5 changes: 3 additions & 2 deletions base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,10 @@ const disable_text_style = AnyDict(
# of colors.
available_text_colors = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors)))
const possible_formatting_symbols = [:normal, :bold, :default]
available_text_colors = cat(1,
available_text_colors = cat(
sort!(intersect(available_text_colors, possible_formatting_symbols), rev=true),
sort!(setdiff( available_text_colors, possible_formatting_symbols)))
sort!(setdiff( available_text_colors, possible_formatting_symbols));
dims=1)

const available_text_colors_docstring =
string(join([string("`:", key,"`")
Expand Down
10 changes: 5 additions & 5 deletions doc/src/manual/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ converted to that type using [`convert`](@ref).

Arrays can be constructed and also concatenated using the following functions:

| Function | Description |
|:---------------------- |:---------------------------------------------------- |
| [`cat(k, A...)`](@ref) | concatenate input n-d arrays along the dimension `k` |
| [`vcat(A...)`](@ref) | shorthand for `cat(1, A...)` |
| [`hcat(A...)`](@ref) | shorthand for `cat(2, A...)` |
| Function | Description |
|:--------------------------- |:----------------------------------------------- |
| [`cat(A...; dims=k)`](@ref) | concatenate input arrays along dimension(s) `k` |
| [`vcat(A...)`](@ref) | shorthand for `cat(A...; dims=1)` |
| [`hcat(A...)`](@ref) | shorthand for `cat(A...; dims=2)` |

Scalar values passed to these functions are treated as 1-element arrays.

Expand Down
20 changes: 10 additions & 10 deletions stdlib/LinearAlgebra/test/special.jl
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ end
@test issparse(hcat(specialmata, specialmatb))
@test issparse(vcat(specialmata, specialmatb))
@test issparse(hvcat((1,1), specialmata, specialmatb))
@test issparse(cat((1,2), specialmata, specialmatb))
@test issparse(cat(specialmata, specialmatb; dims=(1,2)))
end
# Test concatenating pairwise combinations of special matrices with sparse matrices,
# dense matrices, or dense vectors
Expand All @@ -157,8 +157,8 @@ end
@test issparse(hcat(othermatorvec, specialmat))
@test issparse(hvcat((2,), specialmat, othermatorvec))
@test issparse(hvcat((2,), othermatorvec, specialmat))
@test issparse(cat((1,2), specialmat, othermatorvec))
@test issparse(cat((1,2), othermatorvec, specialmat))
@test issparse(cat(specialmat, othermatorvec; dims=(1,2)))
@test issparse(cat(othermatorvec, specialmat; dims=(1,2)))
end
end
end
Expand Down Expand Up @@ -199,7 +199,7 @@ end
@test issparse(vcat(annospcmata, annospcmatb))
@test issparse(hcat(annospcmata, annospcmatb))
@test issparse(hvcat((2,), annospcmata, annospcmatb))
@test issparse(cat((1,2), annospcmata, annospcmatb))
@test issparse(cat(annospcmata, annospcmatb; dims=(1,2)))
end
# Test that concatenations of pairwise combinations of annotated sparse/special
# matrices and other matrix/vector types yield sparse matrices
Expand All @@ -215,8 +215,8 @@ end
@test issparse(hcat(other, annospcmat))
@test issparse(hvcat((2,), annospcmat, other))
@test issparse(hvcat((2,), other, annospcmat))
@test issparse(cat((1,2), annospcmat, other))
@test issparse(cat((1,2), other, annospcmat))
@test issparse(cat(annospcmat, other; dims=(1,2)))
@test issparse(cat(other, annospcmat; dims=(1,2)))
end
end
# The preceding tests should cover multi-way combinations of those types, but for good
Expand All @@ -227,8 +227,8 @@ end
@test issparse(hcat(annodmats[2], annospcmats[4], spvec, densevec, diagmat))
@test issparse(hvcat((5,), diagmat, densevec, spvec, annodmats[1], annospcmats[1]))
@test issparse(hvcat((5,), spvec, annodmats[2], diagmat, densevec, annospcmats[2]))
@test issparse(cat((1,2), annodmats[1], diagmat, annospcmats[3], densevec, spvec))
@test issparse(cat((1,2), spvec, diagmat, densevec, annospcmats[4], annodmats[2]))
@test issparse(cat(annodmats[1], diagmat, annospcmats[3], densevec, spvec; dims=(1,2)))
@test issparse(cat(spvec, diagmat, densevec, annospcmats[4], annodmats[2]; dims=(1,2)))
# Test that concatenations strictly involving un/annotated dense matrices/vectors
# yield dense arrays
for densemata in (densemat, annodmats...)
Expand All @@ -243,8 +243,8 @@ end
@test !issparse(hcat(otherdense, densemata))
@test !issparse(hvcat((2,), densemata, otherdense))
@test !issparse(hvcat((2,), otherdense, densemata))
@test !issparse(cat((1,2), densemata, otherdense))
@test !issparse(cat((1,2), otherdense, densemata))
@test !issparse(cat(densemata, otherdense; dims=(1,2)))
@test !issparse(cat(otherdense, densemata; dims=(1,2)))
end
end
end
Expand Down
5 changes: 0 additions & 5 deletions stdlib/SparseArrays/src/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ using Base: @deprecate, depwarn

# BEGIN 0.7 deprecations

# PR #22475
import Base: cat
@deprecate cat(::Type{Val{N}}, A::_SparseConcatGroup...) where {N} cat(Val(N), A...)
@deprecate cat(::Type{Val{N}}, A::_DenseConcatGroup...) where {N} cat(Val(N), A...)

# deprecate remaining vectorized methods over SparseVectors (zero-preserving)
for op in (:floor, :ceil, :trunc, :round,
:log1p, :expm1, :sinpi,
Expand Down
8 changes: 4 additions & 4 deletions stdlib/SparseArrays/src/sparsevector.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1017,10 +1017,10 @@ const _DenseConcatGroup = Union{Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any
const _TypedDenseConcatGroup{T} = Union{Vector{T}, Adjoint{T,Vector{T}}, Transpose{T,Vector{T}}, LinearAlgebra.RowVector{T,Vector{T}}, Matrix{T}, _Annotated_Typed_DenseArrays{T}}

# Concatenations involving un/annotated sparse/special matrices/vectors should yield sparse arrays
function cat(catdims, Xin::_SparseConcatGroup...)
function Base._cat(dims, Xin::_SparseConcatGroup...)
X = map(x -> SparseMatrixCSC(issparse(x) ? x : sparse(x)), Xin)
T = promote_eltype(Xin...)
Base.cat_t(catdims, T, X...)
Base.cat_t(T, X...; dims=dims)
end
function hcat(Xin::_SparseConcatGroup...)
X = map(x -> SparseMatrixCSC(issparse(x) ? x : sparse(x)), Xin)
Expand Down Expand Up @@ -1048,14 +1048,14 @@ promote_to_array_type(A::Tuple{Vararg{Union{_DenseConcatGroup,UniformScaling}}})
promote_to_arrays_(n::Int, ::Type{SparseMatrixCSC}, J::UniformScaling) = sparse(J, n, n)

# Concatenations strictly involving un/annotated dense matrices/vectors should yield dense arrays
cat(catdims, xs::_DenseConcatGroup...) = Base.cat_t(catdims, promote_eltype(xs...), xs...)
Base._cat(dims, xs::_DenseConcatGroup...) = Base.cat_t(promote_eltype(xs...), xs...; dims=dims)
vcat(A::Vector...) = Base.typed_vcat(promote_eltype(A...), A...)
vcat(A::_DenseConcatGroup...) = Base.typed_vcat(promote_eltype(A...), A...)
hcat(A::Vector...) = Base.typed_hcat(promote_eltype(A...), A...)
hcat(A::_DenseConcatGroup...) = Base.typed_hcat(promote_eltype(A...), A...)
hvcat(rows::Tuple{Vararg{Int}}, xs::_DenseConcatGroup...) = Base.typed_hvcat(promote_eltype(xs...), rows, xs...)
# For performance, specially handle the case where the matrices/vectors have homogeneous eltype
cat(catdims, xs::_TypedDenseConcatGroup{T}...) where {T} = Base.cat_t(catdims, T, xs...)
Base._cat(dims, xs::_TypedDenseConcatGroup{T}...) where {T} = Base.cat_t(T, xs...; dims=dims)
vcat(A::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_vcat(T, A...)
hcat(A::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_hcat(T, A...)
hvcat(rows::Tuple{Vararg{Int}}, xs::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_hvcat(T, rows, xs...)
Expand Down
6 changes: 3 additions & 3 deletions stdlib/SparseArrays/test/sparse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1844,7 +1844,7 @@ end
@test issparse(vcat(spmat, spmat))
@test issparse(hcat(spmat, spmat))
@test issparse(hvcat((2,), spmat, spmat))
@test issparse(cat((1,2), spmat, spmat))
@test issparse(cat(spmat, spmat; dims=(1,2)))
# Test that concatenations of a sparse matrice with a dense matrix/vector yield sparse arrays
@test issparse(vcat(spmat, densemat))
@test issparse(vcat(densemat, spmat))
Expand All @@ -1853,8 +1853,8 @@ end
@test issparse(hcat(densearg, spmat))
@test issparse(hvcat((2,), spmat, densearg))
@test issparse(hvcat((2,), densearg, spmat))
@test issparse(cat((1,2), spmat, densearg))
@test issparse(cat((1,2), densearg, spmat))
@test issparse(cat(spmat, densearg; dims=(1,2)))
@test issparse(cat(densearg, spmat; dims=(1,2)))
end
end

Expand Down
8 changes: 4 additions & 4 deletions stdlib/SparseArrays/test/sparsevector.jl
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,8 @@ end
@test issparse(hcat(othervecormat, spvec))
@test issparse(hvcat((2,), spvec, othervecormat))
@test issparse(hvcat((2,), othervecormat, spvec))
@test issparse(cat((1,2), spvec, othervecormat))
@test issparse(cat((1,2), othervecormat, spvec))
@test issparse(cat(spvec, othervecormat; dims=(1,2)))
@test issparse(cat(othervecormat, spvec; dims=(1,2)))
end
# The preceding tests should cover multi-way combinations of those types, but for good
# measure test a few multi-way combinations involving those types
Expand All @@ -487,8 +487,8 @@ end
@test issparse(hcat(densemat, spmat, spvec, densevec, diagmat))
@test issparse(hvcat((5,), diagmat, densevec, spvec, densemat, spmat))
@test issparse(hvcat((5,), spvec, densemat, diagmat, densevec, spmat))
@test issparse(cat((1,2), densemat, diagmat, spmat, densevec, spvec))
@test issparse(cat((1,2), spvec, diagmat, densevec, spmat, densemat))
@test issparse(cat(densemat, diagmat, spmat, densevec, spvec; dims=(1,2)))
@test issparse(cat(spvec, diagmat, densevec, spmat, densemat; dims=(1,2)))
end
@testset "vertical concatenation of SparseVectors with different el- and ind-type (#22225)" begin
spv6464 = SparseVector(0, Int64[], Int64[])
Expand Down
2 changes: 1 addition & 1 deletion test/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ function test_cat(::Type{TestAbstractArray})
D = [1:24...]
i = rand(1:10)

@test cat(i) == Any[]
@test cat(;dims=i) == Any[]
@test vcat() == Any[]
@test hcat() == Any[]
@test hcat(1, 1.0, 3, 3.0) == [1.0 1.0 3.0 3.0]
Expand Down
6 changes: 3 additions & 3 deletions test/ambiguous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ end
Set{Method}(detect_unbound_args(Core; recursive=true))
pop!(need_to_handle_undef_sparam, which(Core.Compiler.eltype, Tuple{Type{Tuple{Any}}}))
@test_broken need_to_handle_undef_sparam == Set()
pop!(need_to_handle_undef_sparam, which(Core.Compiler.cat, Tuple{Any, AbstractArray}))
pop!(need_to_handle_undef_sparam, which(Core.Compiler._cat, Tuple{Any, AbstractArray}))
pop!(need_to_handle_undef_sparam, first(methods(Core.Compiler.same_names)))
pop!(need_to_handle_undef_sparam, which(Core.Compiler.convert, (Type{Union{Core.Compiler.Some{T}, Nothing}} where T, Core.Compiler.Some)))
pop!(need_to_handle_undef_sparam, which(Core.Compiler.convert, (Type{Union{T, Nothing}} where T, Core.Compiler.Some)))
Expand All @@ -285,9 +285,9 @@ end
pop!(need_to_handle_undef_sparam, which(Base.eltype, Tuple{Type{Tuple{Any}}}))
pop!(need_to_handle_undef_sparam, first(methods(Base.same_names)))
@test_broken need_to_handle_undef_sparam == Set()
pop!(need_to_handle_undef_sparam, which(Base.cat, Tuple{Any, AbstractArray}))
pop!(need_to_handle_undef_sparam, which(Base._cat, Tuple{Any, AbstractArray}))
pop!(need_to_handle_undef_sparam, which(Base.byteenv, (Union{AbstractArray{Pair{T}, 1}, Tuple{Vararg{Pair{T}}}} where T<:AbstractString,)))
pop!(need_to_handle_undef_sparam, which(Base.cat, (Any, SparseArrays._TypedDenseConcatGroup{T} where T)))
pop!(need_to_handle_undef_sparam, which(Base._cat, (Any, SparseArrays._TypedDenseConcatGroup{T} where T)))
pop!(need_to_handle_undef_sparam, which(Base.float, Tuple{AbstractArray{Union{Missing, T},N} where {T, N}}))
pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Union{Missing, T}} where T, Any}))
pop!(need_to_handle_undef_sparam, which(Base.promote_rule, Tuple{Type{Union{Nothing, S}} where S, Type{T} where T}))
Expand Down
Loading

0 comments on commit a916cbc

Please sign in to comment.