Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Programming in Dart - A practical approach

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

6972c514f3a1a1d5c47f4325a04e640b?s=128

Tirth Patel

April 13, 2019
Tweet

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. • Less cognitive load. • 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; } • Function chaining: createAdder(10)(20)
  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 • 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
  15. –Albert Einstein “e = (mc) ” 2

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

  17. FP APIs for Iterable

  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");
  30. any List<int> list = List<int>.generate(10, (i) => i); list.any((i) =>

    i % 2 == 0) ? print("Found") : print(“Not 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"));
  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 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
  103. Thank You! Questions?

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

    • medium.com/@piedcipher