julia icon indicating copy to clipboard operation
julia copied to clipboard

RFC: curried `reshape`?

Open mcabbott opened this issue 2 years ago • 10 comments

Sometimes you want to pipe something to a reshape which isn't vec. This makes a Fix2 which allows:

julia> 'a':'z' |> reshape(2, :)
2×13 reshape(::StepRange{Char, Int64}, 2, 13) with eltype Char:
 'a'  'c'  'e'  'g'  'i'  'k'  'm'  'o'  'q'  's'  'u'  'w'  'y'
 'b'  'd'  'f'  'h'  'j'  'l'  'n'  'p'  'r'  't'  'v'  'x'  'z'

julia> stack(reshape(2,3), eachrow(rand(7,6))) |> size
(2, 3, 7)

I think this should be safe since there are no methods to reshape a number.

(What it cannot do is change what reshape(Base.OneTo(4), Base.OneTo(2), Base.OneTo(2)) means, so it can't easily extend to OffsetArrays.)

This is quite different from the many curried predicate functions like isequal(2), but is more like filter(iseven) (from #41173) or getproperty(:x) (#46855) perhaps. Not too sure what the policy is.

mcabbott avatar Jan 03 '23 23:01 mcabbott

Maybe accepting a Tuple instead of splatted integers would support passing flexible args for offsets?

Tokazama avatar Jan 12 '23 16:01 Tokazama

That is true, it could let x |> reshape(axes(y)) work so long as you don't splat axes(y)....

mcabbott avatar Jan 12 '23 16:01 mcabbott

Pre-1.11 bump? I see https://github.com/JuliaLang/julia/pull/46855 may happen at last...

(Needs tests obviously.)

mcabbott avatar Feb 13 '24 15:02 mcabbott

The one trouble is that OffsetArrays adds reshape methods that use ranges (axes) to specify the resulting shape. That use-case becomes ambiguous here.

mbauman avatar Feb 13 '24 15:02 mbauman

Yes. I suppose some possible views are:

  1. OffsetArrays just aren't supported by this convenience feature, write x -> reshape(x, 2:3, :) yourself. (Present state of the PR)
  2. Change to always require a tuple, reshape((2, :)). That's less convenient.
  3. Change to allow but not require a tuple, reshape(2, :) but reshape((2:3, :)). More complex but perhaps since 99% of people never touch OffsetArrays, most need never think about this.

mcabbott avatar Feb 13 '24 15:02 mcabbott

any of

reshape(a, axes(b))
reshape(a, axes(b)...)
reshape(b, size(b))
reshape(b, size(b)...)

all work right now and are equal, I think? would feel a little inconsistent if the curried version only supports one of these paradigms.

adienes avatar Feb 13 '24 16:02 adienes

Yes those are all equivalent. What the curried version cannot do is support the second one, axes(b)....

mcabbott avatar Feb 13 '24 16:02 mcabbott

I see. why not add a dispatch for

reshape(axes::AbstractUnitRange...)

?

adienes avatar Feb 13 '24 16:02 adienes

Because that already means something -- example from first post is:

julia> reshape(Base.OneTo(4), Base.OneTo(2), Base.OneTo(2))
2×2 reshape(::Base.OneTo{Int64}, 2, 2) with eltype Int64:
 1  3
 2  4

mcabbott avatar Feb 13 '24 17:02 mcabbott

ah I see, sorry for missing that at first

hmm I think your option 2. strikes me as best Change to always require a tuple, reshape((2, :))

that way there's no difference in using size vs axes, and it eliminates the possibility of strange behavior if someone tries to do something like make a subtype <:Integer that supports reshaping

but don't have super strong feelings on the matter, just noting

adienes avatar Feb 13 '24 17:02 adienes