I think it is more explicit in the second signature, because the function takes a variable number of arguments, of which the first is always the accumulator, and the rest are the values produced by the stream, e.g.
Folds f over each element of s with i as the initial accumulator. If s is infinite, this function does not terminate. The f function takes the accumulator as its first argument and the next stream element as its second.
(stream-fold
(lambda (x y z) x)
3
(for/stream ([i (in-range 10)])
(values i (* i i))))
I might be wrong, though. Thanks for asking.
Edit:
To be clear, the dots mean "zero or more" in this case. So, "one argument, then zero or more arguments, and then a final argument."
It does have a kind of internal logic to it, I suppose, but at a minimum I would suggest that the documentation of the two functions is inconsistent, and that if there are ellipses (...) in the documentation for the function passed to stream-fold, that there should also be ellipses in the documentation for the function passed to stream-map. Also, it seems to me that the documentation for these functions should definitely make clear that streams returning multiple values will have those values spread across the mapped function.
I'd really be curious to hear what others have to say? Is there history here?
Python's map and reduce(Racket's foldl) only take one argument either, but there is no problem, because Python heavily relies on zip. Python does not rely on variadic arguments.
In other words, if you had stream-zip, you could handle any number of streams.
To be honest, you had better use SRFI-41, but if you had some reason not to use it, you should implement it yourself.
Fortunately, SRFI-41 shows a referential implementation of stream-zip; thus you do not have to think nor be confused. You can just copy and paste the code, and fix that.
Consequently you would get something like this:
Racket does not have stream-lambda, but it is also stated in the SRFI-41's referential implementation. Here it becomes a local macro, or syntax.
There is no explanation for exists in the referential implementation, but it is the stream version of SRFI-1's any. There is its referential implementation, and here it becomes a local function, the modified version of any for streams, too.
You may add this one to your personal utility library.
Anyway, I think you can now handle any number of streams with stream-zip.
I'd just use SRFI-41functions (It comes with Racket) instead of racket/stream. They're generally compatible with the built in streams, and the library has a lot more functionality.
It's exactly what @bakgatviooldoos and @jbclements demonstrated: an element in a stream could have multiple values in Racket, and these values are passed as arguments to the function provided to stream-map. If you allow multiple streams, then it risks ambiguity. E.g., the first stream might be a stream of 2 or 3 values. The second stream might be a stream of 4 or 5 values. Now, how many arguments should the function accept? One possibility is to simply concatenate argument lists together, so the answer is either 6, 7, or 8 through case-lambda, but the 7 case could come from either 2 + 5 or 3 + 4, but you would not know which one it is. Etc, etc.
If there's a fixed number of streams that you are working with, you can either use for/stream to create a stream with each input stream as a clause. If it's not fixed, then you can use @bakgatviooldoos's approach (which is exactly what I would do). Or if you don't care about multiple-valued streams at all, then you can use SRFI-41. Choices are yours.
There is nothing wrong with foldr on an infinite stream. The only caveat is that it must be lazy on the accumulator, so that the folding function can decide whether to evaluate that at all (think of Haskell fold). Racket is a strict language, but we can use mechanisms like promises to accomplish that. For example, consider the following definition (ignore multiple values, for now)
I like your example, but unless I'm missing something, it could be computed just as well using a left fold, making it unclear why you'd reach for the stream-foldr in this instance. Are there any compelling examples that are more natural with stream-foldr? My best is something a bit contrived like "return the product of all the numbers after the last zero, but give up completely if the stream contains any negative numbers".
The difference is that stream-fold (strict or not) only terminates if the stream is finite, while stream-foldr can terminate if the folding function decides to âdiscardâ the accumulator thunk. Itâs analogous to foldl' vs. foldr in Haskell: foldl' strictly reduces a finite structure to a strict result, while foldr ârebuildsâ the structure and therefore can be as lazy as the structure is. (Also, foldl' can be defined in terms of foldr, but not vice versa.)
(BTW, not at all joking when I say that you would have saved me about 10 minutes just now if your response had included a phrase semantically equivalent to "actually, you're wrong". I think you were perhaps a bit too polite?)