It's easy to silently mess up Stream.flatMap calls
ceedubs opened this issue · comments
This is a fun one that I just ran into. What is the result of this watch expression?
> Stream.fromList ["foo", "bar"]
|> Stream.flatMap (s -> Stream.fromList (toCharList s))
|> Stream.toList!
If you guessed []
then you are right!
The issue is that the code should use Stream.fromList! (toCharList s)
instead of Stream.fromList (toCharList s)
.
Let's look at the signature of Stream.flatMap
:
Stream.flatMap :
(a ->{e, Stream b} any) -> '{e, Stream a} r -> '{e, Stream b} r
In the example, the function that we are passing in has the signature Char -> {} ('{Stream Char} )
. So Stream.flatMap
views this as a function that takes a Char
, doesn't emit any stream elements, and then returns an ignored value which happens to be a thunk that would emit a stream of characters should you run it instead of ignore it.
I think that a simple solution would be to make Stream.flatMap
require ()
as the return type of the mapping function:
Stream.flatMap :
(a ->{e, Stream b} ()) -> '{e, Stream a} r -> '{e, Stream b} r
This means that occasionally you might need to add an ignore
to your function, but I think that's a small price to pay to avoid silent issues that cause your code to not at all do what you would expect.
NOTE: the same applies to Stream.flatMap!
.
cc @anovstrup who has done a lot of great Stream
work.
Hah I now retract my "Sounds fine to me" that I commented here.