Let's start with an example. In Advent of Code 2022 day 1 we are given groups of numbers as a string that look like:
1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
We want to write a program that sums up each group and returns the largest group. The largest in this example is the 4th group, 24000.
Here is an imperative solution:
const input = $('pre').innerText let runningMax = 0 for (let group of input.split("\n\n")) { let runningSum = 0 for (let num of group.split("\n")) runningSum += Number(num) runningMax = Math.max(runningMax, runningSum) } console.log(runningMax)
And here is a functional solution:
const input = $('pre').innerText function sumGroup(group) { return group .split("\n") .map(Number) .reduce((a,b)=>a+b) } const sortedGroups = input .split("\n\n") .map(sumGroup) .sort((a,b)=>a-b) console.log(sortedGroups.at(-1))
The imperative solution:
- We declare variables called
runningMax
andrunningSum
that we mutate throughout the program - We use
for
loops to iterate
The functional solution:
- We avoid declaring mutable variables
- Instead of iterating with
for
loops, we use higher order functions likemap
,reduce
, andsort
Higher order functions are functions that operate on other functions. map
, reduce
, and sort
take a function as an argument.
How exactly does one do functional programming?
Avoid | Favor |
---|---|
mutable state | function parameters and immutable data |
iterative steps | transforming data |
loop statements | recursion or higher order functions |
intertwined data and behavior | separated data and behavior |
Looking at the two examples above, you may think functional programming is simply using higher level functions. Maybe you are wondering, "map
is just a for loop! What if map
did not already exist?" A functional programmer would write map
themself using recursion. Anything written with loop statements can also be written with recursion.
Imagine we want a range
function where range(5)
returns [1,2,3,4,5]
.
Imperative solution:
function range(end) { let nums = [] for (let i=1; i<=end; i++) nums.push(i) return nums }
Functional solution:
function range(end, cur=[]) { if (cur.length >= end) return cur return range(end, [...cur, cur.length+1]) }
Before I started exploring functional programming I would have never thought to implement this function recursively. But now that I compare the two solutions, the recursive solution actually feels more natural.
Why use functional programming
Functional programming has a lot of amazing benefits. Let's explore the biggest things:
Avoid bugs related to mutable state. Just like static typing eliminates the class of bugs caused by dynamic typing, functional programming elimates the class of bugs caused by mutable state. Every programmer has copied an array, modified it, and then spent way too long trying to figure out how the original array also got modified (aka shallow copies vs. deep copies). When mutable state is shared all over code, the potential for bugs like this is increased.
Write declarative code. Imperative code describes how to do things while declarative code describes what we are doing. This means declarative code is often more natural to understand. Think about how the original imperative solution has variables named runningMax
and runningSum
. It's tempting to name these variables maxGroup
and sum
, but that would be lying. They start at 0 and change over the course of the program. We are stuck with variable names describing how we do things. In the functional solution, sortedGroups
describes exactly what we are doing.
Write code that is easier to test. If you are used to writing imperative code, it might be tempting to write void functions which mutate variables outside of the function's scope. A common way to make games is to declare state as variables and write a void function like update()
to mutate those variables each tick. Instead of mutating variables, functional programming favors update
taking game state as an argument and returning new state. Now our code is much easier to test. We simply call update
with some game state and assert that the result is what we expect.
from Hacker News https://ift.tt/gsFDSnC
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.