aboutsummaryrefslogtreecommitdiff
path: root/post/programming-as-art.typ
blob: ef80e334d71ec33671c4137f1388e45abc6e1fae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#import "/global/common.typ": *

#let doc = [
Why there is art in programming.

During my early days as a CS student, one of the first mind-blowing moments was
watching `Hello World!` getting printed out to the console thousands of times
in just two functional lines of code.

```python
for _ in range(1, 1001):
  print("Hello World!")
```

At the time it felt like having the
#extlink("https://harry-potter-compendium.fandom.com/wiki/Elder_Wand")[Elder Wand].

#fig("programming-as-art/wand.png", alt: "Dumbledore with Elder Wand pic")

But there was more to our lesson. The TA then asks us to put our newly found
power to use by computing the sum of 1 to 100. Of course, it was a natural
application of what we had just done earlier:

```python
sum = 0
for i in range(1, 100+1):
  sum = sum + i
```

Sure enough we saw the answer `5050` in the console. But then the TA reminds us
that we are making our computers *work too hard*. In other words, the computer
needs to do one-hundred ADD instructions in order to make this computation
happen.

What if the number was a million? How well would our method scale?

Well then it would take a million ADD instructions. We call this scaling
_linearly_ with the input size. Later we would formalize this to $cal(O)(n)$
(pronounced: _Big Oh of N_).

The TA hinted that there is a better way, and that we already know of the
better way in math.

#fig("programming-as-art/teaching.png", alt: "Teaching meme")

$ S_n = sum_(i=1)^n i = 1 + 2 + ... + n = (n (n + 1)) / 2 $

With this we are no longer using loops, but a known mathematical fact about
sequences. If you don't belive me, see the
#extlink("https://letstalkscience.ca/educational-resources/backgrounders/gauss-summation")[proof].

Written as code:

```python
sum = n (n + 1) / 2
```

This one-liner solves our problem with just 3 (ADD, MULTIPLY, DIVIDE)
instructions. Crucially, it does not depend on the size of the input like our
previous solution, thus *no matter the input, it always takes 3 instructions to
compute!* This is a HUGE win!

#fig("programming-as-art/math.png", alt: "Math meme")

Later we would formalize this to $cal(O)(1)$, or _constant_ scaling.

#quote(block: true)[
    Yes, yes I know IRL the complier would optimize the loop solution such
    that it does not take N instructions but for the purposes of learning we
    were not allowed to depend on that.
]

Looking back at it now, both solutions are equally correct, and modern
compilers would optimize the first solution in the final instructions sent to
the cpu, such that any performance differences would be negligible. In other
words, the computer wouldn't acutally be _working so hard_.

Objectively, the first solution is more readable, and friendly to a new
observer than the second.

_Why then am I still so drawn to the second solution?_

The first solution reminds me of the saying "to a hammer, eveything looks like
a nail". Its a brute force approach. In comparison, the second solution is
using the exact tool for our particular problem. It somehow feels personalized
and dare I say _romantic_.

When I reflect on moments like this, it reminds me that there is emergent
elegance and beauty even in the seemingly arbitrary sequence of symbols that is
`code`.

#fig("programming-as-art/code.png", alt: "The Matrix code going by image")

Programming is not quite as _objective_ as people would have you believe. There
are trade-offs to each solution, and which solution you prefer relect on the
trade-offs you are willing to accept, which varies by the observer: much like
_art_.
]