Free Monads

Recapitulación

Functor

Un funtor es un tipo F[A] con una operación map con el tipo (A => B) => F[B]

Funtor

Un funtor es un tipo F[A] con una operación map con el tipo (A => B) => F[B]

A            F

Funtor

Un funtor es un tipo F[A] con una operación map con el tipo (A => B) => F[B]

A            F

map

Funtor

Un funtor es un tipo F[A] con una operación map con el tipo (A => B) => F[B]

A            F

map

val o = Option(1)
//o: Option[Int] = Some(1)

val f = ( i: Int ) => i + 1
//f: Int => Int

o.map( f )
//Option[Int] = Some(2)

Mónada

Una mónada es un mecanismo de control para secuenciar cálculos

Mónada

Una mónada es un mecanismo de control para secuenciar cálculos

Una operación pure con tipo A => F[A]

Una operación flatMap con tipo (F[A], A => F[B]) => F[B]

Mónada

Una mónada es un mecanismo de control para secuenciar cálculos

import cats.Monad
import cats.instances.option._

Monad[Option].pure(1)
//Option[Int] = Some(1)

Mónada

Una mónada es un mecanismo de control para secuenciar cálculos

val o = Some(1)
val f = ( i: Int ) => Some( i + 1 )
//f: Int => Some[Int]

o.flatMap( f )
//Option[Int] = Some(2)

Mónada

Una mónada es un mecanismo de control para secuenciar cálculos

val o1 = Some(1)
val o2 = Some(2)

val r = for {
  x <- o1
  y <- o2
} yield x + y
//Option[Int] = Some(3)

Transformación Natural

Transforma los valores de un tipo de primer orden, en otro tipo de primer orden

F[_], G[_]

Transformación Natural

Transforma los valores de un tipo de primer orden, en otro tipo de primer orden

F[_], G[_]

import cats.~>


val optionToList = new (Option ~> List) {

  override def apply[A](fa: Option[A]): List[A] =
    fa.toList

}

optionToList( Option(1) )
//List[Int] = List(1)

DSL

DSL es un lenguaje específico del dominio. Contiene funciones que se refieren al dominio.

Free Monads

No fijar nuestras composiciones monádicas a un solo efecto sino como,
por el contrario, dejar el efecto libre y definirlo solo hasta el momento de la ejecución.

Separación entre la definición del programa (es decir, la lógica de negocio) y su implementación

Separación entre la definición del programa (es decir, la lógica de negocio) y su implementación

for {
    n <- getUserName( id )
    p <- getUserPhoto( id )
} yield User( n, p )
def getUserName(userId: Long): UserName = ???
def getUserPhoto(userId: Long): Image = ???
for {
    n <- getUserName( id )
    p <- getUserPhoto( id )
} yield User( n, p )
def getUserName(userId: Long): UserName = ???
def getUserPhoto(userId: Long): Image = ???
for {
    n <- getUserName( id )
    p <- getUserPhoto( id )
} yield User( n, p )
import scala.language.higherKinds

trait Monad[F[_]] {
  def pure[A](value: A): F[A]

  def flatMap[A, B](value: F[A])(func: A => F[B]): F[B]
}
def getUserName(userId: Long): UserName
case class GetUserName(userId: Long) extends Service[UserName]

Comenzamos creando un DSL

type UserName = String
type Image = String


sealed trait Service[A]

case class GetUserName(userId: Long) extends Service[UserName]
case class GetUserPhoto(userId: Long) extends Service[Image]
// Eleva un F[A] en la mónada libre.

import cats.free.Free.liftF

def getUserName(userId: Long): ServiceF[UserName] = liftF( GetUserName( userId ) )
def getUserPhoto(userId: Long): ServiceF[Image] = liftF( GetUserPhoto( userId ) )
import cats.free.Free

type ServiceF[A] =  Free[Service, A]
def getUser(id: Long) = for {
    n <- getUserName( id )
    p <- getUserPhoto( id )
} yield User( n, p )
// Un intérprete requiere una mónada como parte final de la transformación
// ~> natural transformation

import cats.{Id, ~>}

object ServicePrinter extends (Service ~> Id) {
  def apply[A](fa: Service[A]): Id[A] = fa match {
  
    case GetUserName(userId) =>
      println("GetUserName...")
      "Mauricio"
    
    case GetUserPhoto(userId) =>
      println("GetUserPhoto...")
      "=)"
      
  }
}

Nuestro primer intérprete

GetUserName...
GetUserPhoto...
val res: Id[User] = getUser( 1 ).foldMap( ServicePrinter )
//res: Id[User] = User(Mauricio,=))
import cats.{Id, ~>}

object ServiceUser extends (Service ~> Id) {
  def apply[A](fa: Service[A]): Id[A] = fa match {

    case GetUserName(userId) =>

      "Mauricio"

    case GetUserPhoto(userId) =>
      
      "=)"

  }
}

Otro intérprete

Preguntas

FreeMonad

By maocq

FreeMonad

  • 203