24 August 2023

About feelings. And rationality.

It's a common contradiction: feelings vs. rationality. Do we listen to our feelings? Or do we make our decisions using facts, numbers and reasoning? Perhaps we try to balance both? But is the contradiction between the two even real?

Our culture and our language seem to teach us that feelings and rationality are opposites, with phrases like "being a hot-head" vs. "keeping your head cool". I'd like to argue that this is a misconception.

Feelings are important. We can trace them back to biochemical and neurological processes in the brain, each with a distinct evolutionary origin. Fear, for example, evolved to keep us away from danger. Empathy evolved to make us help one another. They are an inherent part of us, like our arms, legs, muscles and bones. Moreover, feelings may be the only reason we do things at all: Why walk? To cross the street. Why cross the street? To get to work. Why work? To make money. Why make money? To provide for my family. Why do that? Because it makes me feel satisfied and secure. Why does it make you feel satisfied and secure? It just does. There's no why anymore. This, of course, is exactly why feelings evolved in the first place: to make us do things that benefit us!

Rational reasoning is a skill that we can use when we make decisions, analyze things, or communicate with someone. And whenever we do, we can include our feelings in that reasoning. Is it irrational to treat ourselves to something unhealthy once in a while? Is that "emotional behaviour"? Not at all, because the mental benefits of that treat may outweigh the effects of the "unhealthy" food. In other cases, rational thinking may tell us to ignore certain feelings, such as unjustified fears. If we add our feelings to our list of considerations when making a decision, then rationality can actually contribute to our mental health.

There is no contradiction between feelings and rationality. In fact, they can't even be compared in the first place! Feelings are a part of who we are and rationality is a skill. It's like comparing "an apple" to "blue". It makes no sense.

So, if feelings are not irrational, then which things are? And how do they relate to our feelings?

Principles, Morals & Ethics

Most people have principles in order to spare them the effort of thinking

- Fliegende Blätter -

Principles, morals & ethics are shortcuts that lead people to make decisions "just because". They don't need a reason, they just do it "out of principle" or because "it's the right thing to do". Principles, morals & ethics assume that different situations can all be treated the same. In reality, they rarely can. It's a better idea to rationally analyze the specifics of each situation individually, especially when we take our feelings, like empathy and sense of justice, into account. Thinking rationally about our the facts, and our feelings, leads to a better outcome than just mindlessly applying a principle or moral code, and probably makes us feel better about it too.

Beliefs
Let's say that we believe that vegetables are healthy. That could be true, or not. But does it depend on what we believe? If eating vegetables is good, then it's good, no matter what we believe. And if it's not good, then it's not good, no matter what we believe.

Whether vegetables are healthy or not, is therefore a thing of fact. It is either true, or not. Of course, it could be somewhere in between, but the truth does not depend on what goes on in our brain. It does not matter what we think. We don't change the health benefits of vegetables by believing things about them.

How about placebos? Some people seem to benefit medically from the belief that they've been given a real medicine. It is, however, not the belief itself that positively affects their body, but rather the mental process that is triggered by that belief. Mental processes can indeed affect how the body works, as Wim Hof famously demonstrated. He also showed that we don't need a false belief to do this. We can simply study the biochemical processes behind this skill and apply that knowledge. Beliefs don't turn placebos into real medicine.

So, do we really believe that vegetables are healthy? Or, do we suspect it? Suspecting is fundamentally different from believing, since suspecting means that we try to find the truth about something. Believing means that we pretend we've already found it.

Believing is admitting that one cannot deal with the uncertainty of suspecting

Now, let's look at what those words do with our feelings. When two people have opposite beliefs, they tend to clash. They tend to become defensive and, if confronted with evidence that goes against their beliefs, instead of changing them, they tend to double down on them. All the while they experience negative feelings, related to feeling attacked and dislike of people with opposite beliefs.

What if we use the word "suspect" instead? If one person suspects that vegetables are healthy and another suspects that they're unhealthy, what do they feel when confronted with one another? Exactly! The urge to find out! This urge is not only very constructive, it's also a very pleasant, motivating feeling. And even when we find out that our suspicion was wrong, it doesn't feel bad. Instead, we feel good about having learned something!

Suspecting automatically leads to understanding, whereas believing makes understanding unnecessary.

I'd rather have a mind opened by wonder than one closed by belief

- Gerry Spence -

Opinions
Strictly speaking, opinions don't exist. Only facts exist. Facts can contain probabilities (eg. "there's a 30% chance of rain") and conditions ("if it rains, then there are clouds", which does not say that it rains, or that there are clouds, yet it is still a fact). We either know a fact, or we don't know a fact. And if it's somewhere in between, we suspect a fact.

But what about things where the truth depends on what we think? For example, I may have the opinion that a certain pizza is nice, and you may disagree. In that case, what is the truth? Is the pizza nice or not? Are there two truths?

Multiple realities and truths are nice for science fiction, but in real life, there is only one reality, one truth and one set of facts. In this case, the pizza is round, the pizza is warm and the pizza is spicy. Those are facts about the pizza.

The pizza however, is not nice. It's also not bad. Instead, I find it nice and you find it bad. Those are not facts about the pizza, they're facts about you and me. It is a fact that I like it and it is a fact that you don't.

If we communicate using the words "opinion", "agree" or "disagree", then we pretend that reality depends on our words. If I say "It's my opinion that my car will pass the inspection" and you say "I disagree", then each of us pretends to be able to shape the future. Whether or not the car will pass, however, is not influenced at all by our opinions. As with beliefs, it's much better to use the word "suspect". The effects on our feelings are very similar. Having an opinion tends to make us feel defensive. Having a suspicion tends to make us feel curious and constructive.

Dogma
Principles, morals, ethics, beliefs and even opinions, are forms of dogma: things that we accept as truth, even if we aren't certain. While there is no contradiction between rationality and feelings, there is a big contradiction between rationality and dogma. If we think rationally, we let go of all dogma. That means that we have to accept uncertainties and things that we simply don't know. This is not a problem. In fact, knowing that we don't know something is useful! It's better to make a decision that takes into account uncertainties, than one that is based on false beliefs or opinions.

Another problem with dogma is that it blocks our mind from learning new things that conflict with what is already there. A rational mind, free from dogma, is therefore the most open-minded one possible.

A Rational Mindset
Imagine yourself at the edge of the jungle. Behind you is society, with its principles, morals, ethics, beliefs and opinions. But as soon as you step into the wilderness, all of those are gone. Only facts and feelings remain. Rational decision making will give you the best chance of surviving. After years, you return to human society, but you keep the jungle mindset and apply it within society. You'll be able to make better decisions, communicate more effectively and feel much better throughout. You've achieved a rational mindset. Congratulations!

Of course, it's possible without the jungle too...

Thanks for reading and if you have any feedback, let me know!

Welcome to the jungle

- Axl Rose -

12 January 2023

Reactor: even more fun in Kotlin!

In the previous article, I gave an introduction into Reactor: a Reactive Programming Java library that makes working with data easier and more fun. In this article, I will explore how Reactor behaves in that other, very popular, JVM language: Kotlin. I will also compare Reactor to Kotlin Flows & Coroutines.

Extension methods
One of the most powerful features of Kotlin are extension methods (also known as lambda-with-receiver): the ability to add methods to external classes in your own code. For example, this is a method that prepends “Hello” to a String:

Reactor uses this to add various extension methods of its own:

There are quite a few more handy extension methods available. For a complete overview, check the API Docs.

Scope functions
Scope functions are extension methods that are defined by Kotlin itself. One example is the let method, that works on any Object. It executes a function block and returns its result. This is particularly useful in Builder style patterns, where you invoke a chain of methods that all return an Object of the same type. Of course a Flux isn’t technically a Builder, because the chain of methods creates a chain of fluxes that subscribe to one another, rather than operating on a single Flux, but the programming style is very similar.

Let’s have a look at the hotel example from the previous article. There, we have a hotelService and a reservationService that return a Reactor Flux of resp. HotelRooms and Reservations. A Flux is an Object that can stream data asynchronously and exists even when the data itself isn’t there (yet). In addition, a Flux has many, many methods to manipulate the stream.

Now, let’s say that we only want to call the switchIfEmpty method if the Marriot hotels are enabled. In Java, you would have to do this:

Using the Kotlin let scope function, we can simply do:

Competition: Reactor vs Flows & Coroutines!
In addition to providing several improvements to the Reactor experience, Kotlin also comes with its own solution for Reactive Programming: Kotlin Flows & Coroutines. By adding the suspend keyword to a method, you can turn that method into an async method, that will execute in a non-blocking, async fashion when data is actually available. Here is an example, using the Spring Framework, which offers support for both Reactor as well as Kotlin Flows & Coroutines.

In Kotlin Flows & Coroutines, you would write:

This is similar to the following in Reactor:

A much more comprehensive comparison between the two can be found in this Medium article.

Which is better
As always, this depends largely on your specific needs and preferences. Speaking for myself, I prefer Reactor for the following reasons:

  • In Kotlin Flows & Coroutines, the code requires a mix between language keywords (eg. suspend) and API classes and methods (eg. Flow). This is not very elegant and slightly confusing. In Reactor, the use of the language itself (Java, Kotlin or any other JVM language) doesn’t change. Instead, the reactive patterns are fully contained in the Reactor API (eg. Flux).

  • Related to this, Reactor makes reactive programming more explicit. It’s obvious from the code that you’re working with a reactive API. Code written with Kotlin Flows & Coroutines looks very similar to blocking code. This may seem nice, but making explicit what is actually happening is very important for readability and maintenance. If programmers see code that differs from traditional, blocking code only because of the suspend keywords, they may not be aware that they are dealing with a reactive system and may make mistakes because of that. Reactor makes it obvious that you’re working with an async, non blocking API, without having to deal with callbacks.

  • Functions with the suspend keyword are only callable from other functions with the suspend keyword, or from a coroutine. This makes it more difficult to share logic with blocking code. The various methods of Flux simply take ordinary lambda’s, that can contain any logic.

  • Kotlin Flows & Coroutines is purely push-based: the async code executes when data becomes available from the source. Reactor, on the other hand, offers a push-pull hybrid: data gets pushed downstream, but subscribers can also request data from upstream and propagate backpressure signals upstream.

  • A quick comparison between the Flow API from Kotlin Flows & Coroutines and the Flux API from Reactor shows that the amount of operations that Reactor offers is many, many times larger. It doesn’t even come close. Whatever use case you have, whatever operation you need, Reactor is likely to offer it. And in the rare cases where it doesn’t, it offers plenty of hook methods to implement your own.
Conclusion
Using Reactor as a Reactive Programming platform enables you to write async, non blocking code with any JVM language. However, by using some of Kotlin’s unique and powerful language constructs, you can make the experience even better!

Thank you for reading!