Skip to content

Instantly share code, notes, and snippets.

@telekosmos
Last active February 20, 2022 10:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save telekosmos/109292f77d6a28864b350a33d25ec001 to your computer and use it in GitHub Desktop.
Save telekosmos/109292f77d6a28864b350a33d25ec001 to your computer and use it in GitHub Desktop.
Snippets on Scala type classes and implicits

FP Scala snippets

Type classes, implicits, monads, higher-kinded types.

To start with:

  • Semigroup -> combine(x: A, y: A): A associative
  • Monoid extends Semigroup -> zero: A means (combine(x, zero) == combine(zero, x) == x) (zero can be call empty, id)

And/Then:

  • Functor defines map -> map[A, B](fa: F[A])(f: A => B): F[B]
  • Applicative extends Functor with ap and pure
    • pure[A](a: A): F[A]
    • ap[A,B](ff: F[A => B])(fa: F[A]): F[B]
  • Monad extends Applicative with flatten, hence flatMap -> flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]

Monads (flatMap) give function composition for effectul functions A => F[B], B => F[C]. Obviously, the effect F has to be the same

// F[_] is a monad like Option, Try, etc, F has to be always the SAME
private def doA(F[A] input): F[B] = ???
private def doB(F[B] input): F[C] = ???
private def doC(F[C] input): F[D] = ???
def computation(input: A): F[D] = {
for {
a <- doA(input)
b <- doB(a)
c <- doC(b)
} yield c
}
import java.util.Date
import cats.{Show, Eq}
import cats.implicits._
import cats.syntax.option._
// implicit val dateShow: Show[Date] = Show.show(date => s"${date.getTime}ms since the epoch")
implicit val dateShow: Show[Date] = new Show[Date] {
def show(date: Date) = s"${date.getTime}ms since the epoch"
}
val d1 = new Date()
val res = dateShow.show(d1)
res
final case class Cat(name: String, age: Int, color: String)
val showCat: Show[Cat] = Show.show(cat => s"${cat.name} is a ${cat.age} year-old ${cat.color} cat")
val pusa = showCat.show(Cat("pusilla", 14, "gray")) // no implicit ^^ no extended method .show
val compareOption = Eq[Option[Int]]
compareOption.eqv(Some(1), None)
// Some(1) === None // not working
Option(1) =!= None
implicit val compareDates: Eq[Date] = Eq.instance[Date]{ (d1, d2) => d1.getTime == d2.getTime }
d1 === new Date() // doesn't work if no implicit ^^
implicit val compareCats: Eq[Cat] = Eq.instance[Cat] { (c1, c2) => c1.name == c2.name && c1.age == c2.age && c1.color == c2.color }
trait Semigroup[A] {
def combine(x: A, y: A): A
}
trait Monoid[A] extends Semigroup[A] {
def empty: A
}
object Monoid {
def apply[A](implicit monoid: Monoid[A]) = monoid
}
// Implicits are not really necessary here, but in a larger program are likely necessary for another computations
implicit val booleanAndMonoid: Monoid[Boolean] = new Monoid[Boolean] {
def combine(a: Boolean, b: Boolean) = a && b
def empty = true
}
// Following wouldn't work as there are too many implicits for the Monoid object
// We should use the vals:
// val myAndOp = booleanAndMonoid
/*
implicit val booleanOrMonoid: Monoid[Boolean] = new Monoid[Boolean] {
def combine(a: Boolean, b: Boolean) = a || b
def empty = false
}
*/
val myAndOp = Monoid[Boolean]
myAndOp.combine(true, false) // res0: false
trait DivisionResult
case class FiniteResult(q: Int) extends DivisionResult
case object InfiniteResult extends DivisionResult
object divide {
def apply(op1: Int, op2: Int): DivisionResult = {
op2 match {
case 0 => InfiniteResult
case _ => FiniteResult(op1/op2)
}
}
}
val res1 = divide(1, 2) // FiniteResult(0): DivisionResult
val res2 = divide(8, 0) // InfiniteResult: DivisionResult
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment