By Brett Slatkin
Recursive sum_up function
Advantages
- Completely deterministic
- no mutable state
- Data-driven tests are easy
- Debugging: easy to reproduce production issues
Split up program into three distinct phases:
- Input phase
- Reading input
- Do stuff phase
- Pure business logic
- Output phase
- "dirty work"
- E.g., writing stuff back to a file
Python not technically "functional". Recursion can typically break early. Python is technically more imperative.
"Stackless Python," not well-maintained.
Python recursive economics can be unintuitive.
Example: sum_up_imperative vs sum_up_unrolled
- Not the type of mutability that he's worried about
Stateful Fibonacci sequence:
class Fib:
...
def fib_up(n, Fib: fib)
...Example: fib_down
Intially:
fib = Fib()
result = fib_up(8, fib)
print(result)Later developer:
fib = Fib()
result = fib_up(8, fib)
result2 = fib_down(3, fib)
print(result) # current == 5
print(result2) # current == 5@dataclass(frozen=True)
class FibFrozen:
current: int = 0
next: int = 1
def fib_up(n, fib):
for i in range(n)
fib = FibFrozen(
current=fib.next,
next = fib.current + fib.next)
return fibTesting easier and predictable.
def my_func(n):
return n * 2Big speedups available for pure functions with functools.cache:
import functools
@functools.cache
def ...:
...Assigning a parameter in advance. Convenient with functools.partial.
def apply_tax(total, rate):
...
from functools import partial
my_apply_tax = partial(apply_tax, rate=0.095)Readability issue
def finalize_cart(total, coupon, tax_rate, shipping):
...
print(finalize_cart(...)) # 129.40
def fnlcart_customt(total, apply_shipping, apply_tax, apply_coupon):
return apply_shipping(apply_tax(apply_coupon(total)))def fnlcart_customt(total, apply_shipping, apply_tax, apply_coupon):
... = apply_coupon(total)
... = apply_tax(...)
... = apply_shipping(...)
return finishedPipe operator in R... Might go through in Python with some PEP...
def many_fibonaccis(items):
```
fibonacci for each input element
```
...
many_fibonaccis([1,3,5])
# returns 1, 2, 5 )Mclaren F1 example:
- Clay versus Lego
- Clay: customized
- Lego: replaceable parts, etc.
Less bespoke stuff. How do I take this problem and solve it immutably with map.
Get bonuses for writing things functionally.
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ProcessPoolExecutor- Free threading (no GIL)
- multiproccessing (IPC)
- Threading (parallel I/O)
Read-only stuff
Why?
- Stable hashing
- Tracking lineage
- Sharing
- help you avoid certain errors
- easier: datadriven tests, bug reproducbility
- wrap pure logic in functional sandiwch
- composition for pluggability and extensibility
- metaprogramming with higher order functions
- AI: tell LLM to use immutability except when necessary for performance or clarity
Effective Python