TL;DR: Generators can hide stateful operations for you.
No time for FP vs OOP. Instead we will concentrate on the clarity and reusability of Protocol-Oriented code, while enhancing the well known for in loop.
When you got such a loop, there is a chance that you might keep track of a certain value and change the related variable in each iteration.
Like, building a sum of all the elements up to the current element …
These lines sum up the values in each iteration, e.g. to get the starting point when you draw a horizontal bar, and you want to know the starting x for each bar segment.
Extra shoutouts for using stride, but calculating the sum this way is not reusable at all.
In those 5 lines of code, the word sum appears 4 times. Amazing how much visual space is dedicated
to that simple operation. And worse, once you have more complicated conditions or even multiple things to keep track of,
you enter the dark zone. This is where you code gets unmanageable, hard to understand at first sight and possibly dangerous.
Some people claim: “No, thats the easiest way to do. I don`t need another concept for this.”
Sure, we will find out.
Any generator is good enough 🤖
So the basic thing to understand at first is the SequenceType and the related Generator it has.
When you look into the doc of SequenceType, the first line makes it clear:
A type that can be iterated with a for ... in loop.
Many protocols conform to this protocol (CollectionType, LazyCollectionType, … ), check out the Adopted By section in the docs.
So when you have a SequenceType you get a Generator from it, which returns an Element for each .next() call.
Eventually the sequence is done, there is no more Element left, and you will get nil: The loop is done
Attention: Swift 3 will rename Generator to Iterator and there are more changes to come
This is a great start to add some more sugar to the for in loop.
The sum of it 🎲
In order to build up SequenceTypes that provide more power we need protocols.
First we add a basic protocol which declares that two variables of a type can be combined:
And one more protocol: Before we get the first element of the loop, we need to have an initial value for the sum - a zero.
To sum up:
✅ We have a protocol to ‘get an initial value for a sum’
✅ We have a protocol to ‘add a value to a sum’
An implementation looks like this:
Now we build a Sequence Type which knows about those two protocols.
What do we gain?
The zipWithSum is concise and meaningful at the same time.
And the best part: You will never ever have to calculate a sum in a loop again.
The calculation went into a sequence type function. All that is left is the code you really want to focus on.
Btw: If you wonder why am i using stride instead of a range?
There is no reason. Of course you can also take a simple range.
Fold all the things 🗺
Now it is up to you. You can stop reading and say:
“It`s enough, I don`t want more abstraction in my code”.
Or we can go one step further. (And i bet there a lot of ways to make this concept even better).
But first - one step back: when we try to describe what “building a sum” is, in an abstract way, we can use the following (curried) type signature:
The type signature is the interesting part: A -> E -> A (curried notation). If you are a category theory fan - i will call it a Catamorphism. What`s so interesting about this signature? Well you can build up anything by destroying the previous structured value: Like group by, filter, sum, difference and so forth.
Gimme the protocol to start from!
You can see a rebuild of the sum example in the following code.
Interestingly, the sum operation now becomes a Type.
And types are awesome.
Next we add a new SequenceType which has a Generator that returns the result of the current fold in each iteration….
The Proof of Pudding 🍮
The zip fold is done. The motivation is to abstract away the operations you want to perform on every loop. You will find all the implementations in the Gist at the end.
Question:What does the result for sum look like?
Question:Want to start the sum at 100?
Question:Want to see the min value up to each position?
Question:Want to see the worst three values on each position?
Question:Want to see the Min and the Sum at each position?
Question:What is the lowest value when we reach a sum of 100?
Question:Is it better than writing the loop manually?
Of course. This is a great way to abstract away code that smells in front of your nose.