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 ?
- 503