Plus permissif que le Js et plus robuste que le Java
Faible
Fort
Fixe
Dynamique
Scala
Java 7
Java 8
C#
C++
Ocaml
JS
TypeScript
PHP
Les variables et les constantes
// 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"
Les variables et les constantes
// 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"
// 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
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.
Les collections – Déclaration
// 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")
Les collections – Déclaration
const list = ["a", "b"];
// Array
val array = Array("a", "b")
// List
val list = Seq("a", "b")
Les collections – transformation et filtres
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)
Les collections – transformation et filtres
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)
Comment :
Solution :
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!")
}
J'ai déjà obtenu des NullPointerException en Scala :
Ma fonction renvoie une valeur ou une erreur, comment en informer le développeur ?
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
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
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 {}
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)
}
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"
}
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
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
}));
Je souhaite créer une classe représentant ma table « User » en base de données.
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;
}
}
// 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; }
}
// 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")
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}'")
}
Le pattern matching peut également permettre de « retyper » une variable dont on ignore son type.
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é.
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.