1.2k

# Functional Programming in Dart - A practical approach

Functional programming has been a recent trend in the market and has many things to offer for increasing developer productivity. We're going to cover what Functional APIs are available in Dart and how they can help us write clear and concise code.

Spearkers: Samvid Mistry & Tirth Patel April 13, 2019

## Transcript

1. Functional Programming
in Dart
A practical approach

2. Course of talk
• Functions & Variables
• First class & Higher order functions
• Lambdas
• Code quality and quantity
• List APIs

3. Functions & Variables

4. Properties of functions
• Purity
• Only depends on data provided to it
• add(a, b) => a + b; //Pure
• int a;
add(b) => a + b; //Impure

5. Properties of functions
• Referential transparency
• A function always returns the same value on same input,
no matter when you call it
• add(a,b) => a + b; //transparent
• int a;
add(b) => a + b; //not transparent
• a = 5, add(5) => 10; a = 10, add(5) => 15
• Facilitated by purity of functions

6. Properties of variables
• Immutable, final, const.
• Functional Programming principles are language
independent
• All math functions are referentially transparent, i.e.
Math.sin(s), Math.sqrt(x), Math.max(a,b).

7. Why, bruh?
• Why should variables be immutable?
• No race conditions.
• Why should functions be pure?
• Concurrency and parallel evaluation.
• Why should functions be referentially transparent?
• Lazy evaluation.

8. First class & Higher
order functions

9. First class functions
• What does it mean to be first class?
• First class = type system can make sense of it.
• Imperative: a = 5; a = "string"; a = new Object();
• Functional: var f = (a) => (a + 1)
• Type: (int) -> int
• In general: (parameter_types) -> return_type

10. Higher order functions
• Simply put, it is a function which takes function as
argument and/or returns a function.
• apply(Function f, int a) { return f(a); }
• Function createAdder(int a) { return (b) => a + b; }

11. Lambdas

12. Lambdas
• OOP => Inheritance, top-down approach
• Functional => composition, bottom-up approach
• Naming is one of the biggest problems in programming
• Lambda = anonymous function
• (param1, param2, ...) => single_statement
• (param1, param2, ...) { //multiple statements }
• var adder = (a, b) => a + b;

13. Code quality and
quantity

14. Code quality and quantity
• Not laziness, but conciseness
• More code = More chances of error
• APIs = tried, tested and stable code
• "Best code is no code at all" - Unknown
• "We use code like violence. if it doesn't work, we use
more of it" - Venkat Subramaniam

15. –Albert Einstein
“e = (mc) ”
2

16. –Albert Einstein
“errors = (more code) ”
2

17. FP APIs for Iterable

18. removeWhere
List list = List.generate(10, (i) => i);
for (int i in list) {
if (i % 2 == 0)
list.remove(i);
}

19. removeWhere
List list = List.generate(10, (i) => i);
for (int i in list) {
if (i % 2 == 0)
list.remove(i);
}

20. removeWhere
List list = List.generate(10, (i) => i);
for (int i in list) {
if (i % 2 == 0)
list.remove(i);
}

21. removeWhere
List list = List.generate(10, (i) => i);
for (int i in list) {
if (i % 2 == 0)
list.remove(i);
}

22. removeWhere
List list = List.generate(10, (i) => i);
for (int i in list) {
if (i % 2 == 0)
list.remove(i);
}

23. removeWhere
List list = List.generate(10, (i) => i);

list.removeWhere((f) => f % 2 == 0);

24. any
List list = List.generate(10, (i) => i);
bool isFound = false;
for (int i in list) {
if (i % 2 == 0) {
isFound = true;
break;
}
}
if (isFound) print("Found");

25. any
List list = List.generate(10, (i) => i);
bool isFound = false;
for (int i in list) {
if (i % 2 == 0) {
isFound = true;
break;
}
}
if (isFound) print("Found");

26. any
List list = List.generate(10, (i) => i);
bool isFound = false;
for (int i in list) {
if (i % 2 == 0) {
isFound = true;
break;
}
}
if (isFound) print("Found");

27. any
List list = List.generate(10, (i) => i);
bool isFound = false;
for (int i in list) {
if (i % 2 == 0) {
isFound = true;
break;
}
}
if (isFound) print("Found");

28. any
List list = List.generate(10, (i) => i);
bool isFound = false;
for (int i in list) {
if (i % 2 == 0) {
isFound = true;
break;
}
}
if (isFound) print("Found");

29. any
List list = List.generate(10, (i) => i);
bool isFound = false;
for (int i in list) {
if (i % 2 == 0) {
isFound = true;
break;
}
}
if (isFound) print("Found");

30. any
List list = List.generate(10, (i) => i);
list.any((i) => i % 2 == 0) ? print("Found") : print(“Not Found”);

31. firstWhere
List list = List.generate(10, (i) => i);
int index = -1;
for (int i in list) {
if (i % 2 == 0) {
index = i;
break;
}
}
if (index != -1) print("Found \${list[index].toString()}");

32. firstWhere
List list = List.generate(10, (i) => i);
int index = -1;
for (int i in list) {
if (i % 2 == 0) {
index = i;
break;
}
}
if (index != -1) print("Found \${list[index].toString()}");

33. firstWhere
List list = List.generate(10, (i) => i);
int index = -1;
for (int i in list) {
if (i % 2 == 0) {
index = i;
break;
}
}
if (index != -1) print("Found \${list[index].toString()}");

34. firstWhere
List list = List.generate(10, (i) => i);
int index = -1;
for (int i in list) {
if (i % 2 == 0) {
index = i;
break;
}
}
if (index != -1) print("Found \${list[index].toString()}");

35. firstWhere
List list = List.generate(10, (i) => i);
int index = -1;
for (int i in list) {
if (i % 2 == 0) {
index = i;
break;
}
}
if (index != -1) print("Found \${list[index].toString()}");

36. firstWhere
List list = List.generate(10, (i) => i);
int index = -1;
for (int i in list) {
if (i % 2 == 0) {
index = i;
break;
}
}
if (index != -1) print("Found \${list[index].toString()}");

37. firstWhere
List list = List.generate(10, (i) => i);
int element = list.firstWhere((i) => i % 2 == 0, orElse: () => -1);
if(element != -1) {
print("Found \$element");
}

38. every
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
bool isAllA = true;
for (String follower in followers) {
if (!(follower.startsWith("a"))) isAllA = false;
}
if (isAllA) print("Good to go");
else print("Uh oh");

39. every
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
bool isAllA = true;
for (String follower in followers) {
if (!(follower.startsWith("a"))) isAllA = false;
}
if (isAllA) print("Good to go");
else print("Uh oh");

40. every
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
bool isAllA = true;
for (String follower in followers) {
if (!(follower.startsWith("a"))) isAllA = false;
}
if (isAllA) print("Good to go");
else print("Uh oh");

41. every
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
bool isAllA = true;
for (String follower in followers) {
if (!(follower.startsWith("a"))) isAllA = false;
}
if (isAllA) print("Good to go");
else print("Uh oh");

42. every
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
bool isAllA = true;
for (String follower in followers) {
if (!(follower.startsWith("a"))) isAllA = false;
}
if (isAllA) print("Good to go");
else print("Uh oh");

43. every
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
bool isAllA = true;
for (String follower in followers) {
if (!(follower.startsWith("a"))) isAllA = false;
}
if (isAllA) print("Good to go");
else print("Uh oh");

44. every
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
if(followers.every((s) => s.startsWith("a")))
print("Good to go");
else
print("Uh oh");

45. join
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
StringBuffer buffer = StringBuffer();
for (int i = 0; i < followers.length; i++) {
if (i == followers.length - 1) buffer.write(followers[i]);
else buffer.write(followers[i] + ", ");
}
print(buffer.toString());

46. join
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
StringBuffer buffer = StringBuffer();
for (int i = 0; i < followers.length; i++) {
if (i == followers.length - 1) buffer.write(followers[i]);
else buffer.write(followers[i] + ", ");
}
print(buffer.toString());

47. join
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
StringBuffer buffer = StringBuffer();
for (int i = 0; i < followers.length; i++) {
if (i == followers.length - 1) buffer.write(followers[i]);
else buffer.write(followers[i] + ", ");
}
print(buffer.toString());

48. join
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
StringBuffer buffer = StringBuffer();
for (int i = 0; i < followers.length; i++) {
if (i == followers.length - 1) buffer.write(followers[i]);
else buffer.write(followers[i] + ", ");
}
print(buffer.toString());

49. join
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
StringBuffer buffer = StringBuffer();
for (int i = 0; i < followers.length; i++) {
if (i == followers.length - 1) buffer.write(followers[i]);
else buffer.write(followers[i] + ", ");
}
print(buffer.toString());

50. join
List followers =
List.generate(10, (i) => "a".padLeft((i % 2) * 5, "z"));
StringBuffer buffer = StringBuffer();
for (int i = 0; i < followers.length; i++) {
if (i == followers.length - 1) buffer.write(followers[i]);
else buffer.write(followers[i] + ", ");
}
print(buffer.toString());

51. join
List followers =
2) * 5, "z")
print(followers.join(", "));

52. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
double sum = 0;
for (Employee e in employees) {
sum += e.salary;
}
return sum / employees.length;
}

53. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
double sum = 0;
for (Employee e in employees) {
sum += e.salary;
}
return sum / employees.length;
}

54. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
double sum = 0;
for (Employee e in employees) {
sum += e.salary;
}
return sum / employees.length;
}

55. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
double sum = 0;
for (Employee e in employees) {
sum += e.salary;
}
return sum / employees.length;
}

56. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
double sum = 0;
for (Employee e in employees) {
sum += e.salary;
}
return sum / employees.length;
}

57. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
double sum = 0;
for (Employee e in employees) {
sum += e.salary;
}
return sum / employees.length;
}

58. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
double sum = 0;
for (Employee e in employees) {
sum += e.salary;
}
return sum / employees.length;
}

59. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
return employees.fold(0, (s, e) => s + e.salary) / employees.length;
}

60. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
return employees.fold(0, (s, e) => s + e.salary) / employees.length;
}

61. fold
List employees = List.generate(
10, (i) => new Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(averageSalary(employees));
double averageSalary(List employees) {
return employees.fold(0, (s, e) => s + e.salary) / employees.length;
}

62. employees.fold(0, (s, e) => s + e.salary)
Employee {
name: a
salary: 0
}
Employee {
name: az
salary: 1000
}
Employee {
name: azz
salary: 2000
}
0

63. employees.fold(0, (s, e) => s + e.salary)
Employee {
name: a
salary: 0
}
Employee {
name: az
salary: 1000
}
Employee {
name: azz
salary: 2000
}
0

64. employees.fold(0, (s, e) => s + e.salary)
Employee {
name: a
salary: 0
}
Employee {
name: az
salary: 1000
}
Employee {
name: azz
salary: 2000
}
0

65. employees.fold(0, (s, e) => s + e.salary)
Employee {
name: a
salary: 0
}
Employee {
name: az
salary: 1000
}
Employee {
name: azz
salary: 2000
}
0

66. employees.fold(0, (s, e) => s + e.salary)
Employee {
name: az
salary: 1000
}
Employee {
name: azz
salary: 2000
}
Employee {
name: azzz
salary: 30
}
0

67. employees.fold(0, (s, e) => s + e.salary)
Employee {
name: az
salary: 1000
}
Employee {
name: azz
salary: 2000
}
Employee {
name: azzz
salary: 30
}
0

68. 1000
employees.fold(0, (s, e) => s + e.salary)
Employee {
name: az
salary: 1000
}
Employee {
name: azz
salary: 2000
}
Employee {
name: azzz
salary: 30
}

69. 1000
employees.fold(0, (s, e) => s + e.salary)
Employee {
name: azz
salary: 2000
}
Employee {
name: azzz
salary: 3000
}
Employee {
name: azzz
salary: 40
}

70. 1000
employees.fold(0, (s, e) => s + e.salary)
Employee {
name: azz
salary: 2000
}
Employee {
name: azzz
salary: 3000
}
Employee {
name: azzz
salary: 40
}

71. 3000
employees.fold(0, (s, e) => s + e.salary)
Employee {
name: azz
salary: 2000
}
Employee {
name: azzz
salary: 3000
}
Employee {
name: azzz
salary: 40
}

72. 3000
employees.fold(0, (s, e) => s + e.salary)
Employee {
name: azzz
salary: 3000
}
Employee {
name: azzzz
salary: 4000
}
Employee {
name: azzz
salary: 50
}

73. Fold as (almost) universal function
• employees.max()
employees.fold(0, (s, e) => (e.salary > s) ? e.salary : s)
• employees.any(predicate)
employees.fold(false, (s, e) =>
(s == false) ? predicate(e) : true)
• employees.every(predicate)
employees.fold(true, (s, e) =>
(s == true) ? predicate(e) : false)

74. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

75. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

76. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

77. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

78. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

79. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

80. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

81. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

82. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
List names = List();
for (Employee e in employees) {
}
return names;
}

83. map
List employees =
List.generate(10, (i) => Employee("a".padRight(i + 1, "z"), i * 1000));
employees.shuffle();
print(capitalize(employees));
List capitalize(List employees) {
return employees
.map((e) => e.name.replaceRange(0, 1, e.name.toUpperCase())).toList();
}

84. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

85. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

86. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

87. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

88. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

89. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

90. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

91. where
List numbers = List.generate(10, (i) => i);
for (int number in numbers) {
if (number % 2 == 1) {
print("\$number is odd");
}
}
for (int number in numbers) {
if (number % 2 == 0) {
print("\$number is even");
}
}

92. where
List numbers = List.generate(10, (i) => i);
numbers
.where((number) => number % 2 == 1)
.forEach((number) => print("\$number is odd"));
numbers
.where((number) => number % 2 == 0)
.forEach((number) => print("\$number is even"));

93. More Functional APIs
• https://api.dartlang.org/stable/2.2.0/dart-
core/Iterable-class.html#instance-methods
• https://api.dartlang.org/stable/2.2.0/dart-
async/Stream-class.html#instance-methods

94. A Combined Example

95. words.txt
art
dartisan
mart
data
dartium
earth
datamart
eggs
state of the art dart
spinach
dart
rider
part

96. main.dart
File('words.txt')
.split('\n')
.where((word) => word.contains('dart'))
.map((word) => '\${String.fromCharCodes(Runes('\u2764'))} -> \$word')
.forEach((word) => print(word));

97. main.dart
File('words.txt')
.split('\n')
.where((word) => word.contains('dart'))
.map((word) => '\${String.fromCharCodes(Runes('\u2764'))} -> \$word')
.forEach((word) => print(word));

98. main.dart
File('words.txt')
.split('\n')
.where((word) => word.contains('dart'))
.map((word) => '\${String.fromCharCodes(Runes('\u2764'))} -> \$word')
.forEach((word) => print(word));

99. main.dart
File('words.txt')
.split('\n')
.where((word) => word.contains('dart'))
.map((word) => '\${String.fromCharCodes(Runes('\u2764'))} -> \$word')
.forEach((word) => print(word));

100. main.dart
File('words.txt')
.split('\n')
.where((word) => word.contains('dart'))
.map((word) => '\${String.fromCharCodes(Runes('\u2764'))} -> \$word')
.forEach((word) => print(word));

101. main.dart
File('words.txt')
.split('\n')
.where((word) => word.contains('dart'))
.map((word) => '\${String.fromCharCodes(Runes('\u2764'))} -> \$word')
.forEach((word) => print(word));

102. Output
❤ -> dartisan
❤ -> dartium
❤ -> state of the art dart
❤ -> dart

103. Thank You!
Questions?

104. • github.com/samvidmistry
• github.com/piedcipher