A word on ‘bind’

Gautier DI FOLCO
Linagora Engineering
3 min readNov 4, 2019

A good practice in software development is to separe concerns. Business code should be focused on business-specific problems and technical code on more generic issues.

It is expected that business code deals with generic containers (generally Collection s). These containers are holding business-valuable values/objects having dedicated operations.

There are two main access patterns on the container:

* Those which preserve the context

Using a map here will not change the number of elements

* Those which modify the context

Using a flatMap will change the number of elements

The rise of bind

bindis the name given to the Haskell’s operator commonly associated with flatMap in mainstream programming languages.

It is the common way to express the composition of two context-modifying operations.

To go further, every context-modifying operations are a specialization of bind.

Let’s take filter as an example, it can be rewritten as following:

It is possible to extract a flatten operation:

Why Stream does not have a bind

In order to understand how bind differs from flatMap, the difference between map and fmaphas to be highlighted

fmap is part of an abstraction called Functor in Haskell. It should follows a set of laws (an expected behavior). The composition law, defined as:

There are two reasons why this rule is important:

  • It allows to iteratively create a complex transformations with a simple composition mechanism
  • It lets the compiler rewrite the expression in order to reduce the Functor traversals

Java, on another hand, is imperative and Stream has kept this paradigm applying the operations one after the other.

The same goes with bind, it can be defined as:

a way to compose operations acting and depending on a context

It cannot be applied to an imperative-oriented API since everything is already executed.

The importance of bind for Haskell

Haskell is declarative, contrary to Java which is imperative.

Consequentially, instead of dealing with statements, which are sequentially executed, Haskell deals with expressions to be evaluated.

It is an opportunity and a drawback:

  • The evaluation is not constrainted to one moment, to an order, to a moment, and it is possible to even not evaluate useless expression
  • Since there is not order, expressing a sequentiality is a concern

Let’s take the following tuple:

(1.0 / 0.0, length "Hello")

In an imperative code, it would generate an error, while it is perfectly accceptable in Haskell, as long as you do not need to evaluate the first element. If only the second element has to be evaluated, this tuple can live a long and peaceful life.

There are times when the sequentiality is needed. For example, you may want to represent a chess game. Each move depends on the current board state.

That’s where bind get in the game, you want to moves your pieces according to this state. In this case, this simplest solution is to use a container disposing of bind and holding the chess board state in one part. And to keep the computation of the next move to a dedicated function in another part.

Lastly, this concern appears in a very important part of a program: inputs and outputs. As stated earlier, evaluation is only triggered when needed. The main reason a need exists is to do input/output operations. When a side effect have to be performed, a value making sense is requested, and the evaluation has to take place in order to get it.

Moreover, side effects inherently require the sequentiality. bind is useful again here. You have a version of the world (for example, the file system), and, after applying some modification (reading or writing a file), you will get a new version of this world. To force the evaluation order of these operation (for example, reading before writing), you need a sequentiality constraint, which is provided by bind.

References

--

--