Share
Embed
Getting started with Functional Programming
Object Oriented Programming is on a decline in popularity since 2010, while Functional Programming experience a second hype. Most new languages today are a mix of both, functional and object-oriented.
Dart is an object-oriented language, featuring only some Functional Programming paradigms.
This talk will teach the mindset behind FP and how to use more FP in todays dart code. I will give an outlook to upcoming Dart language features that make FP actually fun to use in Dart!
Pascal Welsch
October 27, 2022
Transcript
92 slides
-
Functional Programming in Dart by @passsy (Pascal Welsch) Functional Programming
in Dart Pascal Welsch Google Developer Expert CTO at phntm GmbH Co-Founder of wiredash.io Droidcon London 2022 @passsy -
Functional Programming in Dart by @passsy (Pascal Welsch) Functional Programming
in Dart Pascal Welsch Google Developer Expert CTO at phntm GmbH Co-Founder of wiredash.io Droidcon London 2022 @passsy -
Functional Programming in Dart by @passsy (Pascal Welsch) PHNTM ['fæntəm]
Flutter Agency with 15 Flutter Experts from all across Germany -
Functional Programming in Dart by @passsy (Pascal Welsch) You won’t
learn today - Either, Task - Monads, Functors - Composition, pipelining - Pattern matching - Algebraic data types - Tail-recursion -
Functional Programming in Dart by @passsy (Pascal Welsch) You’ll learn
today Function programming basics: - 🍀 Pure functions - 🥶 Immutable data structures - 🌼 First-class functions - 🌸 High-order functions Outlook what the Dart team is working on -
Functional Programming in Dart by @passsy (Pascal Welsch) Dart and
Functional Programming? “Dart is a class-based, single-inheritance, pure object-oriented programming language.” ECMA-408 - 6. Overview -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> _products
= []; SortOrder sortOrder = SortOrder.desc; SortField sortField = SortField.price; -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> _products
= []; SortOrder sortOrder = SortOrder.desc; SortField sortField = SortField.price; class Product { final String name; final int price; final int originalPrice; final double rating; int get discount => originalPrice - price; } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> _products
= []; SortOrder sortOrder = SortOrder.desc; SortField sortField = SortField.price; enum SortOrder { asc, desc, } enum SortField { name, price, discount, rating, } -
Functional Programming in Dart by @passsy (Pascal Welsch) SortOrder sortOrder
= SortOrder.desc; SortField sortField = SortField.price; List<Product> _products = []; … label: InkWell( onTap: () { if (sortField == field) { // same field, just flip the sort order sortOrder = sortOrder.flip(); } else { // field changed, reset sort order sortOrder = SortOrder.desc; sortField = field; } -
Functional Programming in Dart by @passsy (Pascal Welsch) … label:
InkWell( onTap: () { if (sortField == field) { // same field, just flip the sort order sortOrder = sortOrder.flip(); } else { // field changed, reset sort order sortOrder = SortOrder.desc; sortField = field; } // sort the products based on the new sort order // TODO: update _products } List<Product> _products = []; -
Functional Programming in Dart by @passsy (Pascal Welsch) The naive
approach Use Dart built-in List<E>.sort([int compare(E a,E b)?]) function Compare products by selected property using Comparable<T>.compareTo(T other): int if (SortField.discount == sortField) { _products.sort((a, b) { return sortOrder == SortOrder.asc ? a.discount.compareTo(b.discount) : b.discount.compareTo(a.discount); }); } -
Functional Programming in Dart by @passsy (Pascal Welsch) _products.sort((a, b)
{ switch (sortField) { case SortField.name: return sortOrder == SortOrder.asc ? a.name.compareTo(b.name) : b.name.compareTo(a.name); case SortField.discount: return sortOrder == SortOrder.asc ? a.discount.compareTo(b.discount) : b.discount.compareTo(a.discount); case SortField.price: return sortOrder == SortOrder.asc ? a.price.compareTo(b.price) : b.price.compareTo(a.price); case SortField.rating: return sortOrder == SortOrder.asc ? a.rating.compareTo(b.rating) : b.rating.compareTo(a.rating); } }); Today’s task: Let’s make this code functional 🥷 -
Functional Programming in Dart by @passsy (Pascal Welsch) Functions Also
called: Lambda, Closure, Method, Callback, Routine, Subtypes (but still just functions): top-level, anonymous, predicate, high-order, first-class, … -
Functional Programming in Dart by @passsy (Pascal Welsch) String formatPrice(int
cents) {...} Input Output 12 0.12€ 100 1.00€ 2000 20.00€ -
Functional Programming in Dart by @passsy (Pascal Welsch) MoneyFormatter globalEuroFormatter;
String formatPrice(int cents) {...} Input Output 12 + globalEuroFormatter 0.12€ || -,12€ 100 + globalEuroFormatter 1.00€ || 1€ 2000 + globalEuroFormatter 20.00€ || 20,00€ || 20EUR -
Functional Programming in Dart by @passsy (Pascal Welsch) MoneyFormatter globalEuroFormatter;
String formatPrice(int cents) {...} Input Output 12 + globalEuroFormatter 0.12€ || -,12€ + globalEuroFormatter 100 + globalEuroFormatter 1.00€ || 1€ + globalEuroFormatter 2000 + globalEuroFormatter 20.00€ || 20,00€ || 20EUR + globalEuroFormatter -
Functional Programming in Dart by @passsy (Pascal Welsch) String formatPrice(int
cents) {...} 🍀 pure function No side-effects Doesn’t mutate the input Always returns the same result for the same input Does not call an impure function Can be replaced with a lookup table But why? -
Functional Programming in Dart by @passsy (Pascal Welsch) Pure Function
Advantages Can be executed in any order Can be executed in parallel Results can be cached, or pre-computed at compile time Easy to test in isolation Easy to focus on, no surprises Requirement: Only use immutable data structures -
Functional Programming in Dart by @passsy (Pascal Welsch) 🥶 Immutable
data structures final list = [1, 2, 3]; list.add(4); // mutation: Bad // Good: make a copy final copy = [...list, 4]; -
Functional Programming in Dart by @passsy (Pascal Welsch) Immutable data
structures final user = User(id: '27', name: 'Pascal', email: '[email protected]'); user.email = '[email protected]'; // Bad, email is mutable // Better, make a copy final updatedUser = user.copyWith(email: '[email protected]'); // user.email: '[email protected]' (unchanged) // updatedUser.email: '[email protected]' -
Functional Programming in Dart by @passsy (Pascal Welsch) Functional Programming
Style Avoid mutations Avoid side-effects -
Functional Programming in Dart by @passsy (Pascal Welsch) _products.sort((a, b)
{ switch (sortField) { case SortField.name: return sortOrder == SortOrder.asc ? a.name.compareTo(b.name) : b.name.compareTo(a.name); case SortField.discount: return sortOrder == SortOrder.asc ? a.discount.compareTo(b.discount) : b.discount.compareTo(a.discount); case SortField.price: return sortOrder == SortOrder.asc ? a.price.compareTo(b.price) : b.price.compareTo(a.price); case SortField.rating: return sortOrder == SortOrder.asc ? a.rating.compareTo(b.rating) : b.rating.compareTo(a.rating); } }); -
Functional Programming in Dart by @passsy (Pascal Welsch) _products.sort((a, b)
{ switch (sortField) { case SortField.name: return sortOrder == SortOrder.asc ? a.name.compareTo(b.name) : b.name.compareTo(a.name); case SortField.discount: return sortOrder == SortOrder.asc ? a.discount.compareTo(b.discount) : b.discount.compareTo(a.discount); case SortField.price: return sortOrder == SortOrder.asc ? a.price.compareTo(b.price) : b.price.compareTo(a.price); case SortField.rating: return sortOrder == SortOrder.asc ? a.rating.compareTo(b.rating) : b.rating.compareTo(a.rating); } }); Mutation of _products Access of class property sortOrder -
Functional Programming in Dart by @passsy (Pascal Welsch) // Extract
pure function List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { // make a copy, mutation of inputs isn’t pure final copy = products.toList(); copy.sort((a, b) { switch (sortField) { case SortField.name: return sortOrder == SortOrder.asc ? a.name.compareTo(b.name) : b.name.compareTo(a.name); case SortField.discount: case SortField.price: case SortField.rating: // … } }); // return an immutable list return List.unmodifiable(copy); } // Call the pure function _products = sortProducts(_products, sortField, sortOrder); -
Functional Programming in Dart by @passsy (Pascal Welsch) // Extract
pure function List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { // ... } // Call the pure function _products = sortProducts(_products, sortField, sortOrder); -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { // make a copy, mutation of inputs isn’t pure final copy = products.toList(); // ... // return an immutable list return List.unmodifiable(copy); } _products = sortProducts(_products, sortField, sortOrder); -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); // Use our existing sort code copy.sort((a, b) { switch (sortField) { case SortField.name: return sortOrder == SortOrder.asc ? a.name.compareTo(b.name) : b.name.compareTo(a.name); case SortField.discount: case SortField.price: case SortField.rating: // … } }); return List.unmodifiable(copy); } _products = sortProducts(_products, sortField, sortOrder); What a nice, pure function! -
Functional Programming in Dart by @passsy (Pascal Welsch) Pure Function
Advantages Can be executed in any order Can be executed in parallel Results can be cached, or pre-computed at compile time Easy to test in isolation Easy to focus on, no surprises -
Functional Programming in Dart by @passsy (Pascal Welsch) Level 1
completed: You learned the concept of pure functions! -
Functional Programming in Dart by @passsy (Pascal Welsch) 🌼 High
order functions A higher-order function is a function that takes functions as parameters, or returns a function. // Takes a function as input Result myFunction(Function fn) { // … } // Returns a function Function myFunction() { // … } // both, input and output are functions Function myFunction(Function fn) { // … } -
Functional Programming in Dart by @passsy (Pascal Welsch) // Let’s
refactor! List<Product> getProductsWith4StarPlusRating() { final List<Product> result = []; for (final product in _products) { if (product.rating > 4) { result.add(product); } } return result; } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> getProductsWith4StarPlusRating()
{ final List<Product> result = []; for (final product in _products) { if (product.rating > 4) { result.add(product); } } return result; } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> getProductsWithRating(double
minRating) { final List<Product> result = []; for (final product in _products) { if (product.rating > minRating) { result.add(product); } } return result; } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> getProductsWith4StarPlusRating()
{ final List<Product> result = []; for (final product in _products) { if (product.rating > 4) { result.add(product); } } return result; } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> getProducts(bool
Function(Product) predicate) { final List<Product> result = []; for (final product in _products) { if (predicate(product)) { result.add(product); } } return result; } getProducts((product) => product.rating > 4); getProducts((product) => product.rating > 3); -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> getProducts(bool
Function(Product) predicate) { final List<Product> result = []; for (final product in _products) { if (predicate(product)) { result.add(product); } } return result; } getProducts((product) => product.rating > 4); getProducts((product) => product.rating > 3); -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> getProducts(bool
Function(Product) predicate) { final List<Product> result = []; for (final product in _products) { if (predicate(product)) { result.add(product); } } return result; } getProducts((product) => product.rating > 4); getProducts((product) => product.rating > 3); _products.where((product) => product.rating > 4); _products.where((product) => product.rating > 3); -
Functional Programming in Dart by @passsy (Pascal Welsch) // List<Product>
getProducts(bool Function(Product) predicate) { // final List<Product> result = []; // for (final product in _products) { // if (predicate(product)) { // result.add(product); // } // } // return result; // } // // getProducts((product) => product.rating > 4); // getProducts((product) => product.rating > 3); _products.where((product) => product.rating > 4); _products.where((product) => product.rating > 3); -
Functional Programming in Dart by @passsy (Pascal Welsch) _products.where((product) =>
product.rating > 4); _products.where((product) => product.rating > 3); -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> getProductsWith4StarPlusRating()
{ final List<Product> result = []; for (final product in _products) { if (product.rating > 4) { result.add(product); } } return result; } List<Product> getProductsWith3StarPlusRating() { final List<Product> result = []; for (final product in _products) { if (product.rating > 3) { result.add(product); } } return result; } -
Functional Programming in Dart by @passsy (Pascal Welsch) Isn’t creating
lambdas like (product) => product.rating > 4 expensive at scale? -
Functional Programming in Dart by @passsy (Pascal Welsch) Function references
// function as lambda _products.where((product) => product.rating > 4); bool has4StarPlusRating(Product product) { return product.rating > 4; } // function reference _products.where(has4StarPlusRating); -
Functional Programming in Dart by @passsy (Pascal Welsch) 🌸 Functions
as first-class citizens Functions can be stores in variables and data structures and can be passed as arguments or returned from higher-order functions bool has4StarPlusRating(Product product) { return product.rating > 4; } final bool Function(Product) savedFunction = has4StarPlusRating; final List<bool Function(Product)> list = [has4StarPlusRating, has3StarPlusRating]; list.add((Product) => true); -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); copy.sort((a, b) { switch (sortField) { case SortField.name: return sortOrder == SortOrder.asc ? a.name.compareTo(b.name) : b.name.compareTo(a.name); case SortField.discount: case SortField.price: case SortField.rating: // … } }); return List.unmodifiable(copy); } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); // replace switch case with long nested if-else if (SortField.discount == sortField) { if (sortOrder == SortOrder.asc) { copy.sort((a, b) => a.discount.compareTo(b.discount)); } else { copy.sort((a, b) => b.discount.compareTo(a.discount)); } } else { // … } return List.unmodifiable(copy); } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); if (SortField.discount == sortField) { if (sortOrder == SortOrder.asc) { copy.sort((a, b) => a.discount.compareTo(b.discount)); } else { copy.sort((a, b) => b.discount.compareTo(a.discount)); } } else { // … } return List.unmodifiable(copy); } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); late int Function(Product a, Product b) compare; if (SortField.discount == sortField) { if (sortOrder == SortOrder.asc) { compare = (a, b) => a.discount.compareTo(b.discount); } else { compare = (a, b) => b.discount.compareTo(a.discount); } } else { // … } copy.sort(compare); return List.unmodifiable(copy); } -
Functional Programming in Dart by @passsy (Pascal Welsch) _products =
sortProducts(_products, sortField, sortOrder); List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { // make a copy, mutation of inputs isn’t pure final copy = products.toList(); late int Function(Product a, Product b) compare; if (SortField.discount == sortField) { if (sortOrder == SortOrder.asc) { compare = (a, b) => a.discount.compareTo(b.discount); } else { compare = (a, b) => b.discount.compareTo(a.discount); } } else { // … } copy.sort(compare); // return an immutable list return List.unmodifiable(copy); } -
Functional Programming in Dart by @passsy (Pascal Welsch) final Map<SortField,
int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { // make a copy, mutation of inputs isn’t pure final copy = products.toList(); late int Function(Product a, Product b) compare; if (SortField.discount == sortField) { if (sortOrder == SortOrder.asc) { compare = (a, b) => a.discount.compareTo(b.discount); } else { compare = (a, b) => b.discount.compareTo(a.discount); } } else { // … } copy.sort(compare); // return an immutable list return List.unmodifiable(copy); -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); late int Function(Product a, Product b) compare; if (SortField.discount == sortField) { if (sortOrder == SortOrder.asc) { compare = (a, b) => a.discount.compareTo(b.discount); } else { compare = (a, b) => b.discount.compareTo(a.discount); } } else { // … } copy.sort(compare); return List.unmodifiable(copy); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); final Function(Product a, Product b) compare = sortFunctionsByFieldAsc[sortField]!; copy.sort(compare); return List.unmodifiable(copy); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); final Function(Product a, Product b) compare = sortFunctionsByFieldAsc[sortField]!; copy.sort(compare); return List.unmodifiable(copy); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); var Function(Product a, Product b) compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse; } copy.sort(compare); return List.unmodifiable(copy); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; extension Reverse<T> on int Function(T, T) { int Function(T, T) get reverse => (a, b) => this(b, a); } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); var Function(Product a, Product b) compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse; } copy.sort(compare); return List.unmodifiable(copy); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; extension Reverse<T> on int Function(T, T) { int Function(T, T) get reverse => (a, b) => this(b, a); } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { final copy = products.toList(); var Function(Product a, Product b) compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse; } copy.sort(compare); return List.unmodifiable(copy); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; extension Reverse<T> on int Function(T, T) { int Function(T, T) get reverse => (a, b) => this(b, a); } -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { var compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse; } return List.unmodifiable(products.toList()..sort(compare)); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; extension Reverse<T> on int Function(T, T) { int Function(T, T) get reverse => (a, b) => this(b, a); } -
Functional Programming in Dart by @passsy (Pascal Welsch) Vanilla Dart
solution List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse; } return List.unmodifiable(products.toList()..sort(compare)); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; extension Reverse<T> on int Function(T, T) { int Function(T, T) get reverse => (a, b) => this(b, a); } -
Functional Programming in Dart by @passsy (Pascal Welsch) What about
functional programming dart packages? -
Functional Programming in Dart by @passsy (Pascal Welsch) List<Product> sortProducts(List<Product>
products, SortField sortField, SortOrder sortOrder) { var compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse; } return List.unmodifiable(products.toList()..sort(compare)); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; extension Reverse<T> on int Function(T, T) { int Function(T, T) get reverse => (a, b) => this(b, a); } -
Functional Programming in Dart by @passsy (Pascal Welsch) import 'package:kt_dart/kt.dart';
List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse(); } return List.unmodifiable(products.toList()..sort(compare)); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; // extension Reverse<T> on int Function(T, T) { // int Function(T, T) get reverse => (a, b) => this(b, a); // } -
Functional Programming in Dart by @passsy (Pascal Welsch) import 'package:kt_dart/kt.dart';
List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse(); } return List.unmodifiable(products.toList()..sort(compare)); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; // extension Reverse<T> on int Function(T, T) { // int Function(T, T) get reverse => (a, b) => this(b, a); // } -
Functional Programming in Dart by @passsy (Pascal Welsch) import 'package:dartx/dartx.dart';
import 'package:kt_dart/kt.dart'; List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var compare = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { compare = compare.reverse(); } // return List.unmodifiable(products.toList()..sort(compare)); return products.sortedWith(comparator); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; // extension Reverse<T> on int Function(T, T) { // int Function(T, T) get reverse => (a, b) => this(b, a); // } -
Functional Programming in Dart by @passsy (Pascal Welsch) import 'package:dartx/dartx.dart';
import 'package:kt_dart/kt.dart'; List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var comparator = sortFunctionsByFieldAsc[sortField]!; if (sortOrder == SortOrder.desc) { comparator = compare.reverse(); } return products.sortedWith(comparator); } final Map<SortField, int Function(Product a, Product b)> sortFunctionsByFieldAsc = { SortField.rating: (a, b) => a.rating.compareTo(b.rating), SortField.name: (a, b) => a.name.compareTo(b.name), SortField.discount: (a, b) => a.discount.compareTo(b.discount), SortField.price: (a, b) => a.price.compareTo(b.price), }; -
Functional Programming in Dart by @passsy (Pascal Welsch) import 'package:dartx/dartx.dart';
import 'package:kt_dart/kt.dart'; List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var comparator = compareBy(fieldSelectors[sortField]!); if (sortOrder == SortOrder.desc) { comparator = comparator.reverse(); } return products.sortedWith(comparator); } final Map<SortField, Comparable Function(Product)> fieldSelectors = { SortField.rating: (it) => it.rating, SortField.name: (it) => it.name, SortField.discount: (it) => it.discount, SortField.price: (it) => it.price, }; -
Functional Programming in Dart by @passsy (Pascal Welsch) import 'package:dartx/dartx.dart';
import 'package:kt_dart/kt.dart'; List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var comparator = compareBy(fieldSelectors[sortField]!); if (sortOrder == SortOrder.desc) { comparator = comparator.reverse(); } return products.sortedWith(comparator); } final Map<SortField, Comparable Function(Product)> fieldSelectors = { SortField.rating: (it) => it.rating, SortField.name: (it) => it.name, SortField.discount: (it) => it.discount, SortField.price: (it) => it.price, }; -
Functional Programming in Dart by @passsy (Pascal Welsch) _products =
sortProducts(_products, sortField, sortOrder); List<Product> sortProducts(List<Product> products, SortField sortField, SortOrder sortOrder) { var comparator = compareBy(fieldSelectors[sortField]!); if (sortOrder == SortOrder.desc) { comparator = comparator.reverse(); } return products.sortedWith(comparator); } final Map<SortField, Comparable Function(Product)> fieldSelectors = { SortField.rating: (it) => it.rating, SortField.name: (it) => it.name, SortField.discount: (it) => it.discount, SortField.price: (it) => it.price, }; Functional darty solution -
Functional Programming in Dart by @passsy (Pascal Welsch) _products.sort((a, b)
{ switch (sortField) { case SortField.name: return sortOrder == SortOrder.asc ? a.name.compareTo(b.name) : b.name.compareTo(a.name); case SortField.discount: return sortOrder == SortOrder.asc ? a.discount.compareTo(b.discount) : b.discount.compareTo(a.discount); case SortField.price: return sortOrder == SortOrder.asc ? a.price.compareTo(b.price) : b.price.compareTo(a.price); case SortField.rating: return sortOrder == SortOrder.asc ? a.rating.compareTo(b.rating) : b.rating.compareTo(a.rating); } }); The naive approach -
Functional Programming in Dart by @passsy (Pascal Welsch) Functional Code
>>> Imperative Code Easy to test Pure functions can’t be flaky - by design Easy to debug Easy to refactor Thread-safe by design, easy to parallelize -
Functional Programming in Dart by @passsy (Pascal Welsch) Is Dart
good choice for Functional Programming? Pro - Functions are first-class citizens - one-line lambdas are supported - Immutable references (final) - Generics - Extension functions - Type inference Cons - No immutable collections - No algebraic data types (unions) - No pattern matching - Many dart:core functions have side-effects and throw exceptions - Lambda are quite verbose -
Functional Programming in Dart by @passsy (Pascal Welsch) Functional Programming
- a new tool in your toolbox -
Functional Programming in Dart by @passsy (Pascal Welsch) Dart and
Functional Programming? “Dart is a class-based, single-inheritance, pure object-oriented programming language.” ECMA-408 - 6. Overview -
Functional Programming in Dart by @passsy (Pascal Welsch) A bright
functional future for Dart? “Patterns and related features” is in the “impl” phase (dart-lang/language/546) -
Functional Programming in Dart by @passsy (Pascal Welsch) Immutable data
structures No official support by Dart. Use kt_dart or fast_immutable_collection -
Functional Programming in Dart by @passsy (Pascal Welsch) You learned
today Function programming basics: - 🍀 Pure functions - no side effects. input -> output - 🥶 Immutable data structures - freezed, kt_dart - 🌼 First-class functions - function are like objects - 🌸 High-order functions // both, input and output can be functions Function myFunction(Function fn) {…} -
Functional Programming in Dart by @passsy (Pascal Welsch) Do yourself
a favour and write more functional code. Thank me later Pascal Welsch 👋 -
Functional Programming in Dart by @passsy (Pascal Welsch) Functional Programming
in Dart Pascal Welsch Google Developer Expert CTO at phntm GmbH Co-Founder of wiredash.io Droidcon London 2022 @passsy