Accumulator Pattern for beginners | Fold vs Reduce | AndroidBites
Worse fate you put your codebase in when you don't focus on such primitive Pattern
Featured on :
|
---|

Disclaimer : All the assets used as background belongs to respective companies I don't promote them as my own, they are just used to make code relatable and attractive.
Accumulator pattern is one the most used pattern in programming, knowingly unknowingly every developer is using it daily without even realising it. If you don't know what it is, let me demonstrate using an example :
var total = 0
val items = listOf(1,2,3,4,5,6,7,8,9)
for(item in items){
total+=item
}
println(total)
//output : 45
You might have got the gist, in accumulator pattern we are accumulating
a computation
into a variable
, from above example that variable would be total.
This style of programming is also called the imperative style
of programming. You might have heard this term a lot because there has been numerous debates in community regarding, which kind of programming style is better - imperative
or functional
?.
This article won't focus on that but I would like to add, both styles have their own benefits and we should consider mixing the concepts they bring together in order to get the most out of them.
Let's focus on the shortcoming of the above example and try to overcome :
var total = 0
// `total` is a mutable property i.e. if someone can
// access it, then can change its value. We need to
// make sure that doesn’t happen,a variable should be local to its
// logic and shouldn’t be updated outside the scope.
// This variable needs to be `val`, `private` or both.
// If we consider this to be in a function then `private` isn't
// the option and same problem will persist.
val items = listOf(1,2,3,4,5,6,7,8,9)
for(item in items){
total+=item
// we only want our variable to be mutable within the scope of
// its computation not outside of it. In order to do that we
// need to bring in `total` variable into the scope of `for`
// loop but then we won't be able to return result
// and our logic will fail.
// other point is that, to perform total we are relying on
// an external variable. This creates dependency and since
// its a mutable object, if its value is updated before
// reaching this point of the code, we would get the wrong
// result, making this logic hard to trust for correctness.
}
// ... after lots of loc[line of code] ...
// the role of `total` variable has ended up already
// but since its available for access we can might face accidental
// updations. example:
total += 23
// suppose an accidental access to the variable has happened
// someone other then you, gets the access of the variable and
// create a feature or fixed a bug using it, now there is
// multiple point to access/update on your logic variable.
// this will directly impact code maintenance and also create
// hard dependencies/coupling because you don't know how deep that
// variable is rooted inside of that code.
println(total) // output : 68
// you would be wasting lots of time ⏰ in figuring out source of bugs
// desperately relying on 🕵🏻 the debugger and feeling frustrated.💆🏻
The issue you see is part of the imperative style of programming, you need to be extra careful in terms of visibility and scoping while designing your APIs using OOPs concept , let’s try to fix our pain points.
Disclaimer : it is not a deep dive or history lesson forfold
orreduce
operations, please refer to Kotlin docs for detailed information.
Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation💰
Fold Operation: [docs] 🚙
It’s one of the functional implementation of accumulator pattern in functional style. Let see the function signature and the let's break it down :
inline fun <T, R> Iterable<T>.fold(
initial: R,
operation: (acc: R, T) -> R
): R
If you want me to coverlambdas
andinline
or any other topic, submit the feedback in form below.
The extension is formed on many receivers, you can refer docs for all supported types.
Let’s focus on what it says :
- Fold extension takes two parameters, first is the
initial value
and second is alambda
called operation. lambda
is invoked on each iteration.- Iteration starts from first item to the last i.e from
left to right
. lambda
has 2 parameters,acc:R
which isaccumulator
andT
isitem
of current iteration.- lambda always return
R
i.e theaccumulator
- And the entire fold extension returns
R
type i.e the finalResult of the accumulation
or we can say thelast value of Accumulator
which is returned by the lambda
So according to this, the result of computation is returned by lambda, and that result on its final step is returned to the receiving variable. My lambda returns my local scoped computation. That is a lot of power! We have to achieve closed controlled mutation.
Now we can transform above example into :
val total = listOf(1,2,3,4,5,6,7,8,9)
.fold(0){ acc , item->
var sum = acc + item
// pass 1 : sum = 0 + 1
// pass 2 : sum = 1 + 2
// pass 3 : sum = 3 + 3
// pass 4 : sum = 6 + 4
// pass 5 : sum = 10 + 5
// and so on...
return@fold sum
}
println(total)
//output : 45
// see now accumulator Variable total is
// immutable/read only, therefore, no accidental
// update.
// moreover my compuation now have a scope if
// it gives a bug then area to navigate for
// logic is a lot narrow!
Reduce Operation : [Docs] 🚗
Same as fold
, It’s one of the functional implementation of accumulator pattern in functional style. Let’s see its signature :
inline fun <S, T : S> Iterable<T>.reduce(
operation: (acc: S, T) -> S
): S
The extension is formed on many receivers, you can refer docs for all supported types.
Let’s focus on what it says :
- Reduce extension takes only one parameter, that is a
lambda
called operation. lambda
is invoked on each iteration.- Iteration starts from first item to the last i.e from
left to right
. - lambda has 2 parameters,
acc:R
which isaccumulator
andT
isitem
of that current iteration. - lambda always return
R
i.e theaccumulator
. - And the entire reduce extension returns
R
type i.e the finalResult of the accumulation
or we can say,the last value of Accumulator
which is returned by the lambda.
Using reduce, the above example would become :
val total = listOf(1,2,3,4,5,6,7,8,9)
.reduce{ acc , item->
var sum = acc + item
// pass 1 : sum = 1 + 2
// pass 2 : sum = 3 + 3
// pass 3 : sum = 6 + 4
// pass 4 : sum = 10 + 5
// pass 5 : sum = 15 + 6
// and so on...
return@reduce sum
}
println(total)
// output: 45
So both of them helps you to break out imperative style of coding, greatly improves accumulator pattern and also introduce immutability in your code.
But now, if both, fold
and reduce
serves the purpose, which one should you use?
Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation💰
Fold 🆚 Reduce
The difference between both of these functions is that, fold()
takes a default value and uses it as the first step of the accumulator, whereas reduce()
uses the first item of iteration as the first step of the accumulator.
// You can't reduce an empty collection
val items = listOf<Int>()
val total = items.reduce{acc,it->acc+it}
// Fatal : java.lang.UnsupportedOperationException:
// Empty collection can't be reduced.
// prefer fold over reduce when you know collection could be empty
val items = listOf<Int>()
val total = items.fold(0){acc,it->acc+it} // output : 0
//OR
//like always kotlin have `getOrNull` types extensions for most of
//exception throwing functions
val items = listOf<Int>()
val total = items.reduceOrNull{acc,it->acc+it} ?: -1 // output : -1
One more is thing that you can control is the return type
of an object that is received from the operation. For instance :
val values = listOf(1,2,3,4,5,6,7,8,9)
val doublesOfValue = values.fold(mutableListOf<Int>()){acc,it->
acc.add(it*2)
return@fold acc
}
// output : [2, 4, 6, 8, 10, 12, 14, 16, 18]
// we don't have such flexibility with reduce.
// Note : this is different than map operation , which we
// will discover in later posts. stay connected!
If we operate only on a non-empty collection and combine all elements into a single result of the same type, then reduce()
is a good choice. On the other hand, if we want to work on a default value or change the result type of the operation, then fold()
gives us the flexibility to do that!.
Conclusion 💆🏻♀️
So, we are done, now we understood accumulator pattern and how fold()
and reduce()
are better ways to implement it. It could also be your first step to functional programming, own it slowly and grow your arsenal.
That's all for today 💻! see ya again in next article. Peace!✌
Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation💰