def pure[F[_], A](x: => A): F[A]Qui suis-je ? (vaste question)
@ugobourdon
Mon boulot actuellement
Ce que j'aime faire
Scala n'est pas un langage fonctionnel
Scala est un langage purement OBJET
    
        class ScalaIsAwesome(z: Option[String]) {
        
            val x: String = "functional programming is great !"
        
            var y: Int = 42
            def toString = s"$x / $y / ${z.getOrElse("not here !")}"
        }
        
        
        case class Toto(a: String, b: Double = 0D) 
        
        
        object Singleton {
            def g = "Le 'compilo' Scala assure que je suis un singleton"
            def get = new ScalaIsAwesome(Option("yo !"))
            val sayHelloFunc: String => String = x => x + " !"
        }
        
        
        trait MyTrait {
            def f = Toto("As trait I can have implementation")
        }Scala s'appui sur ce paradigme pour proposer une API "fonctionnelle"
    
        () => A <=> Function0[+A]
    
    
        A => B  <=> Function1[-A,+B]
    
        ...Oh que si !
object Main {
    // Une fonction est une valeur comme une autre
    val f: Int => String = x => x.toString
    // Elle peut donc être passée en paramètre d'une autre fonction
    def func1(f: Int => String, y: Int): String = f(y)
    // Et être retournée par une fonction
    def func2(devise: String): Double => String = x => x.toString + " " + devise
    // Ou les deux
    def func3(f: Int => String, y: Int): String => String = f(y) + " joueurs"
}
object WhatIsMyJob {
    def decompose(pb: ComplexProblem): List[LessComplexProblem]
    def findSolution(pb: LessComplexProblem): Solution
    def compose(actions: List[LessComplexProblem => Solution]): Application
    
    def main(complexProblem: ComplexProblem) {
        decompose(complexProblem) map { findSolution } andThen compose
    }
}Strong type vs weak type
Static type vs Dynamic type
The good choice !
______________________
MaClasse
val param1: String
val param2: Double
...
-------------------------------
def func1: String = ...
...
case class MyData(param1: String, 
                  param2: Double, 
                  ...)
object DomainFunc {
    def func1: String = ...
    ...
}Objet vs Fonctionnel
Les données d'un côté
Les fonctions de l'autre
Comment représenter les données ?
Comment composer les fonctions entre-elles ?
    case class Contact(
        
        firstName: String,
        middleInitial: String,
        lastName: String,
        
        
        emailAddress: String,
        emailIsVerified: Boolean
    )valeur optionnelle ?
des contraintes ?
des champs liés ?
une logique métier ?
doit être annulé si email change ?
data ProductType = AField * AnotherField * ...
data SumType = AType | AnotherType | ...
data AlgebraicDataType = AField * AnotherField |
                         MyField |
                         Constant 
                         ...
    data UnoCard = NumericCard { value: NumericValue, color: Color } |
                   KickBackCard { color: Color } |
                   PlusTwoCard { color: Color } |
                   ChangeColorCard |
                   PlusFourCard
    
    data NumericValue = Zero | One | Two | Three | Four | Five | 
                        Six | Seven | Eight | Nine
    
    data Color = Red | Blue | Green | Yellow
    sealed trait UnoCard
    
    case class NumericCard(value: NumericValue, color: Color) extends UnoCard
    case class KickBackCard(color: Color) extends UnoCard
    case class PlusTwoCard(color: Color) extends UnoCard
    case object ChangeColorCard extends UnoCard
    case object PlusFourCard extends UnoCard
    
    sealed trait NumericValue
    case object Zero extends NumericValue
    case object One extends NumericValue
    case object Two extends NumericValue
    case object Three extends NumericValue
    case object Four extends NumericValue
    case object Five extends NumericValue
    case object Six extends NumericValue
    case object Seven extends NumericValue
    case object Eight extends NumericValue
    case object Nine extends NumericValue
    
    sealed trait Color
    case object Red extends Color
    case object Blue extends Color
    case object Green extends Color
    case object Yellow extends Color
    def color(card: UnoCard): Option[Color] = ???
    def color(card: UnoCard): Option[Color] = card match {
        case NumericCard(_, color) => Some(color)
        case KickBackCard(color)   => Some(color)
        case PlusTwoCard(color)    => Some(color)
        case ChangeColorCard       => None
        case PlusFourCard          => None
    }Comment faire pour atteindre le champ "color" ?
Surtout que toutes les cartes n'ont pas de couleur
Utiliser le pattern matching
    case class Contact(
        
        firstName: String,
        middleInitial: String,
        lastName: String,
        
        
        emailAddress: String,
        emailIsVerified: Boolean
    )valeur optionnelle ?
des contraintes ?
des champs liés ?
une logique métier ?
doit être annulé si email change ?
    case class PersonalName(
        firstName: String_50, 
        middleInitial: Option[String_1], 
        lastName: String_50)
    sealed trait EmailAddress
    case class VerifiedEmail(email: Email) extends EmailAddress
    case class UnverifiedEmail(email: Email) extends EmailAddress
    case class Contact(
        name: PersonalName,
        emailAddress: EmailAddress
    )
    object String_50 {
        def apply(rawValue: String): NotValidValue \/ String_50 = ???
    }    
    sealed trait Option[A]
    
    case class Some[A](x: A) extends Option[A]
    case object None extends Option[Nothing]    
    def filterValueMinus10(i: Int): Option[Int] = 
        if(i < 10) Some(i)
        else None    
    sealed trait \/[+A, +B]
    
    case class -\/[+A](a: A) extends (A \/ Nothing)
    case class \/-[+B](b: B) extends (Nothing \/ B)    
    def forbidValueMinus10(i: Int): IllegalArgumentException \/ Int = 
        if(i < 10) new IllegalArgumentException(s"value is minus than 10 [$i]").left
        else i.right    
    def println(x: String): IO[Unit] = ??? // On verra après pour plus de détailLa théorie des catégories étudie les structures mathématiques et les relations qu'elles entretiennent.
En gros
Bon donc une fois que j'aurais passé ma thèse je pourrais commencer la programmation fonctionnelle ?
    
    def f: Int => List[String]
    def g: String => String
    val result: List[String] = f(2).map { s => g(s) }
    val result: List[String] = f(2) map(g)Composer une valeur et un context avec la fonction map
    trait Functor[F[_]] {
        def map[A,B](f: A => B)(fa: F[A]): F[B]
    }
    // Tous les Functor doivent suivrent 2 lois
    map (id) = id
    map (f compose g) = map f compose map g    
    trait Exemple[A] {
        def f: () => Option[A]
        def g[B]: A => Option[B]
    }
    val result: Option[B] = f().flatMap { a => g(a) }
    val result: Option[B] = f() flatMap(g)Composer une valeur et une succession de contexte avec la fonction flatMap
    trait Monad[M[_]] {
        def point[A](a: => A): M[A]
        def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B]
    }
    // Toutes les Monades doivent suivre 3 lois
    avec a: A, ma: M[A], f: A => m[B] et g : B => M[C]; 
    flatMap(ma)(point)          <=> ma                                  // right identity
    
    flatMap(point(a))(f)        <=> f(a)                                // left identity
    flatMap( flatMap(ma)(f))(g) <=> flatMap(ma)(x => flatMap(f(x))(g))  // associativity    
    trait IO[A] {
        def effect(x: => A): IO[A]
        def unsafePerformIO(io: IO[A]): A
    }   Utiliser le concept de lazy evaluation pour différer l'application de l'effet de bord.
Décrire l'effet de bord par un type.
Appliquer les effets à la fin du programme.
Décrire ce que le programme doit faire
Déclencher l'éxécution de ce programme à un autre moment (à la fin du programme)
    
        println("vive")
        println("la ")
        println("programmation fonctionnelle !")
    def println(x: String): IO[Unit]    
    println("vive")
        .flatMap { _ => println("la ") }
        .flatMap { _ => println("programmation fonctionnelle !") }Pour utiliser le type IO, il nous faut une Monade !
    def println(x: String): IO[Unit]    
    object Main {
    
        def main(id: String): Unit = {
            val result = for {
                _    <- println("démarre le programme")
                user <- findUser(id)
                _    <- println(s"l'utilisateur se nomme ${user.name}"
            } yield ()
            result.unsafePerformIO()
        }
    }Notre programme est constitué d'un ensemble de type qui décrivent des effets.
Il faut pouvoir les composer entre eux.
Les catégories (monades, foncteurs, ...) sont des abstractions utiles qui nous permettent de faire cela avec élégances.
Nous nous retrouvons alors à décrire l’exécution d'un programme, comme une suite de composition de fonction pure.
Par définition, ce programme est composable avec un autre programme de même type.
Nous pouvons donc raisonnablement (avec raison) recomposer notre décomposition qui nous permet de résoudre notre problème métier.
    def sum(xs: List[Int]): Int = xs.foldLeft(0)(_+_)    // notation spéciale dédicace ^^Et pour des Double ?
    def sum(xs: List[Double]): Double = xs.foldLeft(0D) { (x,y) => x + y }Les String ?
    def sum(xs: List[String]): String = xs.foldLeft("") { _ + _ }    
    // Même les listes !!!
    def sum[A](xs: List[List[A]]): List[A] = xs.foldLeft(Nil) { _ ++ _ }
    def sum[A](xs: List[A]): A = xs.foldLeft(0)(_+_) Utiliser un type paramétré (générique)
mais A n'a pas de méthode (+) ni de zéro
    def sum[A](xs: List[A])(implicit m: Monoid[A]): A = xs.foldLeft(m.zero)(m.plus) 
    trait Monoid[M] {
	def zero: M
	def plus(x: M, y: M): M
    }
    def sum[A](xs: List[A])(implicit m: Monoid[A]): A = xs.foldLeft(m.zero)(m.plus) 
    trait Monoid[M] {
	def zero: M
	def plus(x: M, y: M): M
    }
    import Monoid.IntMonoid
    
    val ints = 1 :: 2 :: 3 :: 4 :: Nil
    sum(ints) shouldBe 10 
    object Monoid {
	implicit object IntMonoid extends Monoid[Int] {
            def zero: Int = 0
            def plus(x: Int, y: Int) = x + y
        }
    }Double, String, List, Price, ...
    // Cette fonction multiplie par deux
    def f(x: Int): Int = ???    
    // Cette fonction multiplie par deux
    def f(x: Int): Int = x + 9    
    def addNine(x: Int): Int = ???    
    def addNine(x: Int): Int = x * 2    
    def f[A](x: A): A = ???    
    def f[A](x: A): A = x    
    def f[A](x: A): A = xMais il faut généraliser pour pouvoir restreindre les choix d'implémentation
    def compose[A,B,C](g: B => C, f: A => B): A => C = (x: A) => g(f(x))
    def compose[A,B,C](g: B => C, f: A => B): A => C = g compose f
    def compose[A,B,C](g: B => C, f: A => B): A => C = f andThen g 
    def reverse[A](xs: List[A]): List[A]Que peut on dire ?
Tous les éléments retournés par reverse se trouvent dans la liste d'entrée
    def reverse[A](xs: List[A]): List[A]
    ∀x. reverse(reverse(x)) == x
    ∀x. ∀y. reverse(x ++ y) == reverse(y) ++  reverse(x)cf scalacheck, quickcheck, etc...