## Generator Functions

Have you ever heard of the Baader-Meinhof phenomenon? It's a cognitive illusion where once you become aware of some *thing*, you see that *thing* every where. It's called an illusion because the thing was always there, you were just unaware of its presence.

I feel like generator functions are a bit of a Baader-Meinhof phenomenon for me. I never had a use for them, but once I figured out a single use for them, now I see reasons to use them everywhere.

I recently used them while doing some maze generation experiments during some time off. Check out the tweets in this thread if that interests you. They were perfect for the problem I had at hand, and I want to take some time to explain what they are and why they might be useful to you.

### What's a Generator Function?

A generator function is a special function, denoted by the use of the `*`

in its declaration.

```
function* myFirstGenerator() {
// do stuff here
}
```

This function is *different*. Calling this function will not return the value of your function's body. Not right away, at least. More on this shortly. Instead, it returns a `Generator`

object. This object has several methods on it, but the one we're most concerned with is the `next`

method. Calling `next()`

on our generator object is how we return the next generated result from our generator function.

This result is an object with two properties: `value`

and `done`

. Let's discuss how we get these `value`

s first.

The most common way we will "return" values from a generator functions is with the `yield`

keyword. Let's give it a try in a *very* rudimentary way.

```
function* myFirstGenerator() {
yield 42
}
const generatorObject = myFirstGenerator()
console.log(generatorObject.next()) // { value: 42, done: false }
console.log(generatorObject.next()) // { value: undefined, done: true }
```

I want to draw your attention to a few things. First, as I pointed out, we call our generator function to get our `generatorObject`

. Calling `next`

returns an object with the `yield`

ed `value`

and a `done`

property. But why is `done`

`false`

on the first `yield`

? There's nothing else returned in our function body, right?

Wrong.

Just like all normal JavaScript functions, there is an implicit `return undefined`

if there isn't an explicit `return`

statement in the function body. This implicit `return undefined`

is what we get on the second call of the `next`

method. Hence why `value`

is `undefined`

and `done`

is now `true`

.

From this, we can deduce that using `return`

instead of `yield`

is how to stop a generator.

Let's combine the use of `return`

with *multiple* `yield`

s. Yes, that's right. You can `yield`

as many times as you would like in a generator function.

```
function* countTo(number) {
let i = 0
while (i < number - 1) {
i++
yield i
}
return number
}
const countTo3 = countTo(3)
console.log(countTo3.next()) // { value: 1, done: false }
console.log(countTo3.next()) // { value: 2, done: false }
console.log(countTo3.next()) // { value: 3, done: true }
// Let's call it an extra time to see what happens
console.log(countTo3.next()) // { value: undefined, done: true }
```

As you can see, our function "counts" as we expect and is `done`

when we expect, too.

### Something Mildly More Interesting

Let's try this on something mildly more interesting. Are you familiar with the Collatz conjecture. It's an algorithm in mathematics that returns a sequence of numbers. The algorithm is as follows:

Given any positive integer, if the number is even, divide it by 2, otherwise, multiply it by 3 and add 1. Repeat until you arrive at 1.

Writing this algorithm as a generator function is pretty straightforward *and* we can generate each step in the sequence.

```
const isOdd = num => Boolean(num % 2)
const collatz = num => (isOdd(num) ? 3 * num + 1 : num / 2)
function* collatzSequence(num) {
let current = num
while (current !== 1) {
yield current
current = collatz(current)
}
return current
}
const sequence = collatzSequence(17)
```

I want to draw your attention to something I find interesting about how we're able to write this algorithm with a generator. Because the function essentially pauses execution on the `yield`

statement, we're able to `yield`

the current number *before* mutating the `current`

variable with the next `collatz`

result. I've made a simple React component that will allow you to try out different numbers and generate the Collatz sequence.

#### Collatz Sequencer

Isn't that interesting? We're able to get each step in the sequence very easily, and the code is pretty simple to write as well.

### Something Practical

Alright, let's make one more generator function, but use it to solve a practical problem. In fact, I'm going to use it to solve the very problem that inspired me to write this blog post: **loan repayment**.

If you follow me on Twitter, you probably know of my trevails with student loans. We've been paying them off aggressively for years, and still have a few more years to go, but how many to be exact? And how much will an extra \$100 here or there help me? All questions that can be answered with a small program. So let's build a simplified version of it.

A loan has a `principal`

(starting amount), an `interestRate`

, a compounding period (how often it compounds), and a `payment`

.

Let's start with the simplest part, a few helper functions:

```
// Basic compounding formula
const compound = (amount, rate) => amount + amount * rate
// Keeps floats to a max of 2 places past the decimal
const to2 = num => Number(num.toFixed(2))
```

Now, let's set about writing a `makePayment`

generator function. I'll bake some details specific to student loans in the function, and make comments when I'm doing so.

```
function* makePayment(principal, rate, payment) {
// Student loans compound daily, so we create a daily interest rate
// by dividing our annual rate with the number of days in a year
const dailyInterestRate = rate / 365
let remaining = principal
let totalInterestPaid = 0
let paymentsCount = 0
while (remaining > 0) {
let beginWith = remaining
let endWith = remaining
// For simplification, we're going to treat each payment period
// as 30 days long
for (let i = 0; i < 30; i++) {
endWith = compound(endWith, dailyInterestRate)
}
// Student loans pay the accrued interest first, then the principal
// We're keeping this formula rudimentary, but this let's us see what
// interest accrued since the last payment
const interestAccrual = to2(endWith - beginWith)
totalInterestPaid = to2(totalInterestPaid + interestAccrual)
endWith = to2(endWith - payment)
remaining = to2(endWith)
paymentsCount++
if (endWith <= 0) {
endWith = 0
remaining = 0
return {
beginWith,
endWith,
interestAccrual,
paymentsCount,
totalInterestPaid,
}
}
yield {
beginWith,
endWith,
interestAccrual,
paymentsCount,
totalInterestPaid,
}
}
}
```

#### Simple Payment Generator

As of the time of this writing, looks like I have about ~33 payments to go. It's pretty cool to see how much changing the payment can change how long it will take to pay the rest of the loan.

If I wanted to spend more time on this in this particular post, you could make some very cool data visualizations with generator functions sequencing each update to the visualization. Perhaps I'll explore this in a future post.

### Summary

Generator functions are a special function that allow you to pause execution, `yield`

ing multiple (if not infinite) values from them. You might have to stretch your imagination to find a good use for them, but they're a handy tool to have in your tool chest.

If you'd like to read more about them, I suggest looking at the MDN docs on them.

### Caveat

Generator functions will not work in any version of IE. Womp womp.

### Finished reading?

Liked the post? Give the author a dopamine boost with a few "beard strokes". Click it up to 50 times show your appreciation.