Julien Richard-Foy
http://julienrf.github.io/breizhcamp-2016
BreizhCamp 2016
Principe selon lequel la signification d’une expression complexe est définie par les significations des expressions la composant, et par les règles employées pour les combiner.
IllegalStateException
(existe aussi sous la forme d’une NullPointerException
, IOException
, etc.)
User user = new User();
println(user.name);
User user = new User("Julien");
doSomething(user);
println(user.name);
Integer n = 1;
n + n == 1 + 1;
Image img = new Circle(5);
img.beside(img) == (new Circle(5)).beside(new Circle(5));
Image ear = new Circle(8);
Image face = new Circle(10);
Image mickey =
ear.beside(ear)
.above(face);
ie. Éviter de ne « rien » retourner
When you program you have to think about how someone will read your code, not just how a computer will interpret it.
Kent Beck
public class Note {
public Name name;
public Rhythm rhythm;
}
public class Note {
public final Name name;
public final Rhythm rhythm;
public Note(Name name, Rhythm rhythm) {
this.name = name;
this.rhythm = rhythm;
}
}
Note do1 = new Note(C, Quarter);
Note do2 = new Note(C, Quarter);
assert(do1.equals(do2)); // ???
public final class Note {
public final Name name;
public final Rhythm rhythm;
public Note(Name name, Rhythm rhythm) {
this.name = name;
this.rhythm = rhythm;
}
@Override
public Boolean equals(that) {
if (that instanceof Note) {
return this.name.equals(that.name) && this.rhythm.equals(that.rhythm);
} else return false;
}
@Override
public Long hashCode() {
return (17 + name.hashCode()) * 31 + rhythm.hashCode();
}
}
Scala :
case class Note(
name: Name,
rhythm: Rhythm
)
Haskell :
data Note = Note Name Rhythm
public abstract class Symbol {
private Symbol() {}
public abstract void accept(Visitor visitor);
public static final class Note extends Symbol {
…
@Override
public void accept(Visitor visitor) {
visitor.visitNote(this);
}
}
public static final class Rest extends Symbol {
…
@Override
public void accept(Visitor visitor) {
visitor.visitRest(this);
}
}
public static interface Visitor {
void visitNote(Note note);
void visitRest(Rest rest);
}
}
a.k.a. union types
Scala :
sealed trait Symbol
case class Note(name: Name, rhythm: Rhythm) extends Symbol
case class Rest(rhythm: Rhythm) extends Symbol
def doSomething(symbol: Symbol): Unit =
symbol match {
case Note(name, rhythm) => …
case Rest(rhythm) => …
}
Haskell :
data Symbol = Note Name Rhythm | Rest Rhythm
doSomething (Note name rhythm) = …
doSomething (Rest rhythm) = …
The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise
Edsger W. Dijkstra
Each significant piece of functionality in a program should be implemented in just one place in the source code.
Where similar functions are carried out by distinct pieces of code, it is generally beneficial to combine them into one by abstracting out the varying parts.
Benjamin C. Pierce
Double earArea = earRadius * earRadius * Math.PI;
Double faceArea = faceRadius * faceRadius * Math.PI;
Double earArea = earRadius * earRadius * Math.PI;
Double faceArea = faceRadius * faceRadius * Math.PI;
public Double disqArea(Double radius) {
return radius * radius * Math.PI;
}
Double earArea = earRadius * earRadius * Math.PI;
Double faceArea = faceRadius * faceRadius * Math.PI;
public Double disqArea(Double radius) {
return radius * radius * Math.PI;
}
Double earArea = disqArea(earRadius);
Double faceArea = disqArea(faceRadius);
public Double disqArea(Double radius) {
return radius * radius * Math.PI;
}
public Integer sum(List<Integer> numbers) {
Integer result = 0;
for (Integer n : numbers) {
result = result + n;
}
return result;
}
public Integer product(List<Integer> numbers) {
Integer result = 1;
for (Integer n : numbers) {
result = result * n;
}
return result;
}
public Integer reduce(
List<Integer> numbers,
Integer init,
BiFunction<Integer, Integer, Integer> op
) {
Integer result = init;
for (Integer n : numbers) {
result = op.apply(result, n)
}
return result;
}
public Integer sum(List<Integer> numbers) {
return reduce(numbers, 0, (n1, n2) -> n1 + n2);
}
public Integer product(List<Integer> numbers) {
return reduce(numbers, 1, (n1, n2) -> n1 * n2);
}
public Image allBeside(List<Image> images) {
Image result = Image.EMPTY;
for (Image image : images) {
result = result.beside(image);
}
return result;
}
public Image allBeside(List<Image> images) {
return reduce(images, Image.EMPTY, (i1, i2) -> i1.beside(i2));
}
Scala :
def reduce(numbers: List[Int], init: Int, op: (Int, Int) => Int): Int
def sum(numbers: List[Int]): Int =
reduce(numbers, 0, (x, y) => x + y);
Haskell :
reduce :: Integer -> (Integer -> Integer -> Integer) -> [Integer] -> Integer
reduce init op numbers = …
sum :: [Integer] -> Integer
sum = reduce 0 (\x y -> x + y)
La programmation fonctionnelle est pratique pour :
Java
interface Optional<T> {
public <U> Optional<U> map(Function<? super T, ? extends U> mapper);
}
Scala
trait Option[A] {
def map[B](f: A => B): Option[B]
}
Haskell
fmap :: (a -> b) -> Maybe a -> Maybe b