Skip Navigation

Re-frame Effects VS Coeffects

Published: 2019-08-13

#clojure

When I first started using re-frame, I was a bit confused about what effects and coeffects are, or, what fxs and cofxs are. They both seemed to relate to the “Side Effects” (or “Effects”) functional programmers dislike. It was hard for me to distinguish them.

Before you read on, if you’ve never came across re-frame’s official documentation or the cljdoc page, please start there. They are fantastic resources. However, if you find yourself need some more examples or direct comparisons to help you understand them, please read on.

Two types of side effects

side effects everywhere meme

Out of the box, re-frame comes with the mechanisms to update the application state. When your application needs to cause a side effect on the application state, you only need to write reg-event-db, to register your event handlers. The side effects that operate on the application state is well-managed. There’s very little confusion about them, so this article will not focus on those side effects.

The two types of side effects we are focusing on are the effects and the coeffects, or fxs and the cofxs. These two types represent things that are not a part of the application state, such as:

  1. Database
  2. LocalStore
  3. Third-Party APIs
  4. (Other external resources that are not deterministic)

The differences are:

Examples of fx

  1. Push data to a database
  2. Push data to localStore/cookie
  3. Push data to a third party service
  4. Push message to js/alert
  5. Push a function to JavaScript event queue, e.g. js/setInterval, js/setTimeout, js/Promise

Example of cofx

  1. Pull data from a database
  2. Pull data from localStore/cookie
  3. Pull data from a third party service
  4. Pull current datetime from browser
  5. Pull a random number

What about js/Promises that fetch data?

This situation is a bit mind-boggling at first. A more concrete example is when doing interop with a JavaScript library in an event handler to fetch data, and it returns a promise immediately. Thinking sequentially, what I wanted is to wait until the data fetched, then run the event handler. However, promises don’t work that way.

The solution is to think of this as a push side effect, or fx. For example:

dispatch [:fetch-data] -> do-fx [:promise-fetch-data] -> dispatch [:data-fetched %]

This ends up with two event handlers, one effect handler, and no coeffect handler. I doubt this is the best way because I wish I can just have one :fetch-data coeffect handler.

Summary

  1. Effects push,
  2. Coeffects pull,
  3. (Please let me know if you have a better way to handle javascript promises.)

This work is licensed under a Creative Commons Attribution 4.0 International License.