diff --git a/blog/2024/05/14/laminar-v17.0.0.html b/blog/2024/05/14/laminar-v17.0.0.html index 131df13..de0eefb 100644 --- a/blog/2024/05/14/laminar-v17.0.0.html +++ b/blog/2024/05/14/laminar-v17.0.0.html @@ -143,8 +143,18 @@

FRP glitches, and defeats Airstream's painstakingly fine-tuned Transaction mechanism that prevents such glitches. To be super clear, using flatMap does not cause glitches on its own. It can only cause glitches when it's used unnecessarily, and even then, only under certain conditions. When flatMap is used by true necessity, the observable graph is pretty much always structured in such a way that a glitch can't possibly happen (simplifying a bit here, but it really does work like that. Airstream docs about transactions, topological rank, and loopy vs flowy operators explain all that in more detail).

Unfortunately, with flatMap being such a common operation on many data types, developers tend to reach for it before they learn about why it's a bad idea in Airstream, and many never read the entirety of the documentation – which does explain the undesirable characteristics of flatMap in great detail. And so, they end up using flatMap in a way that is unnecessary, and can thus cause FRP glitches.

Most of the problem with flatMap is its very inviting / innocuous name, as well as Scala's for-comprehensions using it invisibly under the hood, resulting in developers using it on autopilot. And so, to improve the user experience, especially for non-experts, the method called flatMap on Observables is now renamed into several variants, such as flatMapSwitch, flatMapMerge, and flatMapCustom. It is thus no longer available via for-comprehensions.

+

Of the new operators, flatMapSwitch is the "standard" one that matches the default behaviour of flatMap.

Similar changes apply to the flatten operator, of course.

See the rewritten Flattening Observables section of Airstream docs.

+
+

UPDATE: I would like to emphasize that using flatMap (now flatMapSwitch) to get async events is perfectly fine. To put it simply, the concept of glitches basically does not apply to observables that emit their events asynchronously. So you can safely use flatMapSwitch to get e.g. network responses:

+
+
// This is fine.
+val userS: Signal[User] = ???
+val responseS: EventStream[Response] = userS.flatMapSwitch { user =>
+  FetchStream.get(s"/user/${user.id}")
+}
+

Migration: