rubico simplifies asynchronous code
Richard Tong

Richard Tong @richytong

About: Founder

Joined:
May 21, 2020

rubico simplifies asynchronous code

Publish Date: May 30 '20
10 12

You are suddenly dropped into a world where all people write code in assembly.

Language Hierarchy

There is no "High Level Language", only "Assembly Language". There is no C, just ASM. There are no variables, only registers. You are in charge of managing all memory in your programs: moving data from register to register, pushing and poping data on the hardware supported stack.

How would you write a webserver, or a database? How long would that take? How much longer would it take you to do whatever it is that you are currently doing?

We need not stay here any longer.

...interdimensional warp...

Welcome back to reality, where the world is rife with programming languages above assembly

How did this come to be? Why would anyone not want to spend their day to day in assembly?

According to an answer thread on stack overflow,

ASM has poor legibility and isn't really maintainable compared to higher-level languages.

[Assembly] takes more code to do the same thing as in a high-level languge, and there is a direct correlation between lines of code and bugs.

Another take from wikipedia

In contrast to low-level programming languages, [high-level programming languages] may use natural language elements, be easier to use, or may automate (or even hide entirely) significant areas of computing systems (e.g. memory management), making the process of developing a program simpler and more understandable than when using a lower-level language.

Perhaps the abundance of higher level languages comes down to readability versus performance

First code for correctness, then for clarity (the two are often connected, of course!). Finally, and only if you have real empirical evidence that you actually need to, you can look at optimizing.

IMO the obvious readable version first, until performance is measured and a faster version is required.

I would go for readability first.

Great, looks like people are for clarity, so where does that leave us? And what does any of this have to do with rubico?

Consider these two samples of JavaScript code. Both execute an asynchronous function for every element of an array

Promise.all(array.map(doAsyncThing)) // vanilla JavaScript

map(array, doAsyncThing) // rubico
Enter fullscreen mode Exit fullscreen mode

It looks like you can write a little less to do the same thing with rubico. Is the rubico version more readable? I'd say it's up for debate.

What if we wanted to do multiple asynchronous things in parallel for every item of the array?

Promise.all([
  Promise.all(array.map(doAsyncThingA)),
  Promise.all(array.map(doAsyncThingB)),
  Promise.all(array.map(doAsyncThingC)),
]) // vanilla JavaScript

map(array, all([
  doAsyncThingA,
  doAsyncThingB,
  doAsyncThingC,
])) // rubico
Enter fullscreen mode Exit fullscreen mode

It looks like vanilla JavaScript has four more Promise.all statements, and two more map keywords. rubico, on the other hand, has one map and one fork. Simpler? It's starting to look like it. More readable? Hold your horses.

What if we now want to do another async thing per item of each of the responses?

Promise.all([
  Promise.all(array.map(doAsyncThingA).then(
    arrayA => Promise.all(arrayA.map(doAsyncThingAA))
  )),
  Promise.all(array.map(doAsyncThingB).then(
    arrayB => Promise.all(arrayB.map(doAsyncThingBB))
  )),
  Promise.all(array.map(doAsyncThingC).then(
    arrayC => Promise.all(arrayC.map(doAsyncThingCC))
  )),
]) // vanilla JavaScript

map(array, all([
  pipe([doAsyncThingA, map(doAsyncThingAA)]),
  pipe([doAsyncThingB, map(doAsyncThingBB)]),
  pipe([doAsyncThingC, map(doAsyncThingCC)]),
])) // rubico
Enter fullscreen mode Exit fullscreen mode

I think it's safe to say that rubico is more expressive here. I'll let you be the judge of whether something is more readable; though I will say this: rubico cuts out a lot of cruft.

Back to assembly. You could compare what rubico does for JavaScript to what C does for assembly.

In contrast to assembly, C uses natural language elements, is easier to use, and automates (but doesn't hide entirely) memory management. C makes the process of developing a program simpler and more understandable than when using assembly.

In contrast to vanilla JavaScript, rubico automates (hides entirely) the cruft surrounding Promises. rubico makes the process of developing a program simpler and more understandable than when using vanilla JavaScript.

I should also mention that when you use rubico, you get the benefits of the functional programming paradigm (but not the confusion) for free.

If this motivated you to see what rubico's all about,
please 🌟read on🌟

Comments 12 total

  • MikeJMontgomery
    MikeJMontgomeryJul 15, 2020

    Hey thanks for putting this library together. It looks very promising. No pun intended. Can you advise on the use of schedulers? I want to use for making web animations, and schedulers seems to be the way to do it with lots of async. I know schedulers have been used for some RXJS animations, and Unity has a job scheduler in their Data-Oriented Design Tech Stack. Thanks

    • Richard Tong
      Richard TongJul 15, 2020

      Are you open to using React?

      • MikeJMontgomery
        MikeJMontgomeryJul 16, 2020

        From my research, React isn't good for animation because its diffing algorithm renders in batches. You cant precisely control time like you can with Rxjs. I found your article on Stop tryin to learn Rxjs - as I'm trying to learn it now - and resonate with your thinking. I'm aiming for a stateless reactive streaming approach, using time to synchronize everything.

        • Richard Tong
          Richard TongJul 16, 2020

          I was going to recommend React-controlled CSS. Could you describe your animations?

          • MikeJMontgomery
            MikeJMontgomeryJul 16, 2020

            I'm interested in making information visualization interfaces, kind of like what you see in sci-fi movies, like Minority Report. So this would include a combination of css animations (for elastic UI transitions) and webgl (for data visualizations, using libraries such as E-Charts). From what I can tell, using schedulers like in Rxjs would enable me to control these animations without complex state management.

            • Richard Tong
              Richard TongJul 16, 2020

              Hmm I see. Actually I hope one day we can all interact with computers like in Minority Report heheh. What do you like about schedulers currently?

              • MikeJMontgomery
                MikeJMontgomeryJul 16, 2020

                I might be wrong - again I'm learning RXJS - but schedulers seem to be what can be used to control animations on the web, where lower and higher bandwidths enable different framerates. At a high level, this is what Unity is doing for their new tech stack using the jobs scheduler, so I'm following their lead. On a lower level, with RXJS, I'm following the guidance of Ben Lesh (RXJS core team member) on doing advanced animations using RXJS schedulers. All this seems to be to move away from complex stateful OOP to an event-driven time-based synchronized computing model.

                • Richard Tong
                  Richard TongJul 16, 2020

                  If it is what works for you, I recommend you keep using it. Nevertheless, I'd be willing to support something similar in rubico, if desired. What do you mean by bandwidth?

                  • MikeJMontgomery
                    MikeJMontgomeryJul 20, 2020

                    By bandwidth Im referring to using the scheduler to adjust rendering frame rate to user device capabilities. This sort of thing is being explored in the WebXR api spec. But more broadly, it seems the use of a scheduler is needed for real-time streaming interaction. Reactive manifesto readings mentions the idea of a scheduler as central to FRP, though its pretty unexplored in the community, perhaps due to the overall barrier of jargon complexity. For more clarity on FRP, I think discrete event simulation modeling offers clarity on all the concepts. For example, all events incorporate the notion of delay, for future processing and handling in schedulers. So if you could incorporate schedulers in your library that offers a simpler approach to FRP than rxjs, I'd be interested in giving it a try.

  • Daniel Richter
    Daniel RichterJul 15, 2020

    I really liked that style of writing, although the first few sentences at first glance had nothing to do with javascript like the title promised the storytelling kept me reading.

    But how is this different from rxjs, I feel like I could acomplish the same task with the same level of readability with rxjs, which I and probably others know well from working with angular? Is it that it's truely functional and can be used with other functional libraries like lodash/fp or ramda which rxjs cannot?

    Indeed it looks promising because Promises are great and way better than callback hell but still somewhat cumbersome to work which. Recently I needed to compose sync and async functions together and ended up wrapping the sync function in Promise.resolve() and then compose them with Promise.all() which worked but looked a bit silly. How would this look like with rubico?

    • Richard Tong
      Richard TongJul 15, 2020

      Here's a table from one of my archived posts that highlight some differences of rubico and rxjs

      Features of RxJS Features of rubico
      special asynchronous stream datatype, the Observable - wraps built-in types for reactive operations down the line no special data types: rubico works out of the box for built-in types; this includes async iterables
      familiar design pattern: the Observer pattern - plugs into Observables no design pattern lock-in: rubico composes your functions together. You choose the design pattern
      interop with Promises fully managed Promises: rubico stops you from having to manually resolve Promises, for example having to call
      Promise.all on an array of Promises
      functional programming style via Operators + Observable.prototype.pipe functional programming style by design
      Roadblocks for RxJS More Features of rubico
      async iterables undermine the need for rxjs's core datatype: the Observable. async iterables make rubico great; the async iterable is a core rubico (and JavaScript) datatype
      multitude of concepts simple foundational concept: just pipe functions together
      large API surface small, cohesive API surface
      RxJS is hard to learn rubico is easy to learn

      If you had sync and async functions that you wanted to compose together in a pipe, with rubico it would like something like

      pipe([
        syncFnA,
        asyncFnA,
        syncFnB,
        asyncFnB,
      ])(someValue) // Promise { someOutput }
      

      rubico pipe and other functions that accept functions all handle promise resolution for you. Conversely,

      pipe([
        syncFnA,
        syncFnB,
      ])(someValue) // someOutput
      

      If no functions are asynchronous, you'll get a synchronous value not wrapped in a Promise

  • NOPR9D ☄️
    NOPR9D ☄️Dec 3, 2020

    interesting, i will test !

Add comment