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

The joy of Functional Programming in Dart - #ftcon23

The joy of Functional Programming in Dart - #ftcon23

https://fluttercon.dev/csongor-vogel/

Functional programming is a programming paradigm that has gained increasing popularity in recent years thanks to its ability to improve code quality, reduce bugs, and increase productivity. Luckily, Dart has a strong foundation in functional programming concepts such as higher-order functions, closures, and immutability.

In this talk, we will explore how to unlock the power of functional programming in Dart. We will start with an introduction to functional programming concepts and why they matter. And learn more about functional programming design patterns. We will then dive into how Dart supports functional programming and how to apply functional programming techniques to solve real-world problems. We will cover the core principles of functional programming, such as immutability, purity, and higher-order functions.

Finally, we will discuss the benefits of functional programming in Dart, such as improved code readability, easier debugging, error handling, and faster development time. We will also explore how to use Dart's functional programming libraries, such as the fpdart and dartz packages, to implement functional programming techniques in your code. We will also address common challenges developers may face when transitioning to functional programming and provide strategies to overcome them.

Whether you are new to functional programming or an experienced developer looking to improve your Dart skills, this talk will equip you with the knowledge and techniques to unlock the power of functional programming in Dart and take your coding skills to the next level.

gerfalcon

July 07, 2023
Tweet

More Decks by gerfalcon

Other Decks in Programming

Transcript

  1. The joy of Functional
    Programming in Dart
    Senior Software Engineer at talabat, Delivery Hero
    Co-organizer of Flutter Abu Dhabi & Dubai
    gerfalcon GerfalconVogel
    Csongor Vogel
    csongorvogel

    View Slide

  2. About me
    ● Senior Software Engineer at talabat
    ● Lecturer at Budapest University of
    Technology and Economics
    ● Co-organizer of Flutter Abu Dhabi & Dubai

    View Slide

  3. Introduction to functional programming
    Functional programming in Dart
    Useful packages
    Conclusion
    1
    Agenda

    View Slide

  4. Functional programming
    ● It’s a programming paradigm based on mathematical functions
    ● Main goals
    ○ Purity
    ○ Immutability
    ○ High-order functions
    ○ Declarative programming style
    f(x)

    View Slide

  5. View Slide

  6. Mathematical functions
    f(x) = 2x
    Inputs Outputs
    1
    -1
    3
    2
    -2
    6
    x f(x)

    View Slide

  7. Mathematical functions
    f(x) = x2
    Inputs Outputs
    1
    -1
    3
    1
    9
    x f(x)

    View Slide

  8. Mathematical functions
    f(x)
    Inputs Outputs
    1
    -1
    3
    1
    9
    x f(x)
    -2

    View Slide

  9. Purity
    Always produce the same output for the
    same input and has no side effects.

    View Slide

  10. Area of a circle
    𝐴 = 𝜋𝑟!
    𝑟

    View Slide

  11. Area of a circle
    𝐴 = 𝜋𝑟!
    void main() {
    List inputs = [1.0, 2.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    double radius = inputs[i];
    double area = areaOfCircle(radius);
    print(area);
    }
    PI = 3.14159;
    double areaOfCircle(double r) {
    return PI * r * r;
    }
    // Prints 3.14159, 12.56636, 28.27431

    View Slide

  12. Area of a circle
    𝐴 = 𝜋𝑟!
    void main() {
    List inputs = [1.0, 2.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    double radius = inputs[i];
    double area = areaOfCircle(radius);
    print(area);
    }
    var PI = 3.14159;
    double areaOfCircle(double r) {
    return PI * r * r;
    }

    View Slide

  13. Area of a circle
    𝐴 = 𝜋𝑟!
    Not pure function

    // Prints 3.14159, 12.56636, 28.27431
    // 3.0, 12.0, 27.0
    var PI = 3;
    double areaOfCircle(double r) {
    return PI * r * r;
    }
    void main() {
    List inputs = [1.0, 2.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    double radius = inputs[i];
    double area = areaOfCircle(radius);
    print(area);
    }

    View Slide

  14. Immutability
    Data cannot be changed once created,
    reducing the chance of unexpected
    mutations and making code more
    predictable.

    View Slide

  15. Area of a circle
    𝐴 = 𝜋𝑟!
    var PI = 3;
    double areaOfCircle(double r) {
    return PI * r * r;
    }
    void main() {
    List inputs = [1.0, 2.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    double radius = inputs[i];
    double area = areaOfCircle(radius);
    print(area);
    }

    View Slide

  16. Area of a circle
    𝐴 = 𝜋𝑟!
    pure function

    const PI = 3.14159;
    double areaOfCircle(double r) {
    return PI * r * r;
    }
    void main() {
    List inputs = [1.0, 2.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    final area = areaOfCircle(radius);
    print(area);
    }
    // Prints 3.14159, 12.56636, 28.27431

    View Slide

  17. Area of a circle
    𝐴 = 𝜋𝑟!
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    final area = areaOfCircle(radius);
    print(area);
    }
    const PI = 3.14159;
    double areaOfCircle(double r) {
    if (r < 0) {
    throw ArgumentError(”error msg");
    }
    return PI * r * r;
    }
    Avoid side-effects

    View Slide

  18. Area of a circle
    𝐴 = 𝜋𝑟!
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    final area = areaOfCircle(radius);
    print(area);
    }
    const PI = 3.14159;
    double areaOfCircle(double r) {
    if (r < 0) {
    throw ArgumentError(”error msg");
    }
    }
    return PI * r * r;

    View Slide

  19. Area of a circle
    𝐴 = 𝜋𝑟!
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    final area = areaOfCircle(radius);
    print(area);
    }
    const PI = 3.14159;
    double? areaOfCircle(double r) {
    if (r < 0) {
    return null;
    }
    }
    return PI * r * r;

    View Slide

  20. Area of a circle
    𝐴 = 𝜋𝑟!
    const PI = 3.14159;
    double areaOfCircle(double r) {
    }
    return PI * r * r;
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    final area = areaOfCircle(radius);
    print(area);
    }

    View Slide

  21. Area of a circle
    𝐴 = 𝜋𝑟!
    const PI = 3.14159;
    double areaOfCircle(double r) {
    }
    return PI * r * r;
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    if (radius > 0) {
    final area = areaOfCircle(radius);
    print(area);
    }
    }

    View Slide

  22. Imperative style
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    if (radius > 0) {
    final area = areaOfCircle(radius);
    print(area);
    }
    }

    View Slide

  23. Imperative style Declarative style
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    inputs
    .where((input) => input > 0)
    .map((radius) => areaOfCircle(radius))
    .forEach((area) => print(area));
    }
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    for (int i = 0; i < inputs.length; ++i) {
    final radius = inputs[i];
    if (radius > 0) {
    final area = areaOfCircle(radius);
    print(area);
    }
    }

    View Slide

  24. Declarative style
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    inputs
    .skip(1)
    .where((input) => input > 0)
    .map((radius) => areaOfCircle(radius))
    .map((area) => area.toStringAsFixed(2))
    .toList()
    .take(2)
    .forEach((area) => print(area));
    }
    // Prints 3.14, 28.26

    View Slide

  25. Higher-order function
    Function can be passed as arguments to another function
    or be returned by another function.

    View Slide

  26. Higher-order function
    double areaOfCircle(double radius) {
    return PI * r * r;
    }
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    inputs
    .where((input) => input > 0)
    .map((radius) => areaOfCircle(radius))
    .forEach((area) => print(area));
    }

    View Slide

  27. Higher-order function
    void areaOfCircle(double r, Function printFunction) {
    double area = PI * r * r;
    printFunction('Area of circle is: $area');
    }
    void main() {
    List inputs = [-1.0, 1.0, 3.0];
    inputs
    .where((input) => input > 0)
    .map((radius) => areaOfCircle(radius, print))
    .toList();
    }

    View Slide

  28. Functional programming
    ● It’s a programming paradigm based on mathematical functions
    ● Contrast to Object-Oriented Programming (OOP),
    ○ mutable state
    ○ imperative programming style
    ● Main goals
    ○ Purity
    ○ Immutability
    ○ High-order functions
    ○ Declarative programming style
    f(x)

    View Slide

  29. ?
    Functional programming

    View Slide

  30. Functional programming
    ?

    View Slide

  31. 2
    Agenda
    Introduction to functional programming
    Functional programming in Dart
    Useful packages
    Conclusion

    View Slide

  32. Functional programming in Dart

    View Slide

  33. Functional programming in Dart
    ● final, const keywords
    ○ constants /// Declaring variables as final and const
    void main() {
    final String name = "John";
    print(name); // Prints John
    //name = "David"; // compile-time error
    const int age = 29;
    print(age); // Output: 25
    //age = 30; // compile-time error
    }

    View Slide

  34. Functional programming in Dart
    ● final, const keywords
    ○ constants
    ○ lists
    void main() {
    final List numbers = [1, 2, 3];
    //numbers = [4, 5, 6]; // compile-time error
    numbers.add(4); // This is allowed
    print(numbers); // Output: [1, 2, 3, 4]
    const List constNumbers = [1, 2, 3];
    //constNumbers = [4, 5, 6]; // compile-time error
    //constNumbers.add(4); // compile-time error
    print(constNumbers); // Output: [1, 2, 3]
    }

    View Slide

  35. Functional programming in Dart
    ● final, const keywords
    ○ constants
    ○ lists
    ○ classes
    class IPerson {
    final String name;
    final int age;
    IPerson(this.name, this.age);
    }
    void main() {
    var john = IPerson('John', 25);
    print(john.name); // Prints: John
    print(john.age); // Prints: 25
    //john.name = 'David'; // compile-time error
    //john.age = 30; // compile-time error
    }

    View Slide

  36. Functional programming in Dart
    ● final, const keywords
    ○ constants
    ○ lists
    ○ classes
    class IPerson {
    final String name;
    final int age;
    IPerson(this.name, this.age);
    IPerson copyWith({String? name, int? age}) {
    return IPerson(
    name ?? this.name,
    age ?? this.age,
    );
    }
    }
    void main() {
    var john = IPerson('John', 25);
    print(john.name); // Prints: John
    print(john.age); // Prints: 25
    var olderJohn = john.copyWith(age: 26);
    print(olderJohn.name); // Prints: John
    print(olderJohn.age); // Prints: 26
    }

    View Slide

  37. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    void main() {
    myFunction();
    }
    void myFunction() { /* ... */ }

    View Slide

  38. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ○ Lambda
    void main() {
    var greet = (name) => 'Hello, $name!’;
    print(greet('Dash')); // Hello, Dash!
    }

    View Slide

  39. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ○ Lambda
    ○ Closures
    void main() {
    var myCounter = counter(start: 0);
    print(myCounter()); // Prints 0
    print(myCounter()); // Prints 1
    }
    Function counter({required int start}) {
    return () => start++;
    }

    View Slide

  40. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    extension ExtensionList on List {
    // get the last item of the list
    T get lastItem => this[length - 1];
    // get a reversed copy of the list
    List get reversedCopy => this.reversed.toList();
    // repeat the list n times
    List repeat(int times) => [for(int i = 0; i < times; i++) ...this];
    }
    void main() {
    var list = [1, 2, 3, 4, 5];
    print(list.lastItem); // 5
    print(list.reversedCopy); // [5, 4, 3, 2, 1]
    print(list.repeat(2)); // [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2]
    }

    View Slide

  41. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ● Patterns
    Dart v3 🎉

    View Slide

  42. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ● Patterns
    Dart v3 🎉
    const year = (name: 'Best year ever', date: 2023);
    final nextYear = (name: year.name, date: year.date + 1);

    View Slide

  43. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ● Patterns
    Dart v3 🎉
    const year = (name: 'Best year ever', 2023);
    final nextYear = (name: year.name, year.$1 + 1);

    View Slide

  44. (String, String) getName() {
    var firstName = 'John';
    var lastName = 'Smith';
    return (firstName, lastName);
    }
    void main() {
    final name = getName();
    print('${name.$1} ${name.$2}'); // John Smith
    }
    Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ○ Multiple return values
    ● Patterns
    Dart v3 🎉

    View Slide

  45. (String, String) getName() {
    var firstName = 'John';
    var lastName = 'Smith';
    return (firstName, lastName);
    }
    void main() {
    final name = getName();
    print('${name.$1} ${name.$2}’); // John Smith
    // Destructuring
    final (firstName, lastName) = getName();
    print('$lastName $firstName'); // Smith John
    🇭🇺
    }
    Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ○ Multiple return values
    ○ Destructuring
    ● Patterns
    Dart v3 🎉

    View Slide

  46. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ○ Multiple return values
    ○ Destructuring
    ● Patterns
    ○ Exhaustiveness
    Dart v3 🎉
    String describeBools(bool b1, bool b2) => switch ((b1, b2)) {
    (true, true) => 'both true',
    (false, false) => 'both false',
    (true, false) => 'one of each',
    (false, true) => 'one of each',
    };

    View Slide

  47. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ○ Multiple return values
    ○ Destructuring
    ● Patterns
    ○ Exhaustiveness
    Dart v3 🎉
    String describeBools(bool b1, bool b2) => switch ((b1, b2)) {
    (true, true) => 'both true',
    (false, false) => 'both false',
    (true, false) => 'one of each',
    // (false, true) => 'one of each’, compile-time error
    };

    View Slide

  48. Functional programming in Dart
    ● final, const keywords
    ● Top-level functions
    ● Anonymus functions
    ● Extensions
    ● Records
    ○ Multiple return values
    ○ Destructuring
    ● Patterns
    ○ Exhaustiveness
    ○ Algebraic data type
    Dart v3 🎉
    sealed class Shape {}
    class Square implements Shape {
    final double length;
    Square(this.length);
    }
    class Circle implements Shape {
    final double radius;
    Circle(this.radius);
    }
    double calculateArea(Shape shape) => switch (shape) {
    Square(length: var l) => l * l,
    Circle(radius: var r) => PI * r * r
    };

    View Slide

  49. 𝑟
    getArea()
    areaOfCircle()
    Area of the delivery

    View Slide

  50. Try-catch

    Server

    No Internet

    Authentication

    Parsing

    Unknown
    Future getArea() async {
    try {
    final uri = Uri.parse(Constants.getAreaApi);
    final response = await http.get(uri);
    switch (response.statusCode) {
    case 200:
    final data = json.decode(response.body);
    return AreaModel.fromMap(data);
    default:
    throw Exception(response.reasonPhrase);
    }
    } on ParsingException catch (_) {
    rethrow;
    } on TypeError catch (_) {
    rethrow;
    } on Exception catch (_) {
    rethrow;
    } catch (_) {
    rethrow;
    }
    }

    View Slide

  51. Try-catch

    Server

    No Internet

    Authentication

    Parsing

    Unknown
    Future getArea() async { /// ...}
    void main() async {
    try {
    final result = await getArea();
    } on Exception catch (_) {
    print(Exception while getting area’);
    } catch (e) {
    print('Unexpected error while getting area');
    }
    }

    View Slide

  52. Result
    sealed class Result {
    const Result();
    }
    final class Success extends Result {
    const Success(this.value);
    final S value;
    }
    final class Failure extends Result {
    const Failure(this.exception);
    final E exception;
    }

    View Slide

  53. Result Future getArea() async {
    try {
    final uri = Uri.parse(Constants.getAreaApi);
    final response = await http.get(uri);
    switch (response.statusCode) {
    case 200:
    final data = json.decode(response.body);
    return AreaModel.fromMap(data);
    default:
    throw Exception(response.reasonPhrase);
    }
    } on ParsingException catch (_) {
    rethrow;
    } on TypeError catch (_) {
    rethrow;
    } on Exception catch (_) {
    rethrow;
    } catch (_) {
    rethrow;
    }
    }

    View Slide

  54. Result Future> getArea() async {
    try {
    final uri = Uri.parse(Constants.getAreaApi);
    final response = await http.get(uri);
    switch (response.statusCode) {
    case 200:
    final data = json.decode(response.body);
    return Success(AreaModel.fromMap(data));
    default:
    throw Exception(response.reasonPhrase);
    }
    } on Exception catch (e) {
    return Failure(e);
    } on TypeError catch (error) {
    return Failure(Exception(error.toString()));
    } catch (error) {
    return Failure(Exception(error.toString()));
    }
    }

    View Slide

  55. Try-catch
    void main() async {
    try {
    final result = await getArea();
    print(result);
    } on Exception catch (_) {
    print(Exception while getting area’);
    } catch (e) {
    print('Unexpected error while getting area');
    }
    }
    Future getArea() async {}

    View Slide

  56. Result
    void main() async {
    final Result result = await getArea();
    final String value = switch (result) {
    Success(value: final area) => area.toString(),
    Failure(exception: final exception) => exception.toString(),
    };
    }
    Future> getArea() async {}

    View Slide

  57. Next specification

    getArea()

    getRestaturantByAreaId()

    getBestDealByRestaurantId()

    View Slide

  58. Results 😱
    Future> getBestDeal() async {
    final areaResult = await getArea();
    if (areaResult case Success(value: final area)) {
    final restaurantResult = await getRestaurantByAreaId(area);
    return switch (restaurantResult) {
    Success(value: final restaurant) => await getBestDealByRestaurantId(restaurant.id),
    Failure(exception: final _) => Failure(Exception('')),
    };
    } else {
    return Failure(Exception('Error while getting best deal'));
    }
    }
    Future> getArea() async {}
    Future> getRestaurantByAreaId(int) async {}
    Future> getBestDealByRestaurantId(int) async {}

    View Slide

  59. Results 😱
    Future> getBestDeal() async {
    final areaResult = await getArea();
    if (areaResult case Success(value: final area)) {
    final restaurantResult = await getRestaurantByAreaId(area);
    return switch (restaurantResult) {
    Success(value: final restaurant) => await getBestDealByRestaurantId(restaurant.id),
    Failure(exception: final _) => Failure(Exception('')),
    };
    } else {
    return Failure(Exception('Error while getting best deal'));
    }
    }
    Future> getArea() async {}
    Future> getRestaurantByAreaId(int) async {}
    Future> getBestDealByRestaurantId(int) async {}

    View Slide

  60. 3
    Agenda
    Introduction to functional programming
    Functional programming in Dart
    Useful packages
    Conclusion

    View Slide

  61. Useful packages
    ● functional programming
    ○ dartz
    ○ fpdart
    ● immutability
    ○ fast_immutable_collections
    ○ kt_dart
    ○ built_collection
    ○ equatable
    ○ freezed
    ● before Dart 3
    ○ tuple
    ○ sealed_unions
    ○ multiple_result

    View Slide

  62. fpdart
    ● Well-documented
    ● Extensions
    ● Types
    ○ Unit
    ○ Option
    ○ Either
    ○ Task
    ■ TaskOption
    ■ TaskEither
    ○ State
    ■ StateAsync
    ○ IO
    ■ IOOption
    ■ IOEither
    ■ IORef
    ○ Reader
    Sandro Maglione

    View Slide

  63. Either /// Create an instance of [Right]
    final right = Either.of(10);
    /// Create an instance of [Left]
    final left = Either.left('none');
    /// Return [Left] if the function throws an error.
    /// Otherwise return [Right].
    final tryCatch = Either.tryCatch(
    () => int.parse('invalid'),
    (e, s) => 'Error: $e',
    );
    /// Extract the value from [Either]
    final value = right.getOrElse((l) => -1);
    /// Chain computations
    final flatMap = right.flatMap((a) => Either.of(a + 10))
    /// Pattern matching
    final match = right.match(
    (l) => print('Left($l)'),
    (r) => print('Right($r)'),
    );
    ● Error handling
    ● Try-catch constructor
    ● Extraction
    ● Chaining
    ● Pattern matching

    View Slide

  64. Result Future> getArea() async {
    try {
    final uri = Uri.parse(Constants.getAreaApi);
    final response = await http.get(uri);
    switch (response.statusCode) {
    case 200:
    final data = json.decode(response.body);
    return Success(AreaModel.fromMap(data));
    default:
    throw Exception(response.reasonPhrase);
    }
    } on Exception catch (e) {
    return Failure(e);
    } on TypeError catch (error) {
    return Failure(Exception(error.toString()));
    } catch (error) {
    return Failure(Exception(error.toString()));
    }
    }

    View Slide

  65. Either Future> getArea() async {
    try {
    final uri = Uri.parse(Constants.getAreaApi);
    final response = await http.get(uri);
    switch (response.statusCode) {
    case 200:
    final data = json.decode(response.body);
    return Either.right(AreaModel.fromMap(data));
    default:
    throw Exception(response.reasonPhrase);
    }
    } on ParsingException catch (e) {
    return Either.left(e.toString());
    } on Exception catch (e) {
    return Either.left(e.toString());
    } on TypeError catch (error) {
    return Either.left(error.toString());
    } catch (error) {
    return Either.left(error.toString());
    }
    }

    View Slide

  66. Result
    void main() async {
    final result = await getArea();
    final String value = switch (result) {
    Success(value: final area) => area.toString(),
    Failure(exception: final exception) => exception.toString(),
    };
    }
    Future> getArea() async {}

    View Slide

  67. Either Future getArea() async {}
    void main() async {
    final result = await getArea();
    /// Pattern matching
    result.match(
    (String error) => print(error),
    (AreaModel area) => print('$area'),
    );
    }

    View Slide

  68. Task /// Create instance of [Task] from a value
    final Task task = Task.of(10);
    /// Create instance of [Task] from an async function
    final taskRun1 = Task(() async => 10);
    final taskRun2 = Task(() => Future.value(10));
    /// Map [int] to [String]
    final Task map = task.map((a) => '$a');
    /// Extract the value inside [Task] by running its
    async function
    final int value = await task.run();
    /// Chain another [Task] based on the value of the
    current [Task]
    final flatMap = task.flatMap((a) => Task.of(a + 10));
    ● Wrapper around Future
    ● lazy evaluation
    ● Composing async functions

    View Slide

  69. TaskEither TaskEither getAreaTaskEither() =>
    TaskEither.tryCatch(() async {
    final uri = Uri.parse(Constants.getAreaApi);
    final response = await http.get(uri);
    switch (response.statusCode) {
    case 200:
    final data = json.decode(response.body);
    return AreaModel.fromMap(data);
    default:
    throw Exception(response.reasonPhrase);
    }
    }, (e, s) => '$e');

    View Slide

  70. Result Future> getArea() async {}
    void main() async {
    final result = await getArea();
    /// Pattern matching
    result.match(
    (l) => print(l),
    (r) => print(r),
    );
    }

    View Slide

  71. TaskEither TaskEither getArea() async {}
    void main() async {
    final task = getAreaTaskEither();
    final Either result = await task.run();
    /// Pattern matching
    result.match(
    (l) => print(l),
    (r) => print(r),
    );
    }

    View Slide

  72. Results
    Future> getBestDeal() async {
    final areaResult = await getArea();
    if (areaResult case Success(value: final area)) {
    final restaurantResult = await getRestaurantByAreaId(area);
    return switch (restaurantResult) {
    Success(value: final restaurant) => await getBestDealByRestaurantId(restaurant.id),
    Failure(exception: final _) => Failure(Exception('')),
    };
    } else {
    return Failure(Exception('Error while getting best deal'));
    }
    }
    Future> getArea() async {}
    Future> getRestaurantByAreaId(int) async {}
    Future> getBestDealByRestaurantId(int) async {}

    View Slide

  73. TaskEither
    void main() async {
    final task = getAreaTaskEither()
    .flatMap((area) => getRestaurantByAreaIdTaskEither(area.id))
    .flatMap((restaurant) => getBestDealByRestaurantId(restaurant.id));
    final Either result = await task.run();
    result.fold(
    (l) => print(l),
    (r) => print(r),
    );
    }
    TaskEither getArea() {}
    TaskEither getRestaurantByAreaId(int areaId) {}
    TaskEither getBestDealByRestaurantId(int id) {}

    View Slide

  74. 4
    Agenda
    Introduction to functional programming
    Functional programming in Dart
    Useful packages
    Conclusion

    View Slide

  75. Conclusion
    ● Understand the key concepts of functional programming
    ● Be aware about the Dart features
    ● Start small
    ○ Focus one aspects of the functional programming
    ○ Adopt it together with your colleagues

    View Slide

  76. Resources
    ● https://fsharpforfunandprofit.com/ by Scott Wlaschin
    ● https://github.com/spebbe/dartz by Björn Sperber
    ● https://github.com/SandroMaglione/fpdart by Sandro Maglione
    ● https://www.sandromaglione.com/ by Sandro Maglione
    ● https://codewithandrea.com/articles/functional-error-handling-either-fpdart/ by Andrea
    Bizotto
    ● https://github.com/marcglasberg/fast_immutable_collections by Marcelo Glasberg and
    Philippe Fanaro
    ● https://www.droidcon.com/2022/11/15/getting-started-with-functional-programming/ by
    Pascal Welsch
    ● https://resocoder.com/2019/12/14/functional-error-handling-in-flutter-dart-2-either-task-
    fp/ by Matt Rešetár (Reso Coder)

    View Slide

  77. Resources
    https://github.com/gerfalcon/fp_playground

    View Slide

  78. Senior Software Engineer at talabat, Delivery Hero
    Co-organizer of Flutter Abu Dhabi & Dubai
    gerfalcon GerfalconVogel
    Csongor Vogel
    csongorvogel
    Thank you

    View Slide