Software Design/Transform collection declaratively
Checklist questions:
- Is it possible to transform or populate a collection or an array declaratively rather than in an imperative loop?
- Is it possible to find or compute some result while iterating a collection with a declarative operation rather than an imperative loop?
The technologies which facilitate declarative computations on collections include:
- LINQ in C#
- Streams in Java
- List comprehensions, generator expressions, and some functions from the
itertools
module in Python
Why
[edit | edit source]It's harder to make a mistake in a declarative or functional transformation than in an imperative loop because less (or none) additional variables are defined in the transformation and each of them is scoped to its anonymous function without interfering with other variables.
The semantics of a declarative pipeline may be more apparent because they refer to library symbols such named like "sum", "map", "where", "collect", "findFirst", etc.[1][2] which identify the logical operation being applied. On the other hand, with collection transformation in an imperative loop, readers have to read the whole loop and recognize the logical operation being applied by the loop pattern, which puts cognitive load on them.
Functional collection processing usually requires less code than imperative. Functional processing is also often clearer than imperative processing because the former is free of boilerplate code such as a declaration of a loop variable and references to a collection element by index. Since functional collection transformation is free of low-level details about how to iterate the collection, it makes the definition of the collection and the processing logic looser coupled.[3]
Declarative computations over a collection automatically ensure following the practice of not doing several collection transformations in a single loop.
Why not
[edit | edit source]It may be harder to debug declarative collection computations than equivalent imperative loops.[3][4]
Functional collection transformation pipeline may be less efficient than an equivalent imperative code.[5] Even when the performance difference between optimally written functional and imperative versions is not significant, it might be easy to make a performance bug in a declarative pipeline which would lead to unnecessary materialization of intermediate collection views. For example, in Kotlin, this is sometimes a matter of forgetting asSequence()
call in the beginning of a pipeline.[6]
Related
[edit | edit source]Sources
[edit | edit source]- Goetz, Brian (July 6, 2016). "An introduction to the java.util.stream library".
References
[edit | edit source]- ↑ Refactoring: Improving the Design of Existing Code (2 ed.). 2018. ISBN 978-0134757599. https://martinfowler.com/books/refactoring.html. Chapter 3, "Loops" section
- ↑ Refactoring: Improving the Design of Existing Code (2 ed.). 2018. ISBN 978-0134757599. https://martinfowler.com/books/refactoring.html. Chapter 8, "Replace Loop with Pipeline" section
- ↑ 3.0 3.1 In Java, what are the advantages of streams over loops? question on StackOverflow
- ↑ Fowler, Martin (June 25, 2015). "Collection Pipeline".
- ↑ Java 8: performance of Streams vs Collections question on StackOverflow
- ↑ Piwowarek, Grzegorz (May 1, 2018). "Kotlin: Beware of Java Stream API Habits".