DEV Community

Discussion on: Introduction to Fluture - A Functional Alternative to Promises

Collapse
 
avaq profile image
Aldwin Vlasblom • Edited

A Future normally represents a computation (that leads to an eventual value). When using cache, you get back a value that pretends to be a Future, but actually behaves a little bit more like a Promise, and can be thought of as representing the eventual value of some computation (the input Future). The fact that the value in this Future "does not change" (meaning its the same value being given to multiple consumers) makes it so the Future is not referentially transparent. That's because now it suddenly matters where in your code that Future has been created to determine its behavour.

For example, let's say you wrap the creation of the cached Future in a thunk. Calling that thunk twice to produce two distinct instances of that Future, and consuming each of them will now behave differently from calling the thunk once and consuming the resulting Future twice, because the underlying computation will run differing amounts of times. Were the Future not cached, the underlying computation would have run every time, independent of the execution context in which the Future was created.

Collapse
 
waugh profile image
waugh

What is meant by a "computation"? What is the significance and use of a representation of a computation?

Thread Thread
 
avaq profile image
Aldwin Vlasblom • Edited

I'm using the word computation to make a distinction between the eventual value, and the composition of functions computing it. So with "computation" I'm referring to the "source function" (running the I/O or other side effect), and all functions composed with it through map, chain, etc. When we get really technical though, this sort of language doesn't really hold up. It's really just to create a conceptual distinction.

A Future abstracts over that composition of functions by design, and although a Promise also has access to its underlying function, Promises are modeled after "Deferreds", which have no access to the underlying "computation", and this makes their design different. This difference is most significant when it comes to caching of the value, exactly.

A Deferred is just a mediator for (or, like, a placeholder of) an eventual value. It is passed to multiple consumers with the promise that at some point, the producer of this mediator will pass it a value, and then the mediator will inform its consumers of this change in state. Deferred are inherently stateful for this reason: They must change their internal state from having no value (yet), to having a value. It is this statefulness that removes their referential transparency: it's now the reference to the exact mediator that makes a difference in your code; Calling the same producer function multiple times will leave you with multiple references, and because reference determines behaviour, that makes the producer function impure.

A (regular, uncached) Future on the other hand has no internal state. There is no difference between multiple consumers of one Future, versus multiple Futures produced by one producer:

const producer = magnitude => Future ((rej, res) => {
  const value = Math.random () * magnitude
  res (value)
  return () => {}
});

const consumer = Future.value (console.log)

// The following program logs two random numbers:
consumer (producer (123))
consumer (producer (123))

// The following program also logs two random numbers:
const rand123 = producer (123)
consumer (rand123)
consumer (rand123)
Enter fullscreen mode Exit fullscreen mode

In the above example, you can see that I was able to replace occurrences of producer (123) with an equivalent constant without affecting the behaviour of the program. If you were to try this same thing with Promises:

const producer = magnitude => new Promise ((res) => {
  const value = Math.random () * magnitude
  res (value)
});

const consumer = p => p.then (console.log)
Enter fullscreen mode Exit fullscreen mode

You'll see that the behaviour of the two equivalent programs now differ: The first program logs two random numbers, but the second program logs the same random number twice.