I am not sure I understand why not having dedicated syntax for nx1 matrices is a problem,as you can still create them rather easily. I see this more as a documentation issue.
The only reason to prefer directly constructing the array (e.g. using the macro I proposed) is that it avoids allocating a temporary Vector and is therefore a bit faster:
julia> f1(x, y) = reshape([x, y], :, 1)
f1 (generic function with 1 method)
julia> f2(x, y) = @col [x, y]
f2 (generic function with 1 method)
julia> using BenchmarkTools
julia> @btime f1($1, $2)
53.962 ns (3 allocations: 192 bytes)
2×1 Array{Int64,2}:
1
2
julia> @btime f2($1, $2)
33.171 ns (1 allocation: 96 bytes)
2×1 Array{Int64,2}:
1
2
We simply don’t have enough surface syntax available to cover all the possible permutations of dimensionality and concatenation. In fact I’d re-phrase our current setup like this:
No concatenation
,-delimited vectors only
Concatenation:
space-delimited does horizontal concatenation (thus is 2+ dimensional)
;- and newline-delimited does vertical concatenation (thus is either 1- or 2+ dimensional)
Since we’re doing concatenation with ;, you can actually get the behavior you want like this:
It was discussed in the lead up to 1.0 whether [1; 2; 3] should construct a vector or a matrix. But note that there’s nothing special about two dimensions in concatenation syntax in general:
The [... ; ...] syntax calls vcat which does “concatenation along the first dimension”—regardless of how many dimensions the inputs have. If they’re vectors, it produces a vector, if they’re matrices, it produces a matrix, if they’re 3-tensors, it produces a 3-tensor, and so on. There does, however, have to be at least one output dimension since you’re concatenating at least two things vertically, so vcat of scalars does produce a vector. If we’d made [1; 2; 3] produce a 3×1 matrix then that would be arbitrarily treating 2-dimensionally arrays specially, which is a thing that Julia tries very hard to avoid and it would have to lower to something other than just plain vcat (or we’d have to make vcat treat matrices specially as well).
Another way of putting this is that this is the behavior of result = cat(dims=dim, args...) is that it produces a result with the following number of dimensions:
ndims(result) == max(dim, map(ndims, args)...)
Since [... ; ...] is syntax for vcat(...) which is just cat(dims=1, ...) that dictates that ndims([1; 2; 3]) == max(1, map(ndims, (1, 2, 3))...) == max(1, 0, 0, 0) == 1. If we special-cased [1; 2; 3] to produce a matrix then we’d ruin the above consistent rule and make the rule min(2, max(dim, map(ndims, args)...)) instead, which isn’t the worst but makes two dimensions special. It also leaves you without a syntax for vector concatenation which is itself a useful operation. So the choice here is between:
Having syntax for concatenating scalars/vectors into a vector;
Having syntax for concatenating scalars/vectors into an n×1 matrix.
It’s unclear to me that former is less useful or common than the latter.
That said, in practice I wonder if beginner and even somewhat experienced users think of the [x y ...] and [x, y, ...] syntax as concatenation, as opposed to just a vector/matrix constructor.
I am not saying it isn’t nice or consistent, but perhaps concatenation does not need its own special syntax, which could then obviate the need for the above consistency argument, and allow reusing it for “simple” cases like
I defined my own constructor to do that, I didn’t find one in base.
I don’t think using the constructor falls into the “zillion ways” to create a matrix, there should always be one available in my opinion, even though it might be verbose. Then the special syntax comes on top of that to make your life easier in some cases.