5 Anti Patterns to Avoid When Writing Code in a Functional Programming Language | by Edward Huang | Apr, 2022

Use case examples with Scala

Picture by Priscilla Du Preez on Unsplash

Practical programming languages have gained a lot traction in these previous few years.

Many individuals can see the advantage of writing code that accommodates options equivalent to capabilities as a first-class citizen. They embrace immutability in a concurrent surroundings, working heavy computed duties with out worrying about some concurrency challenge, and love to jot down generic code to be as DRY as attainable.

I noticed this as a very good signal that useful programming language is changing into mainstream once more. Nonetheless, one of many onerous components of writing code in a useful programming language is its design sample and anti-pattern, that are completely different from a daily programming language.

I typically see engineers writing in a big codebase that I categorized them anti-pattern. I had additionally dedicated to those anti-patterns once I initially wrote a production-ready software in a useful programming language. Since then, I’ve learn many books about useful programming design patterns and books which have helped me create a extra maintainable code.

An nameless operate might be good for code-reusability. Nonetheless, an excessive amount of nameless operate can harm the eyes of these engineers who need to lengthen the performance. Though DRY is the best way to go, typically duplication is healthier than the mistaken abstraction.

I’ve touched codebase the place engineers will write a closely concise and summary methodology. The code is one thing like this:

Are you able to inform me what the definition of buildRunner is?

Then, this buildRunner is utilized in all motion operations like authorize, seize, and void within the fee processor. I have a look at it for 2 days to lastly perceive what it’s making an attempt to do.

This creates an summary to be as DRY as attainable on all of the capabilities you write. Nonetheless, having a nested nameless callback might be robust for normal engineers to construct a brand new characteristic or keep. A lot of the engineers will want a few days to grasp what buildRunner is making an attempt to do.

The advantage of useful programming is that you could have a look at the operate signature and know what it’s making an attempt to do instantly. Nonetheless, this operate doesn’t clarify a lot about what it does. It additional confuses engineers making an attempt to make a change within the codebase.

Due to this fact, a very good rule of thumb is to not use an nameless operate is feasible. As a substitute, use a higher-order operate.

If you wish to use an nameless operate, please put a sort on the highest to make it simpler to learn. http4s does this internally by wrapping its type-ins Kleisli. Kleisli in itself is an nameless operate that’s A => F[B]. Nonetheless, wrapping the nameless operate with a sort definition on the highest helps readability in your codebase.

The very first thing that we study the advantage of writing code in useful programming is the pattern-matching characteristic — it eliminates the ugly if-else assertion that we frequently use within the frequent programming language.

Sample matching is sweet in case you solely have a shortcode based mostly. Issues grow to be extra like a callback hell when you’ve greater than two layers of pattern matching.

Usually, engineers who’re new to writing code with useful programming language don’t know what number of built-in higher-order capabilities the language gives. Due to this fact, they default their operate implementation by means of sample matching and recursive operate.

Having nested case expression and recursive implementation in your operate implementation causes onerous to learn and perceive code. It takes a very long time in PR feedback, and more durable to search out bugs if there’s one within the implementation.

One resolution to writing nested pattern-matching case statements is to solely care concerning the success case and depart the error situation exterior the operate implementation. Furthermore, use the built-in higher-order operate offered by the library or language, map and flatMap if attainable. It provides higher ergonomics to your codebase, and you’ll determine instantly the place that error is being dealt with.

The fantastic thing about expressing your sorts within the operate definition is that your operate implementation not must deal with all error case situations — the kind system can propagate these to the caller — implementing the operate up the stack to deal with these errors.

Utilizing Monad Transformer when encountering the nested impact could be very helpful. Within the above situation, a Monad Transformer is one other resolution to resolve the closely nested impact — it helps make your API composable. Nonetheless, we should always not expose Monad Transformer onto the interface as a result of it makes our API tight to a particular Monad Transformer.

Let’s make a extra concrete instance. The beneath interface might be Future[Either[Throwable, String]] as an alternative of EitherT[Future, Throwable, String].

All of the operate that desires to make use of someFunction as an API may even want to make use of EitherT.

What if it’s a collection of capabilities, and we see some operate returns an OptionT?

We might want to name worth a few occasions to return to our impact Future, pointless wrapping.

Alternatively, we should always make someFunction returns a Future[Either[Throwable, String]] and let the impact detects what constraint you have to in your program.

In conclusion, having the purest type of impact is healthier than a monad transformer, because it doesn’t lock providers that use the API to make use of a monad transformer.

Many APIs could return a single boolean worth to point one logic or the opposite. The basic instance, taken from Practical Fp in Scala, is the filter operate.

What does’ filter’ really do in case you have a look at the operate definition?

If the Predicate evaluates to true, it’s going to discard the weather within the listing. Then again, it could actually additionally imply if the Predicate evaluates to true, it’s going to hold the weather within the listing.

That’s ambiguous.

There may be additionally filterNot in Scala, which has the identical operate definition however has a special identify. I typically noticed many bugs happen inside these two capabilities as a result of engineers oversee the distinction between the 2.

We will enhance this by wrapping an ADT (Algebraic Knowledge Sort) across the Predicate with significant values.

This ADT helps us create a extra particular operate signature like this:

Whoever makes use of this operate will perceive in the event that they need to both hold the factor or discard them.

To resolve this downside on the filter class, you may at all times create an extension methodology, filterBy from the scala Listing trait.

The important thing to avoiding confusion in boolean values is to make use of ADT to provide significant values and lengthen these capabilities with the ADT. However, this causes extra boilerplate, however you get much less confusion and bugs round setting up your software.

Wrapping all boolean returned API with an ADT could also be overkill. Therefore, you may wrap the boolean returned API with an ADT within the crucial part and be versatile in the remainder of your software. It’s a matter of settlement along with your crew members.

This assertion could also be controversial as, in common software program engineering follow, an interface must be as generic as attainable to be extendable. It sounds nice in idea however not in follow.

One instance is Seq – a generic illustration outlined within the Scala commonplace library. It is rather generic that Listing, Vector, and Stream all lengthen from Seq. This can be a downside as a result of every of those information constructions behaves in another way.

For instance, we’ve a trait that returns a Future[Seq[String]]:

Some engineers will name the operate fetchAll and convert the Seq right into a Listing with the toList operate.

How have you learnt that it’s secure to name toList? The interpreter could outline the Seq as a Stream, and on this case, it’s going to have a special semantic, and it might throw an exception on the caller’s aspect.

Due to this fact, to lower the variety of surprises on the caller’s aspect, it’s best to outline a extra particular sort, equivalent to Listing, Vector, Stream, relying on the appliance’s objective and efficiency.

The issue with these anti-patterns is that it’s not an anti-pattern in a generic programming language.

As an illustration, we’re taught that writing abstraction is sweet, and it retains your codebase DRY. Nonetheless, the overly nested nameless callback operate might be onerous to learn. The easiest way to resolve this downside is to duplicate the code to extend readability. API that returns a boolean worth is probably not an issue, and it exists in loads of the API designs and purposes. However, implementing an API that returns a boolean doesn’t give any readability when it comes to what these boolean imply. Moreover, human minds typically oversee small particulars within the documentation that may trigger bugs within the implementation.

Sample matching is a robust characteristic within the useful programming language, however it’s overly generic. If yow will discover a greater higher-order operate to implement the operate, you must use that as an alternative.

An excessively generic information construction can improve ambiguity in using the API. Due to this fact, it’s best to create a extra particular sort and make the operate declaration as clear as attainable to the decision aspect.

I hope you keep away from this anti-pattern when writing code in a useful programming language. Do you suppose these are anti-patterns? What’s one other anti-pattern you may consider when writing code in a useful programming language? Please remark them down beneath so I can even be taught from them!

More Posts