Home

Works × Notes × LinkedIn × StackOverflow × Github

Functionnal Programming Notes (Scala)

Strucutres algébriques

graph TB Ma --> DeGr DeGr --> Mo Mo --> Gr Mo --> MoCo Ma[Magma: Ensemble doté d'une loi de
composition interne *] DeGr[Demi-groupe: Magma dont * est associative] Mo[Monoide: Demi-groupe doté d'un
élément neutre pour *] MoCo[Monoide Commutatif: Monoide
dont * est commutative] Gr[Groupe: Monoide admettant un
élément symétrique pour *
pour chacun de ses éléments]

\(G=Monoide(*,e)\) est un groupe \(\Leftrightarrow \forall x\in G,\exists x^{-1}\in G,x*x^{-1}=e\)

Cats

Semigroup

Cats’ semigroup implem (import cats.Semigroup):

trait Semigroup[A] {
  def combine(x: A, y: A): A
}

Monoid

Cats’ semigroup implem (import cats.Monoid):

trait Monoid[A] extends Semigroup[A] {
  def empty: A
}

Functor

Abstraction over a type constructor F[_] (can be List[Int], Map[Int, String], Double) providing ability to map over it.

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

Note: The _ used is just a convention: Functor[F[_]] is the same as Functor[F[T]] but as you do not use T in the Functor, it is clearer to name it in this anonymous fashion. But, this will compile thanks to _:

class C[F[_]](f: F[_])

but this won’t (not found: type T):

class C[F[T]](f: F[T])

To name you’re force to add it as type parameter too. This compiles:

class C[T, F[T]](f: F[T])

Monads Examples

Option[A]

Alternative to OOP’s null. Two subtypes:

Implicits

Implicit conversions

This

((i: Int) => i)("1")

gives

type mismatch;
 found   : String("1")
 required: Int

but this compiles just fine:

implicit def StringToInt(s: String) = Integer.parseInt(s)
((i: Int) => i)("1")

Use Case: Decorator Pattern with implicit class

Suppose we want to be able to call .show() on a DeltaTable (optionally refer to see delta.io) that has not such a method.

Two approaches in OOP:

  1. Try to achieve this with a classic Decorator pattern:
    class ShowableDeltaTable(deltaTable: DeltaTable) extends DeltaTable{  
      def show() = {  
     deltaTable.toDF.show()  
      }  
      // And many method delegating to `this.deltaTable` the calls to DeltaTable's behaviors.
      override def [...] = this.deltaTable.[...]
      override def [...] = this.deltaTable.[...]
      override def [...] = this.deltaTable.[...]
    }
    
  1. Simple inheritance:
    class ShowableDeltaTable(deltaTable: DeltaTable) extends DeltaTable{  
      def show() = {  
     this.toDF.show()  
      }  
      // And many method delegating to `this.deltaTable` the calls to DeltaTable's methods that return a DeltaTable.
      def as(alias: String): DeltaTable = new ShowableDeltaTable(df.as(alias), deltaLog)
    }
    
    • With this solution the methods returning a DeltaTable like def as(alias: String): DeltaTable are problematic in that they make us lose our ShowableDeltaTable type.

Actually both Decorator and inheritance does not compile since DeltaTable’s constructor is private.

So here come the saver that address all the previous issues in term of readability:

implicit class ShowableDeltaTable(deltaTable: DeltaTable){  
  def show() = deltaTable.toDF.show()  // Delegation pattern
}  

that make any instance of DeltaTable showable:

deltaTable.show()  
deltaTable.as("alias").show()