
Streams – A Real-Life Approach - todsacerdoti
https://isank.dev/posts/java-streams-real-life-approach/
======
JadeNB
The usage of "1 to 1" in "`map` is a 1 to 1 operation … meaning one input
produces one output" is at odds with the usual mathematical usage of the term;
I'm not sure how it accords with CS usage. In fact, it seems to be referring
to the `f` in `map f`, not to `map` itself. In mathematics, we say that an `f`
that produces one output for one input is a function. (More generally one
might consider non-function objects `f` called relations, often written infix
as `x f y`; for example, we could have the relation `f = lt`, for which, say,
both `1 lt 2` and `1 lt 3` hold—one input with two (or more) outputs.) If we
say further of a function (or more generally of a relation) that it is one to
one, we mean that _different_ outputs produce _different_ outputs.

For example, properly speaking, one should not speak of the square-root
function, since there are _two_ real square roots of every positive real
number. (What we usually call the square-root function is really the _non-
negative_ square-root function on non-negative reals.) There is a perfectly
good squaring function, since every real number has a unique square, but it's
not 1 to 1 on the domain of all real numbers, because different real numbers
can have the same square.

~~~
mrkeen
No, it refers to map.

Take a stream and count its elements:

    
    
      System.out.println(
        Stream.of(1,2,3).count());
    

Now map a function onto that stream, and count its elements.

    
    
      System.out.println(
        Stream.of(1,2,3).map(f).count());
    

For every 1 element counted in the first example, you get 1 element counted in
the second example - regardless of f.

~~~
JadeNB
Certainly the author is correct in the sense of "1 to 1" that just means
"function" (`map` is a function, in the sense that `map f = map g` whenever `f
= g`); and even in the sense of "1 to 1" that means "injective" (if `map f =
map g`, then `f = g`, at least extensionally), so there's no formal sense in
which the author is wrong; but I still think that the sense in which the
author is right is not the intended sense.

Namely, consider, to take a silly example, a function like `f x = [x, x^2 +
3]`. (I'll use Haskell notation throughout, since I'm comfortable with it.) In
the formal sense this is still a function, since it produces one output, which
happens to be a length-2 list, for each input; but one may also informally
regard it as a one-to-two relation, in the sense that `x` is left-related to
`x` but also to `x^2 + 3`.

OK, so _still_ it is true that `length $ map f [1, 2, 3] == 3`; but there's
also a sense in which `map f [1, 2, 3]`, which equals `[[1, 4], [2, 7], [3,
12]]`, _wants_ to have length 6. This is so in the sense that `concatMap f [1,
2, 3]`, which equals `[1, 4, 2, 7, 3, 12]`, _does_ have length 6.

Thus, although, again, there is no technical sense in which it is wrong to say
what the author does, nonetheless, in the informal and intuitive sense, _I_
think it is better to say that the "1-to-1-ness" (which I would prefer to call
"function-ness") of `map f` is a property of `f`, or at least of how we choose
to use `f`, not of `map` itself. But, again, I emphasise that _I_ think this;
though I have made a case for it, a big chunk of that case is personal
preference, and so cannot, I think, be regarded as conclusively right or
conclusively wrong—although I think it makes it easier to communicate with
mathematicians, for whatever that's worth.

------
mrkeen
> That's exactly what Java thinks and thus it chose to keep the Streams lazy.

Rumours of Stream's laziness have been greatly exaggerated.

    
    
      Stream<Integer> aStream =
        Stream.iterate(1, i -> i + 1)
              .flatMap(i -> Stream.iterate(i, j -> j + 1)
                                  .flatMap(j -> Stream.iterate(j, k -> k + 1)));
    
      // Does not terminate
      Integer firstElement = aStream.findFirst().get();

