Functors in Functional Programming – DZone Java


1. Overview

On this tutorial, we’ll check out Functor kind class in Cats. The concept of Functor is “one thing that may be mapped over”, we’ll see what is definitely mapped and the way. In practical programming, Functors come into play when we now have varieties or values wrapped inside contexts or containers. We don’t need to know any of the implementation particulars of these contexts or containers themselves.

2. SBT Dependencies

To start out, let’s add the Cats library to our dependencies :

libraryDependencies += "org.typelevel" %% "cats-core" % "2.2.0"

Right here we’re utilizing model 2.2.0 of the Cats library.

3. What’s a Functor?

When it comes to practical programming, a Functor is a type of container that may be mapped over by a perform. It’s mainly an abstraction that enables us to jot down generic code that can be utilized for Futures, Choices, Lists, Both, or every other mappable kind.

In easy phrases, any kind that has a map perform outlined and ideally an “identification perform” is a Functor.

Customary library has no native base trait/kind to characterize this so we are able to write :

def calcBudget(orders: Record[LineItem]) = orders.map(...) 
def calcBudget(maybeOrder: Choice[LineItem]) = maybeOrder.map(...) 
def calcBudget(eventualOrder: Future[LineItem]) = eventualOrder.map(...)

However we are able to’t write it generically like this :

def calcBudget(order: Functor[LineItem]) = order.map(...)

4. The Cats Functor Sort Class

Nevertheless utilizing cats’ Functor kind class we are able to write a generic code that can be utilized for Futures, Choices, Lists, Both, or every other mappable kind.

However earlier than that, let see definition of Functor kind class. A Functor is outlined as F[A] with a map operation (A => B) => F[B] :

bundle cats trait Functor[F[_]]  
  def map[A, B](fa: F[A])(f: A => B): F[B] 

Now let’s rewrite calcBudget utilizing Functor :

import cats.Functor case class LineItem(worth: Double) def calcBudget[F[_]](order: F[LineItem])(implicit functorEvidence: Functor[F]): F[LineItem] =  
  Functor[F].map(order)(o => o.copy(worth = o.worth * 1.2))

Let’s decode this technique signature.

The calcBudget technique is parameterized primarily based on a sort of F[_]. Right here F[_] means any mappable kind e.g. Choice, Record, Future, and so forth. The parameter order itself is of kind F[LineItem] i.e. any kind wrapping a LineItem.

The implicit parameter functorEvidence: Functor[F] signifies that we should have a kind class implementation, which permits us to deal with F as a Functor.

In technique physique, we name Functor[F].map() i.e. we create a Functor for F utilizing implicit proof functorEvidence and name it’s map technique.

5. Functor Legal guidelines

If we’re creating our personal Functors, then these Functors need to respect some guidelines, known as Functor’s Legal guidelines :

5.1. Id Regulation

When a Functor is mapped over with identification perform (the perform returning its parameter unchanged), then we should get again the unique Functor (the container and its content material stay unchanged).

Functor[X].map(x => identification(x)) == Functor[X]

5.2. Composition Regulation

When a Functor mapped over the composition of two capabilities, then it ought to be identical as mapping over one perform after the opposite one.

Functor[X].map(f).map(g) == Functor[X].map(x => g(f(x))

6. Examples of Functors in Scala

In Scala, we’re conscious of Functor’s map perform to work with results. Cats present varied kind class implementations for Functors, for predefined varieties like Lists, Futures, Choices, Both, and so forth.

6.1. Record as a Functor

Now, let’s see how Record act as a Functor :

Record is taken into account as a Functor because it has a map technique. Whereas iterating over Record utilizing its map technique, we must always consider it as remodeling the entire values inside in a single go, with out altering the construction of the Record.

We are able to implement the above idea utilizing following instance :

import cats.Functor object ListFunctor  
  def transformList(checklist: Record[Int]): Record[Int] =  
    Functor[List].map(checklist)(_ * 2) 
   
 val checklist: Record[Int] = Record(1, 2, 3, 4, 5) 
val transformedList = Record(2, 4, 6, 8, 10) 
assert(ListFunctor.transformList(checklist) == transformedList)

6.2. Choice as a Functor

Now, let’s see how Choice act as a Functor :

Choice can also be thought of as a Functor because it additionally has a map technique. Once we map over an Choice, we rework the contents however depart the Some or None context unchanged.

We are able to implement the above idea utilizing following instance :

import cats.Functor object OptionFunctor  
  def transformOption(choice: Choice[Int]): Choice[String] =  
    Functor[Option].map(choice)(_.toString) 
   
 val choice: Choice[Int] = Some(10) 
val transformedOption = Some("10")
assert(OptionFunctor.transformOption(choice) == transformedOption)

6.3. Both as a Functor

Now, let’s see how Both act as a Functor :

Both can also be thought of as a Functor because it additionally has a map technique. Once we map over an Both, we rework the contents however depart the Left or Proper context unchanged.

We are able to implement the above idea utilizing following instance :

import cats.Functor object EitherFunctor  
  def transformEither(both: Both[Int, String]): Both[Int, Int] =  
    Functor[Either].left.map(both)(_.measurement) 
   
 val both: Both[Int, String] = Left("Baeldung") 
val transformedEither = Left(8)
assert(EitherFunctor.transformedEither(both) == transformedEither)

6.4. Future as a Functor

Now, let’s see how Future act as a Functor :

Future is a Functor that sequences asynchronous computations by queueing them and making use of them as their predecessors full. The kind signature of its map technique has the identical form because the signatures above. Nevertheless, the habits may be very totally different. In case of Future, the wrapped computation could also be ongoing, full, or rejected. If the Future is full, our mapping perform may be known as instantly. If not, some underlying thread pool queues the perform name and comes again to it later. We don’t know when our capabilities might be known as, however we do know what order they are going to be known as in. On this means, Future gives the identical sequencing habits seen in Record, Choice, and Both.

We are able to implement the above idea utilizing following instance :

import cats.Functor object FutureFunctor  
  def transformFuture(future: Future[Int]): Future[Int] =  
    Functor[Future].map(future)(_ + 1) 
   
 val future: Future[Int] = Future10 
val transformedFutureResult = 11
FutureFunctor.transformFuture(future).map(consequence => assert(consequence == transformedFutureResult))

7. Significance of Functors

By utilizing Functors we’re not restricted to the categories in the usual library, thus we are able to summary over something that’s mappable. We may outline a map technique to our personal varieties through the use of the idea of syntax or extension strategies. This idea permits us to jot down order.map(…) as a substitute of Functor[F].map(order)(…). We are able to additionally drop the implicit proof parameter by specifying that kind F[_] is a Functor :

import cats.syntax.functor._ //for map def calcBudget[F[_]: Functor](order: F[LineItem]): F[LineItem] = 
  order.map(o => o.copy(worth = o.worth * 1.2)) 

However we are able to additionally construct a higher-order perform that may take care of any kind and carry out any mapping operation :

def withFunctor[A, B, F[_]](merchandise: F[A], op: A => B)(implicit functorEvidence: Functor[F]): F[_] = Functor[F].map(merchandise)(op) val lineItemsList = Record(LineItem(10.0), LineItem(20.0)) 
val consequence = FunctorSyntax.withFunctor(lineItemsList, calcBudget) 
assert(consequence == Record(LineItem(10.0), LineItem(20.0)))

In above instance, A, B & F may be something as long as the caller of the tactic :

  • Provides proof that F is a Functor
  • Is aware of how you can map from A to B

8. Abstract

On this article, we’ve checked out Functors offered by Cats library of Scala. Functor is a type of container that gives mapping perform to characterize sequencing behaviors. We talked about what’s the want of Functors and the way can we outline them. Then we discovered about two legal guidelines {that a} Functor has to respect. Then we noticed that we are able to write Functor kind class implementation for all of the mappable varieties current in Scala ecosystem like Futures, Choices, Lists, Both, and so forth. We’re not restricted to the categories in the usual library. We are able to additionally outline map technique to our personal varieties through the use of the idea of syntax or extension strategies.

All of the code implementations can be found over on GitHub.


Supply hyperlink

About PARTH SHAH

Check Also

Galaxy Unpacked August 2021: Official Trailer

Change is the one fixed on the earth of innovation. By driving new concepts ahead …

Leave a Reply

Your email address will not be published. Required fields are marked *

x