Skip to content

Instantly share code, notes, and snippets.

@acominotto
Last active August 29, 2015 13:57
Show Gist options
  • Save acominotto/9506045 to your computer and use it in GitHub Desktop.
Save acominotto/9506045 to your computer and use it in GitHub Desktop.

What is Scala?


Créé par Martin en 2003 -- Son nom vient de l'anglais Scalable language Scala intègre les paradigmes de programmation orientée objet et de programmation fonctionnelle, avec un typage statique


  1. Fonctionnel basique

a) Immutability

avantages: thread-safe et referential transparancy inconvénients: pas simple à mettre en place dans un environnement où les objets contiennent des états

une fonction f(a) est dite référentiellement transparante si à tout instant t, pour un élément a donné, le résultat est le même.

b) Les fonctions sont des objets?!

Tous les éléments du scala sont des objets, même les fonctions. Cela implique que l'on peut les passer en paramètre d'une autre fonction que l'on appelera alors Higher Order Fonction (Fonction de rang supérieur).

 def profile(a: Int, f : Int => Int) = {
      val r = f(a)
      println(s"value = $r")
      r
 }
 val f = {i: Int => i + 42}
 profile(3, f)

c) Pattern Matching

sert à décomposer et récupérer des éléments à l'intérieur d'une structure de données

case class MyClass(a: Int)

MyClass(scala.util.Random.nextInt(100)) match {
 case MyClass(42) => println("Bravo, tu as trouvé la réponse universelle!")
 case MyClass(n) => println(s"$n n'est pas la réponse universelle...")
}

d) Monads

Une monade est une fonction qui encapsule un élément. Le contrat d'une monade spécifie que deux méthodes pourront être effectuées sur celle-ci: unit et bind. unit consiste à encapsuler une valeur dans une monade. bind permet d'effectuer une opération sur celle-ci.

trait Monad[T] {
  def flatMap[U](f: T => Monad[U]): Monad[U] //bind
}
def unit[T](x: T): Monad[T]

Voyez-vous ce qui pourrait être, en java, une monade? (Collections, try-catch)


e) Example Option[T]

Option possède deux sous classes: Some et None. Cette monade peut donc posséder une valeur ou aucune et a pour intérêt d'éviter les NPE. Exemple:

val foo = Some(42).flatMap(i => if(i == 42) Some(s"La réponse universelle est $i") else None)
foo match {
     case Some(v) => println(v)
     case None => println("Whoops, nothing...")
}
  1. Les traits

Les traits c'est entre les classes abstraites et les interfaces. Un trait c'est une class abstraite avec l'avec l'avantage qu'une classe peut hériter de plusieurs traits. On parlera alors de composition ou "mixin"

class Animal {
     override def toString = "I'm an animal"
}

trait Flying extends Animal {
     override def toString = s"${super.toString} and I fly"
}

trait Swimming extends Animal {
     override def toString = s"${super.toString} and I swim"
}

val swimmingBird = new Animal with Flying with Swimming
val flyingFish = new Animal with Swimming with Flying
  1. Les collections

Toute personne qui a programmé un jour ou l'autre connait la notion de collection.

Scala nous fournit sa propre implémentation qui utilise les fonctions ainsi que deux types distincts de collection: les collections mutable et immutable.

Les collections mutables sont semblables à celles de Java à ceci prêt que leurs API fournit des méthodes telles que filter, map, folds, reduce, ...

Les collections immutables fournissent les même fonctionnalités et ne sont plus modifiées après leur création, ce qui les rend thread safe. Nous allons vous décrire quelques méthodes non présentes en Java (1.7) de l'API de collection.

a) filter

Signature: List[T]# filter(f: T => Boolean): List[T]

Comme le décrit sa signature, filter va prendre une fonction qui, pour un objet de type T donné, renverra un Boolean.

Exemple:

persons.filter(p => p.age <= 25) //Renvoie toutes les personnes dont l'âge est inférieur à 25 ans

b) map

Signature List[T]# map[U](f: T => U): List[U]

map va transformer tous les éléments de la liste suivant la fonction f.

Exemple:

persons.map(p => p.name + " " + p.firstName) // renverra une liste de tous les noms et prénoms des personnes.

c) flatMap

Signature: List[T]# flatMap[U](f: T => List[U]): List[U]

flatMap va transformer chacun de ses éléments en liste puis concaténer ces listes (Héritage monadique!)

Exemple:

persons.flatMap(p => p :: friends.filter(f => f.age == p.age))

d) fold (Left or Right)

Signature: List[T]# foldLeft[U](acc: U)(f: (U, T) => U)

foldLeft va parcourir la liste de gauche à droite et effectuer un traitement à l'aide d'un accumulateur

Exemple:

persons.foldLeft((0,0)) { (acc, p) => p.sex match {
         case Female => (acc._1 + 1, acc._2)
         case Male => (acc._1, acc._2 + 1)
         case _ => acc
     }

}

e) for comprehension loop

La boucle for en scala est une manière simplifiée d'utiliser les filter, map et flatMap sur les monades.

Exemple:

for{
    p <- persons
    f <- friends if(f.age == p.age)
} yield (p, f) // renvoie la liste des personnes ainsi que leurs amis associés (ceux qui ont le même âge, le monde est beau)

revient à écrire

persons.flatMap{p =>
    friends
          .filter(_.age == p.age)
          .map(f => (p, f)) 
}
  1. Les implicits

On distingue 2 types d’implicit : Les conversions implicites : implicit def Le passage de paramètres/valeur : implicit val, implicit var // Note : Il existe aussi des objets implicites


implicit val x = 5
def inc(implicit i: Int) = i + 1

case class Person(age: Int, name: String) { def tellAge = println(s"$name >> $age")}
implicit def tupleToPerson(t: (Int, String)): Person = Person(t._1, t._2)
(28, "Alexis").tellAge

def sorted[B >: A](implicit ord: math.Ordering[B]): List[A]
object AgeOrdering extends Ordering[Person] {
     def compare(a: Person, b: Person) = a.age compare b.age
}
person.sorted
  1. Asynchrone (La promesse d'un futur)

Pourquoi en avons-nous besoin?

val socket = Socket()
val paquet = socket.readFromMemory()
socket.sendToUSA(paquet)

[Tableau -- http://norvig.com/21-days.html#Answers]

val socket = Socket()
val paquet = socket.readFromMemory() //50 000 ns
socket.sendToUSA(paquet) // 150 000 000 ns

[Tableau converti 1sec = 1 nanosec]

val socket = Socket()
val paquet = socket.readFromMemory() // 3 jours
socket.sendToUSA(paquet) // 5 ans

Nous bloquerions donc l'accès à l'application pendant plus de 5 ans !!

INTRODUCING: Les Futures.

Future[T] est une monade qui permet d'allouer un thread qui va effectuer l'action demandée. Celle-ci, à sa complétion, lancera les callbacks qui lui étaient assignées.

/!\ Pittfalls, La programmation asynchrone ne fonctionne pas comme la programmation séquentielle /!\


    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.Future
    val socket = Socket()
    val paquet = Future { socket.readFromMemory()  }// paquet est maintenant une future...
    socket.sendToUSA(paquet) // FAUX: paquet est une future !!!
    
    val futurAck = paquet.flatMap { p =>
      Future { socket.sendToUSA(p) }
    }

Aux futures s'ajoutent les Promise[T]. Une promesse est une future sur laquelle on a entièrement la main: on décide quand elle se termine et avec quelle valeur elle se termine. Dès lors, si on souhaite qu'une future ne se termine jamais (tests...), on peut utiliser:

Promise[T]().future

Si on veut que lorsque l'on recoit le paquet, on l'envoie en parallèle aux USA et en Chine et vérifier laquelle se termine la première:

import scala.concurrent._
import ExecutionContext.Implicits.global

val promise = Promise[String]()

promise.future.map(w => println(s"$w FIRST!"))

def sendToUsa(message: String) = Future {Thread.sleep(scala.util.Random.nextInt(5000)); message}.map(m => promise.success(s"USA received $m"))
def sendToChina(message: String) = Future {Thread.sleep(scala.util.Random.nextInt(5000)); message}.map(m => promise.success(s"China received $m"))


sendToUsa("Hello!"); sendToChina("Ni hao!")
@avandendaele
Copy link

Ca me va aussi de laisser de côté les monades. Car je ne l'ai pas compris du premier coup non plus.
Comme le dis Andy, ce qui est important est de savoir ce qu'on veux que les gens se rappelle après le talk.
C'est autant plus vrai que l'introduction de nouvelles techno par chez nous, c'est pas ce qu'il y a de plus rapide.
Je trouve que l'objectif devrait être que c'est fun, concis, rapide et sans devoir fait un reset de leur projets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment