Kuan

October 18, 2022

I saw a Monad in Dart Stream

I knew I had read it, briefly, in one or two of the articles about functional programming before. Or if I type something about "Functional Programming", "Reactive X", and "Dart Stream" into DuckDuckGo, the first page will be full of them.

It is when I wrote these lines of code...

Rx.combineLatestList(
  [_siteInfoController.stream, _productInfoController.stream])
        .flatMap((List<String?> p) {

      if (p.first == null) {
        return Stream.value(_backToCheckAddress());
      } else if (p.last == null) {
        return Stream.value(_backToSiteProducts(p.first!));
      } else {
        final siteInfo = getIt<TaskManager>()
            .runTask(name: 'getting site', task: getSiteInfo(p.first!));
        final productInfo = getIt<TaskManager>()
            .runTask(name: 'getting product', task: getProductInfo(p.last!));
        
          return Rx.zipList([siteInfo.asStream(), productInfo.asStream()])
            .map<Component>((p) {
          return _siteAndProductInfo(p);
        });
      }
    });

Then I realized I am dealing with Monad, by writing the `.flatMap`. Instead of ending up with a `Stream<Stream<Component>>`, I can retain the whole chain of functions and get back a simpler `Stream<Component>`. 

It is beautiful. I am so glad I did not get lost in all this chaining. This is functional programming and I am back at home now. Functional programming in Dart and Flutter!

What do these lines do?

  1. I combine two streams. One may carry the site id, another may carry the product id.
  2. Whenever these two streams have at least one value, I will start getting events.
  3. For each event, I checked how much information I got. Only site id? Only product id? or Both.
  4. For each combination, I will do different things. The first two will display a redirection, for example.
  5. If I got both ids, then I will start another 2 Streams, asynchronous operations that retrieve the product and site information.
  6. I zip the two Streams and send the event into a Component building function.
  7. Because the latter combo needs to return a Stream for the asynchronous operation, I need to return Streams for all three combos.
  8. If I use `map` instead of `flatMap` in line 3, I would end up with a Stream<Stream<Component>>. Instead, I have a Stream<Component> to work with now.

Monad Chaining and FP. Yay!

About Kuan

Web developer building with Flutter, Svelte and JavaScript. Recently fell in love with functional programming.

Malaysian. Proud Sabahan. Ex game developer but still like playing games.

New found hobby is outdoor camping with my love.