Tirth Patel
April 13, 2019
1.3k

# 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

2. ### Course of talk • Functions & Variables • First class

& Higher order functions • Lambdas • Code quality and quantity • List APIs

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. • Less cognitive load. • Why should functions be pure? • Concurrency and parallel evaluation. • Why should functions be referentially transparent? • Lazy evaluation.

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; } • Function chaining: createAdder(10)(20)

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;

14. ### Code quality and quantity • Functional APIs help you write

less code • 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

18. ### removeWhere List<int> list = List<int>.generate(10, (i) => i); for (int

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

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

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

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

i in list) { if (i % 2 == 0) list.remove(i); }
23. ### removeWhere List<int> list = List<int>.generate(10, (i) => i);    list.removeWhere((f)

=> f % 2 == 0);
24. ### any List<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.generate(10, (i) => i); bool isFound

= false; for (int i in list) { if (i % 2 == 0) { isFound = true; break; } } if (isFound) print("Found");

31. ### firstWhere List<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.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<int> list = List<int>.generate(10, (i) => i); int element

= list.firstWhere((i) => i % 2 == 0, orElse: () => -1); if(element != -1) { print("Found \$element"); }
38. ### every List<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.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<String> followers = List<String>.generate(10, (i) => "a".padLeft((i % 2)

* 5, "z") print(followers.join(", "));
52. ### fold List<Employee> employees = List.generate( 10, (i) => new Employee("a".padRight(i

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

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

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

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

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

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

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

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

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

+ 1, "z"), i * 1000)); employees.shuffle(); print(averageSalary(employees)); double averageSalary(List<Employee> 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<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
75. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
76. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
77. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
78. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
79. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
80. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
81. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
82. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { List<String> names = List<String>(); for (Employee e in employees) { names.add(e.name.replaceRange(0, 1, e.name[0].toUpperCase())); } return names; }
83. ### map List<Employee> employees = List.generate(10, (i) => Employee("a".padRight(i + 1,

"z"), i * 1000)); employees.shuffle(); print(capitalize(employees)); List<String> capitalize(List<Employee> employees) { return employees .map((e) => e.name.replaceRange(0, 1, e.name[0].toUpperCase())).toList(); }
84. ### where List<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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"));

95. ### words.txt art bread dartisan mart data dartium earth datamart eggs

state of the art dart spinach dart rider part
96. ### main.dart File('words.txt') .readAsStringSync() .split('\n') .where((word) => word.contains('dart')) .map((word) => '\${String.fromCharCodes(Runes('\u2764'))}

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

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

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

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

-> \$word') .forEach((word) => print(word));
101. ### main.dart File('words.txt') .readAsStringSync() .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

104. ### • github.com/samvidmistry • github.com/piedcipher • twitter.com/@mistrysamvid • twitter.com/@piedcipher • medium.com/@samvidmistry

• medium.com/@piedcipher