De Java ou Js vers Scala

Plus permissif que le Js et plus robuste que le Java

Vocabulaire

  • Typage fixe/dynamique
  • Typage fort/faible
  • Inférence de type

Typage des langages

Faible

Fort

Fixe

Dynamique

Scala

Java 7

Java 8

C#

C++

Ocaml

JS

TypeScript

PHP

Étape 1

Les variables et constantes

Java

Étape 1

Les variables et les constantes

Scala

// Base
String myString = "My String";

// immutable
final String myString = "My String";
// Traduction littérale de java
var myString: String = "My String";

// On (développeur et compilateur) sait que
// c'est une String
var myString = "My String";

// On sait que l'instruction se termine
var myString = "My String"

// immutable
val myString = "My String"

JavaScript

Étape 1

Les variables et les constantes

Scala

// Base
var myString = "My String";
let myString = "My String";

// immutable
const myString = "My String";
// Traduction littérale de java
var myString: String = "My String";

// On (développeur et compilateur) sait que
// c'est une String
var myString = "My String";

// On sait que l'instruction se termine
var myString = "My String"

// immutable
val myString = "My String"

L'intérêt du typage

// Java
int n = document.querySelector("input[type=number]").getValue();
n += 1; // n = 2

// JavaScript
var n = document.querySelector("input[type=number]").getValue();
n += 1; // n = "11" ou 2 ?

// Scala
// inférence de type: Int
var n = document.querySelector("input[type=number]").getValue();
n += 1; // n = 2

L'intérêt du typage

Quelle est la distance de sécurité en voiture ?

L'intérêt du typage

Quelle est la distance de sécurité en voiture ?

Java : La distance de sécurité, qui est je le rappelle en secondes, est de 2 secondes.

JavaScript : La distance de sécurité est 2.

Scala : La distance de sécurité est 2 secondes.

Étape 2

Les collections

Java

Étape 2

Les collections – Déclaration

Scala

// Array
final String[] list = new String[]{ "a", "b" };

// List - Java 7
final List<String> list = new List<String>();
list.add("a");
list.add("b");

final List<String> list = Arrays.asList("a", "b");

// List - Java 8
final List<String> list = new List<>();
list.add("a");
list.add("b");

final List<String> list = Arrays.asList("a", "b");
// Array
val array = Array("a", "b")

// List
val list = Seq("a", "b")

JavaScript

Étape 2

Les collections – Déclaration

Scala

const list = ["a", "b"];
// Array
val array = Array("a", "b")

// List
val list = Seq("a", "b")

Java

Étape 2

Les collections – transformation et filtres

Scala

final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

// java 7
final List<Integer> filteredList = new ArrayList<>();
for (Integer n : list) {
    if (n % 2 == 0) {
        filteredList.add(n);
    }
}
final List<Integer> finalList = new ArrayList<>();
for (Integer n : filteredList) {
    finalList.add(n * 2);
}

// java 8
final List<Integer> filteredList = list
  .stream()
  .filter(n -> n % 2 == 0)
  .map(n -> n * 2)
  .collect(Collectors.toList());
val list = Seq(1, 2, 3, 4, 5)
val finalList = list.filter(_ % 2 === 0).map(_ * 2)

JavaScript

Étape 2

Les collections – transformation et filtres

Scala

const list = [1, 2, 3, 4, 5];

// ES5
const filteredList = [];
for (var i = 0, c = list.length ; i < c ; i++) {
  if (list[i] % 2 === 0) {
    filteredList.push(list[i]);
  }
}
const finalList = [];
for (var i = 0, c = filteredList.length ; i < c ; i++) {
  finalList.push(list[i] * 2);
}

// ES6
const finalList = list
  .filter((n) => n % 2 === 0)
  .map((n) => n * 2);
val list = Seq(1, 2, 3, 4, 5)
val finalList = list.filter(_ % 2 === 0).map(_ * 2)

Étape 3

Les options

Étape 3

Comment :

  • Supprimer les NullPointerException ?
  • Forcer le développeur à considérer les cas où la donnée n'existe pas ?
  • Éviter les conditions imbriquées ?

 

Solution :

  • Déclarer la donnée comme optionnelle !
  • Existe en scala, java 8, C# (approximatif)

Étape 3

val userOpt = db.find(id)
val rolesOpt = userOpt
  .filter(user => user.hasWantedCondition) // It respect a condition
  .map(user => user.roles) // We need its roles only
  .map(roles => roles.mkString(",")) // We concat roles into a string

// This code
if (rolesOpt.isDefined) {
  println(rolesOpt.get)
} else {
  println("No role!")
}
// Is the same as
rolesOpt match {
  case Some(roles) => println(roles)
  case None => println("No role!")
}

Étape 3

Expérience personnelle

J'ai déjà obtenu des NullPointerException en Scala :

  • Lorsque j'ai utilisé des bibliothèques Java
  • Lorsque j'ai affecté un null à une variable (crash test)
  • Jamais dans aucun autre cas !

Étape 4

Either

Étape 4

Cas d'étude

Ma fonction renvoie une valeur ou une erreur, comment en informer le développeur ?

Réponses

Java : Type de l'erreur en retour et exception en erreur

JS : Il testera et verra bien

Scala : Le retour est un type composé des deux cas

Étape 4

Le type Either

  • Objet pouvant accepter deux types différents, mais n'ayant qu'une seule valeur (Cf. code)
  • Propose des méthodes pour traiter les deux cas
  • Utilisé majoritairement pour traiter les cas d'erreur et remplacer les exceptions

Étape 4

def createUser(user: User): Either[String, User] = {
  if (validUser(user)) {
    val persistedUser = persist(user)
    Right(persistedUser)
  } else {
    Left("There is an error!")
  }
}

createUser(user)
  .fold(
    (error) => println(error),
    (createdUser) => println("The user ${createdUser.name} was created!")
  )

Par convention, nous plaçons à gauche (Left) les erreurs, à droite (Right) les valeurs.

There is a value is you are Right, otherwise what is Left is an error

Étape 5

Les singletons

Java

Étape 5

JavaScript

class Singleton {
  private static Singleton instance;

  public static synchronized Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }

  private Singleton(){}
}
const Singleton = {};

// But we always can duplicate the object…
object Singleton {}

Scala

Étape 6

Les traits

Étape 6

Les traits peuvent être considérés comme des interfaces en Java dont certaines méthodes sont déclarées (comme Java 8)

Il n'y a pas d'équivalent en JS

// From https://github.com/scala/scala/blob/v2.11.8/src/library/scala/math/Ordered.scala
trait Ordered[A] extends Any with java.lang.Comparable[A] {
  def compare(that: A): Int

  def <  (that: A): Boolean = (this compare that) <  0

  def >  (that: A): Boolean = (this compare that) >  0

  def <= (that: A): Boolean = (this compare that) <= 0

  def >= (that: A): Boolean = (this compare that) >= 0

  def compareTo(that: A): Int = compare(that)
}

Étape 6

On peut facilement réaliser des mixins (~= héritage multiple)

trait TA {
  def a = "a"
}

trait TB {
  def b = "b"
}

class C extends TA with TB {
  def ab = a + b // "ab"
}

Étape 6

Design pattern : Le cake pattern (notion avancée)

trait Services extends ServiceAComponent with ServiceBComponent

trait ServicesImpl extends Services with ServiceAImplComponent with ServiceBImplComponent
trait ServiceAComponent {
  def serviceA: ServiceA
  trait ServiceA {
    def opa(): Unit
    def opb(): Unit
  }
}

trait ServiceAImplComponent
  extends ServiceAComponent { self: Services =>

  override val serviceA = new ServiceAImpl()
  class ServiceAImpl extends ServiceA {
    override def opa(): Unit = println("a")
    override def opb(): Unit = serviceB.opb()
  }
}
trait ServiceBComponent {
  def serviceB: ServiceB
  trait ServiceB {
    def opa(): Unit
    def opb(): Unit
  }
}

trait ServiceBImplComponent
  extends ServiceBComponent { self: Services =>
  
  override val serviceB = new ServiceBImpl()
  class ServiceBImpl extends ServiceB {
    override def opa(): Unit = serviceA.opa()
    override def opb(): Unit = println("b")
  }
}
object Boot extends App with ServicesImpl {
  serviceA.opa()
  serviceA.opb()
  serviceB.opa()
  serviceB.opb()
}
$ java [...] Boot
> a
> b
> a
> b
> Process finished with exit code 0

Étape 7

Immutabilité

Étape 7

  • Un objet immutable est un objet qui ne peut pas être modifié
  • Évite qu'une fonction tierce ne modifie nos données
  • Meilleure maintenance

Mutable

Étape 7

Immutable

let subscriber = {
  _listeners: [],

  add: function(l) {
    this._listeners.push(l); },

  fire: function(event) {
    this._listeners.forEach((l) => l(event)); }
};

subscriber.add((event) => {
  event.x += 10;
  // other process
});

subscriber.add((event) => {
  if (event.x < 15) {
    // Do not pass because x changed before
  }
});

subscriber.fire({name: "click", x: 10, y: 10});
const subscriber = {
  _listeners: [],

  add: function(l) {
    this._listeners.push(l); },

  fire: function(event) {
    this._listeners.forEach((l) => l(event)); }
};

subscriber.add((event) => {
  // fail or ignore, object is frozen!
  event.x += 10;
});

subscriber.add((event) => {
  if (event.x < 15) {
    // Pass because x cannot be changed
  }
});

subscriber.fire(Object.freeze({
  name: "click", x: 10, y: 10
}));

Étape 8

Case class

Étape 8

Cas d'étude

Je souhaite créer une classe représentant ma table « User » en base de données.

Java

Étape 8

public class User {
    // Our properties are private behind an armored security door!
    private String name;

    private String email;

    // Then we give the key to access and modify our data to every bad guys.
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

JavaScript

Étape 8

// ES5

// What is a model ?

// ES6

class User {
  // Nothing

  // or Java-like getters and setters

  get name() { return this._name; }

  set name(name) { this._name = name; }

  get email() { return this._email; }

  set email(email) { this._email = email; }
}

Scala

Étape 8

// Translation from Java and JavaScript, so mutable:
class User1(
  var name: String,
  var email: String
)

// Good scala code, so immutable:
case class User2(name: String, email: String)

// Usage

val user1 = new User1("Einstein", "ein@ste.in")
user1.name = "Albert"

val user2 = User2("Einstein", "ein@ste.in")
val user22 = user2.copy(name = "Albert")

Étape 9

Pattern matching

Étape 9

val strOpt: Option[String] = ???

strOpt match {
  case Some(str) => println(str)
  case None => println("Nothing!")
}
sealed trait Action
case object Click extends Action
case class Keyboard(key: String) extends Action

val action: Action = ???
action match {
  case Click => println("You clicked on me!")
  case Keyboard(key) => println(s"You write '${key}'")
}

Étape 9

Autre usage

Le pattern matching peut également permettre de « retyper » une variable dont on ignore son type.

Exemple

Le framework Akka utilise des messages non typés (type Any) pour communiquer entre les acteurs. Pour réaliser la bonne opération, il faut retrouver l'action demandée et donc le type lié.

Conclusion

Scala est-il le langage à utiliser ?

Avantages

  • Beaucoup d'erreurs corrigées au compile-time (réduction temps développement)
  • Moyenne de 3 fois moins de code qu'en java
  • De nombreux design patterns
  • Toutes les bibliothèques Java utilisables
  • un des langages les plus appréciés et payés
  • Langage extensible (notion de DSL)

Inconvénients

  • Langage extensible… à tel point que le développeur non accompagné est rebuté
  • Beaucoup de notions à comprendre rapidement lors de l'utilisation d'un framework scala
  • Tous les développeurs n'ont pas la capacité à développer dans ce langage

Conclusion

Conclusion

Conseil personnel

 

S'intéresser au langage au minimum pour les notions qu'il apporte (notamment celles exprimées dans cette présentation).

 

Ne pas chercher à foncer seul dans ce langage si vous n'êtes pas un développeur fonctionnel.

De Java ou Js vers Scala

By Gaëtan Rizio

De Java ou Js vers Scala

Scala, le langage plus permissif que le Js et plus robuste que le Java. Comment y arriver ?

  • 464