Your question has been bumping around in my brain a bit and I want to take another stab at it... I'm going to explain the problem with an analogy which hopefully will work for you.
Instead of a stream of products, let's assume we had an array of products, and a FancyControl built for arrays like this:
FancyControl: (inText: array string, ...other input arrays) => {
asHtml: array HTML
outText: array string
...other output arrays
}
How would you feel about a comment like this?
[edited for the sake of the analogy] Note that output arrays may be wired back into some input arrays. For instance, outText is likely to be joined with other arrays and wired back into inText.
To me, the above immediately stands out as odd, and so did your original comment. I assume the above statement about arrays also strikes you as odd, even though you thought it was only natural to ask it for streams.
But to go on... We can now make a FancyControl to handle the names in the products thusly:
nameControl = FancyControl(products.map(p => p.name, ...);
descriptionControl = FancyControl(products.map(p => p.description), ...);
htmlArrays = combine([nameControl.asHtml, descriptionControl.asHtml],
(nameHtml, descHtml) => 'tr' + ...,
);
The idea with the above is that there is one FancyControl to render all the names and one to render all the descriptions. But what would be a good way to setup the arrays to handle comments?
We could try the same trick as your attempt 1 if we know the maximum number of comments allowed:
commentFancyControls = range(0, maxComments).map(i =>
FancyControl(products.map(p => p.comments[i]))
)
With the above, we end up with an array of FancyControls where commentFancyControl[n] displays products.map(p => p.comment[n]). (Although, what does fancyControl[n] display if a particular product doesn't have a comment[n]?)
Or we could use your attempt 2 idea and "lift and flatten" the comments which will give us a 2-D ragged array of FancyControls which as you say:
[edited for the sake of the analogy] ... avoids all the disadvantages of attempt 1, but we are creating many single-[element] arrays for every comment.
Under this analogy, your general question becomes:
How can I keep the nice simplicity of arrays below the highest level of an aggregate data structure?
Then, according to the analogy, the rest of the question would explain how we are "probing the limits of", and "loosing the benefits of" arrays... but are we really, or is it merely a case of misunderstanding how to use arrays properly?
Generally, when someone posts a question here, it's because they don't understand some aspect of the technology they are asking about. In this particular case your misunderstanding is with Monads. As the analogy above shows, the question you have isn't about streams per se, rather it is about any monad; it even applies to arrays (which are also monads,) and how to handle elements that are at different levels of monadic depth.
Your FancyControl isn't at the right level of monadic depth and so it doesn't fit properly inside the monad. As a matter of fact, if you create a fancy control that handles strings instead of streams of strings, then it would be a simple matter of flatMapping your stream product into a stream array FancyControl.
Hopefully, this analogy will help clear your misunderstanding.