On Sat, Jan 24, 2026 at 3:23 PM Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Thank you for the exhaustive reply. I wanted to address two things right away. First, let me clarify: I find the capability to initialize arrays (or assign to them) in this way very useful, and in fact essential for this library.
Thank you, I also think that it is fundamental to the library. In fact, I sometimes believe that I unearthed a very powerful and overdue array language in C++. And that is why I want to make it work. Thank you for helping me in this endeavour.
My objection is only to using an overloaded operator for the purpose. Consider an alternative choice, where the user has to write:
``` multi::array<int, 2> const A = restriction([](auto i, auto j) { return 10*i + j; }. multi::extensions_t<2>(3, 2)); ```
I find it superior: if a new program maintainer sees it for the first time, it is easier to grasp what is going on. It is easier to grep for a name. The name already sells the intent. It doesn't have to be spelled "lazy". My point is: it should be a function.
If you are a CTAD believer, the CTAD rules of the `multi::restriction<D, Fun>` type have you covered. ``` multi::array<int, 2> const A = [](auto i, auto j) { return 10*i + j; }) ^ multi::extensions_t<2>(3, 2); multi::array<int, 2> const A = multi::restriction(multi::extensions_t<2>(3, 2), [](auto i, auto j) { return 10*i + j; }); ``` I think the logic of the order is to be consistent with the construction of other (non-lazy) arrays (sizes and then a source of data, a buffer or an allocator in the other cases) Your objection seems against providing (or encouraging?) operator^. In any case, I will add all this to the reference section. Second, about this function's contract. It is conceivable that someone will
eventually want to use it like this. I need to populate my const array from a file that I have previously written to. It may occur to me to use a restriction for this purpose:
``` multi::array<int, 2> const A = restriction( [f = std::ofstream("my_array.dat")](auto...) { int i; f >> i; return i; }. multi::extensions_t<2>(3, 2) ); ``` Based on what you explained above it is not an intended use, and it is asking for trouble. But it happens to work, and this may convince me that everything is fine. Because you call it a "lazy constant array", you want operator r[1, 2] to return the same value every time. In order for that to work, the function provided needs to be regular/pure. The function being pure is a *precondition* to your "restrict" operation.
Yes, you are going in the right direction, although one has to be pragmatic too, requiring functions to be absolutely pure limits them too much. They have to be pure in the context of their lifetime. I would say 'deterministic' is a better name. For example, I don't have objections to this (just an example of a crude way to transpose a matrix) ``` multi::array<int, 2> B({2, 3}); B = ... multi::array<int, 2> const A = multi::restriction( multi::extensions_t<2>(3, 2), [&B](auto i, auto j) { return B[j][i]; } ); ``` Although the function is not technically pure (no?). The biggest problem with your example is not that it is read from an external source or subject to the external environment, but that the fundamental reason is that the result depends on the order of evaluation. The library and the underlying algorithm are free to choose the order in which to evaluate the lazy array elements, or even evaluate them in parallel! It doesn't mean you can check it with an if-statements.
I understand that. I think the precondition is that the function is "deterministic (in its scope)". If you have a better name or if I should choose something better, please let me know. It doesn't mean it is a crash or bad memory access when this happens.
I understand this also. But it means that you cannot guarantee the behavior of a constant array
when the function is not pure. The function being pure *is a precondition*. And the documentation has to say it.
Yes, I will document this. The only mention of this is here: "Element initialization is delegated to the library here. Initialization can use optimal hardware- or library-specific operations (e.g., on a GPU) and *may occur in any order*." I think 'pure' is too restrictive, unless it just means deterministic in the context it is used. (Correct me if I am wrong.) Thanks, Alfredo