but I don't see how to get around specifying the domain interval.
It suffices to specify the rank, I think. Common Lisp array literals are of the form `#rA(...)` where r is a sequence of digits representing the rank and (...) is a (possibly) nested list of values representing the contents of the array. The storage class is always generic.
In Scheme we can write this as a procedure (array rank list) that returns a specialized array. All the lower bounds are 0, but this is easily fixed up with array-translate and is a common case anyway. We can infer the upper bounds from the sizes of the nested lists, but we cannot infer the rank reliably: (array 2 '((1 2 3) (4 5 6) (7 8 9)) is an array of numbers, whereas (array 1 '((1 2 3) (4 5 6) (7 8 9)) is an array of lists of numbers. It is an error to pass a non-rectangular list as far as the rank, but of course (array 1 '((1) (2 3) (4 5 6 7)) is fine.
My original idea was to use array-copy to convert the result to the desired storage class, but for very large arrays that would create too much GC pressure. So I think the Right Thing is to accept an optional third argument, the storage class.
Lastly, it seems to be common to use a nested-vector representation when the problem domain has a fixed rank, as in a 2D or 3D transformation matrix, and there is no need to abstract over the interval. Since there is no existing function to convert a nested vector to a nested list, it makes sense to me for `array` to accept either a list or a vector as its second argument.