Keynote presented at NewCrafts (2018-06-18)
Video available at https://vimeo.com/276832516
It has been said that immutability changes everything. But what does that mean in practice? What does it mean for existing code that looks more like the mutant apocalypse than an elegant application of mathematical thinking? Immutability can be an ideal that is hard to reach. Refactoring, on the other hand, is all about the art of the possible. In this talk we'll be clarifying motivation and exploring some approaches to help reducing state mutability in code.
11. To keep our C++ API boundary simple, we [...] adopted
one-way data flow. The API consists of methods to
perform fire-and-forget mutations and methods to
compute view models required by specific views.
To keep the code understandable, we write functional
style code converting raw data objects into immutable
view models by default. As we identified performance
bottlenecks through profiling, we added caches to avoid
recomputing unchanged intermediate results.
The resulting functional code is easy to maintain,
without sacrificing performance.
https://code.facebook.com/posts/498597036962415/under-the-hood-building-moments/
12.
13.
14.
15. Excel is the world's
most popular
functional language
Simon Peyton-Jones
22. As a programmer
I want code to
be reasonable
so that I can
reason about it
23. A large fraction of the flaws in software development
are due to programmers not fully understanding all
the possible states their code may execute in.
In a multithreaded environment, the lack of
understanding and the resulting problems are
greatly amplified, almost to the point of panic if you
are paying attention.
John Carmack
http://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php
25. Mutable
Immutable
Unshared Shared
Unshared mutable
data needs no
synchronisation
Unshared immutable
data needs no
synchronisation
Shared mutable
data needs
synchronisation
Shared immutable
data needs no
synchronisation
The Synchronisation Quadrant
30. There is a beautiful angel in that block
of marble, and I am going to find it.
All I have to do is to knock off the
outside pieces of marble, and be very
careful not to cut into the angel with
my chisel.
George F Pentecost
"The Angel in the Marble"
48. public class TimeOfDay
{
...
public int Hour
{
get ...
set ...
}
public int Minute
{
get ...
set ...
}
public void NextHour() ...
public void NextMinute() ...
}
49. public class TimeOfDay
{
private int minutes;
public const int minutesInHour = 60;
public const int hoursInDay = 24;
public const int minutesInDay = hoursInDay * minutesInHour;
private static int Wrap(int minutes)
{
return minutes % minutesInDay;
}
public int Hour
{
get
{
return minutes / minutesInHour;
}
set
{
if (value < 0 || value >= hoursInDay)
throw new ArgumentException();
minutes = Wrap(value * minutesInHour + Minute);
}
}
public int Minute
{
get
{
return minutes % minutesInHour;
}
set
{
if (value < 0 || value >= minutesInHour)
throw new ArgumentException();
minutes = Wrap(Hour * minutesInHour + value);
}
}
public void NextHour()
{
minutes = Wrap(minutes + minutesInHour);
}
public void NextMinute()
{
minutes = Wrap(minutes + 1);
}
}
50. public class TimeOfDay
{
...
public int Hour
{
get ...
set ...
}
public int Minute
{
get ...
set ...
}
public void NextHour() ...
public void NextMinute() ...
}
55. public class TimeOfDay
{
...
public TimeOfDay(int hour, int minute) ...
public int Hour => ...
public int Minute => ...
public TimeOfDay WithHour(int newHour) ...
public TimeOfDay WithMinute(int newMinute) ...
}
56. public class TimeOfDay
{
...
public TimeOfDay(int hour, int minute) ...
public int Hour => ...
public int Minute => ...
public TimeOfDay WithHour(int newHour) ...
public TimeOfDay WithMinute(int newMinute) ...
public TimeOfDay NextHour() ...
public TimeOfDay NextMinute() ...
}
57. public class TimeOfDay
{
...
public TimeOfDay(int hour, int minute) ...
public int Hour => ...
public int Minute => ...
public TimeOfDay WithHour(int newHour) ...
public TimeOfDay WithMinute(int newMinute) ...
public TimeOfDay NextHour() ...
public TimeOfDay NextMinute() ...
public class Builder
{
...
}
}
58. public class TimeOfDay
{
private readonly int minutes;
public const int minutesInHour = 60;
public const int hoursInDay = 24;
public const int minutesInDay = hoursInDay * minutesInHour;
private TimeOfDay(int minutes)
{
this.minutes = minutes % minutesInDay;
}
public TimeOfDay(int hour, int minute)
{
if (hour < 0 || hour >= hoursInDay || minute < 0 || minute >= minutesInHour)
throw new ArgumentException();
minutes = hour * minutesInHour + minute;
}
public int Hour => minutes / minutesInHour;
public int Minute => minutes % minutesInHour;
public TimeOfDay WithHour(int newHour) => new TimeOfDay(newHour, Minute);
public TimeOfDay WithMinute(int newMinute) => new TimeOfDay(Hour, newMinute);
public TimeOfDay NextHour() => new TimeOfDay(minutes + minutesInHour);
public TimeOfDay NextMinute() => new TimeOfDay(minutes + 1);
...
}
59.
60. try {
Integer.parseInt(time.substring(0, 2));
}
catch (Exception x) {
return false;
}
if (Integer.parseInt(time.substring(0, 2)) > 12) {
return false;
}
...
if (!time.substring(9, 11).equals("AM") &
!time.substring(9, 11).equals("PM")) {
return false;
}
Burk Hufnagel
"Put the Mouse Down and Step Away from the Keyboard"
61. Burk Hufnagel
"Put the Mouse Down and Step Away from the Keyboard"
return time.matches("(0[1-9]|1[0-2]):[0-5][0-9]:[0-5][0-9] ([AP]M)");
62. Je n'ai fait celle-ci plus
longue que parce que je
n'ai pas eu le loisir de la
faire plus courte.
Blaise Pascal
63. I have made this [letter]
longer than usual
because I have not had
time to make it shorter.
Blaise Pascal
64.
65.
66. // Get the unique surnames in uppercase of the
// first 15 book authors that are 50 years old
// or older?
library.stream()
.map(book -> book.getAuthor())
.filter(author -> author.getAge() >= 50)
.limit(15)
.map(Author::getSurname)
.map(String::toUpperCase)
.distinct()
.collect(toList())
67. // Get the first 15 unique surnames in
// uppercase of the book authors that are 50
// years old or older.
library.stream()
.map(book -> book.getAuthor())
.filter(author -> author.getAge() >= 50)
.map(Author::getSurname)
.map(String::toUpperCase)
.distinct()
.limit(15)
.collect(toList())
68. // Get the unique surnames in uppercase of the
// first 15 book authors that are 50 years old
// or older.
library.stream()
.map(book -> book.getAuthor())
.filter(author -> author.getAge() >= 50)
.distinct()
.limit(15)
.map(Author::getSurname)
.map(String::toUpperCase)
.distinct()
.collect(toList())
69. // Get the unique surnames in uppercase of the
// first 15 book authors that are 50 years old
// or older.
library.stream()
.map(book -> book.getAuthor())
.distinct()
.filter(author -> author.getAge() >= 50)
.limit(15)
.map(Author::getSurname)
.map(String::toUpperCase)
.distinct()
.collect(toList())
70.
71. // Get the unique surnames in uppercase of the
// first 15 book authors that are 50 years old
// or older?
List<Author> authors = new ArrayList<Author>();
for (Book book : library)
{
Author author = book.getAuthor();
if (author.getAge() >= 50)
{
authors.add(author);
if (authors.size() == 15)
break;
}
72. // Get the unique surnames in uppercase of the
// first 15 book authors that are 50 years old
// or older?
List<Author> authors = new ArrayList<Author>();
for (Book book : library)
{
Author author = book.getAuthor();
if (author.getAge() >= 50)
{
authors.add(author);
if (authors.size() == 15)
break;
}
}
List<String> result = new ArrayList<String>();
for(Author author : authors)
{
String name = author.getSurname().toUpperCase();
if (!result.contains(name))
result.add(name);
}
73. // Get the first 15 unique surnames in
// uppercase of the book authors that are 50
// years old or older.
List<String> result = new ArrayList<String>();
for (Book book : library)
{
Author author = book.getAuthor();
if (author.getAge() >= 50)
{
String name = author.getSurname().toUpperCase();
if (!result.contains(name))
{
result.add(name);
if (result.size() == 15)
break;
}
}
}
74. // Get the unique surnames in uppercase of the
// first 15 book authors that are 50 years old
// or older.
List<Author> authors = new ArrayList<Author>();
for (Book book : library)
{
Author author = book.getAuthor();
if (author.getAge() >= 50 && !authors.contains(author))
{
authors.add(author);
if (authors.size() == 15)
break;
}
}
List<String> result = new ArrayList<String>();
for(Author author : authors)
{
String name = author.getSurname().toUpperCase();
if (!result.contains(name))
result.add(name);
}
75. Try to leave out the part
that readers tend to skip.
Elmore Leonard
76. When it is not necessary
to change, it is necessary
not to change.
Lucius Cary