aboutsummaryrefslogtreecommitdiff
path: root/hidden/2020-05-22-functions-that-go-backwards.md
blob: cebd1f1e01ec38e04ee897d17e79865a434c4330 (plain) (blame)
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
---
title: Functions That Go Backwards
route: /functions-that-go-backwards
date: 2020-05-22
description: A gentle introduction to logic programming with Prolog, exploring how to define programs in terms of relations instead of input and output.
---

In most programming languages, we may author a function that takes an **input** and produces some **output**. 

```js
console.log(getTrafficLightColor("green", "wait"))
// => "yellow"
```

We can say that our code answers the following question: **Given a light color and an action, what happens to the color of the light?**

And it answers it well! We can test its behavior, give it some types, whatever we want - to get a solid answer. We can compose these questions to ask more interesting ones:

**If I wait twice at a green light, what will the color of the light be?**

```js
console.log(
	getTrafficLightColor(
		getTrafficLightColor("green", "wait"),
		"wait"
	)
) // => "red"
```

But if I asked another question: **What color should the light be such that if I wait, it will turn red?**

We're no longer asking a question in terms of its **input** (which is easy!), but its **output** (which makes us scratch our heads).

## Logically speaking

We'll use a fun "logic programming" language called [Prolog](https://en.wikipedia.org/wiki/Prolog) (you can grab [SWI-Prolog](https://www.swi-prolog.org/) for free) to explore this concept.

Instead of writing a function in terms of its inputs, we write _relationships_ between input and output. An example of such a relation is **a valid transition for our traffic light turns `green` with a `wait` action into `yellow`**.

All we care about are the `green`, `wait`, and `yellow` (these are known as "atoms" in Prolog), and we can use them to build a "**fact**".

```prolog
transition(green, wait, yellow).
```

We can choose whatever order we want as long as we're consistent. We'll go with the one above since it matches the prose we translated into Prolog.

Let's write a few more of these in a file called `fsm.prolog`.

```prolog
% fsm.prolog
transition(green, wait, yellow).
transition(yellow, wait, red).
transition(red, wait, green).
```

We can then load up `swipl` in our terminal and test it out. After we load our facts with `[fsm].` we can begin "querying" them. 

```prolog
?- [fsm]. 	% load fsm.prolog
?- transition(green, wait, yellow).
true.
?- transition(red, wait, yellow).
false.
```

Our first query `transition(green, wait, yellow).` is a fact, because we defined it as such on the first line of `fsm.prolog`. Prolog tells us "true" - the thing we asked is `true`.

The second query, `transition(red, wait, yellow)` does not appear in our database, so it is `false`.

Of course, Prolog can do much more than recall facts that we already entered! The magic happens when we query with variables.

```prolog
?- transition(green, wait, Color).
Color = yellow.
```

By using a variable (we just need to begin a word with an uppercase letter), we're now asking Prolog to fill in the blank for us through a process called "**unification**." Which `Color` does `transition(green, wait, Color)` result in a fact? `yellow`!

Better yet, we can put this variable **wherever we want**. Our original question: **What color should the light be such that if I wait, it will turn red?** can be queried like so.

```prolog
?- transition(X, wait, red).
X = yellow.
```

Because transition does not work on input and output, we can swap 'em as we please.

```prolog
?- transition(X, wait, purple).
false.
```

Rather unfortunately, the traffic light we invented can never turn purple.

How about representing *multiple* transitions? We can join queries with a comma to ask that Prolog fills in the blanks to satisfy both. Let's start at `green` and `wait` twice.

```prolog
?- transition(green, wait, State1), 
|    transition(State1, wait, Final).
State1 = yellow,
Final = red.
```

We left two blanks, `State1` and `Final`, and Prolog filled em both to find that **waiting twice at a green light results in a red light**. Of course this works backwards for free.

```prolog
?- transition(Start, wait, State1),
|    transition(State1, wait, yellow).
Start = red,
State1 = green.
```

Want to wait twice for a yellow? You'll start at red.

## Improving relations

Just as we can define functions and compose them in ordinary programming languages, we can build "**rules**" out of facts and variables. For example, if we wanted an easier way to query for "waiting twice":

```prolog
% fsm.prolog
wait_twice(Start, End) :-
	transition(Start, wait, Middle),
	transition(Middle, wait, End).
```

We've defined `wait_twice` such that we'll `transition` once from `Start` to `Middle`, then `Middle` to `End`. We can use it like so:

```prolog
?- [fsm].
?- wait_twice(green, red).
true.
?- wait_twice(green, yellow).
false.
```

Waiting twice at a green does in fact give us a red light, while it is _not true_ that waiting twice at a green light gives us a yellow light. Similar to before, we can query with variables:

```prolog
?- wait_twice(green, X).
X = red.
?- wait_twice(X, yellow).
X = red.
?- wait_twice(X, purple).
false.
```

Let's dive into something a little more complicated, but much more rewarding. How about a rule that relates a start and end light color with a **list** of actions?

We'll call this rule `transition_multi` and define it recursively - starting with an empty list. If we have an initial `State` and an empty list of actions `[]`, what should our final state be? Right where we started.

```prolog
% fsm.prolog
transition_multi(State, [], State).
```

For the recursive step it may help to see how Prolog's "unification" works with lists.

```prolog
?- A = [1, 2, 3, 4].
A = [1, 2, 3, 4].
?- [A, B, C] = [1, 2, 3].
A = 1,
B = 2,
C = 3.
?- [A, B] = [1, 2, 3].
false.
```

We can set a list to a variable, or pattern-match on `[Var1, Var2, Etc...]` if the lengths are the same.

If we want to match the "rest" of the list, we can use the `|` operator.

```prolog
?- [A | Rest] = [1, 2, 3, 4].
A = 1,
Rest = [2, 3, 4].
?- [A, B | Rest] = [1, 2].
A = 1,
B = 2,
Rest = [].
```

So, back to `transition_multi`. Our definition looks pretty similar to `wait_twice`.

```prolog
% fsm.prolog
transition_multi(State, [], State).
transition_multi(State, [Action | Rest], End) :-
	transition(State, Action, Middle),
	transition_multi(Middle, Rest, End).
```

We unify the list of actions into `Action` and `Rest`, then transition from `State` to `Middle` before recursively `transition_multi`'ing from `Middle` to `End`.

Let's try it out.

```prolog
?- [fsm].
?- transition_multi(green, [wait, wait], red).
true
```

Groovy, so Prolog can confirm that `wait`ing twice at a green light gets us a red light. We can put a few variables in to flex a bit.

```prolog
?- transition_multi(yellow, [wait, wait, wait], X).
X = yellow
?- transition_multi(X, [wait, wait, wait, wait], yellow).
X = green .
```

## Many answers

How about the array in the middle? We can make that a variable too.

```prolog
?- transition_multi(green, Actions, red).
Actions = [wait, wait] <cursor>
```

Prolog tells us that `[wait, wait]` will work, then - interestingly - waits for input. We can hit `Enter` to get back to the original prompt, or hit semicolon `;` to **keep it going**.

```prolog
?- transition_multi(green, Actions, red).
Actions = [wait, wait] ;
Actions = [wait, wait, wait, wait, wait] <cursor>
```

It appears that not only do two `wait`s bring us from green to red, but so do five. This is because waiting three times brings us back to green (then two more for red). We can keep going with another press of `;`.

```
?- transition_multi(green, Actions, red).
Actions = [wait, wait] ;
Actions = [wait, wait, wait, wait, wait] ;
Actions = [wait, wait, wait, wait, wait, wait, wait, wait] ;
Actions = [wait, wait, wait, wait, wait, wait, wait, wait, wait|...] .
```

We'll be here forever (and ever) so we can just hit `.` to stop.

## What are you waiting for?

Up until now we've been dealing with a single action `wait`, which moves the traffic light from one color to another. Let's introduce two more.

```
% fsm.prolog
transition(green, wait, yellow).
transition(yellow, wait, red).
transition(red, wait, green).

transition(green, power_outage, blinking).
transition(yellow, power_outage, blinking).
transition(red, power_outage, blinking).

transition(blinking, power_on, red).
```

And query with them like so:

```
?- [fsm].
?- transition_multi(green, [power_outage], X).
X = blinking.
?- transition_multi(green, [wait, power_outage], X).
X = blinking.
```

Now we can answer more interesting questions such as **What two events can occur to take us from green to red?**.

```
?- transition_multi(green, [A, B], red).
A = B, B = wait <cursor>
```

We can wait twice, but Prolog asks if we want to keep going. We do, using `;`.

```
?- transition_multi(green, [A, B], red).
A = B, B = wait ;
A = power_outage,
B = power_on.
```

So, we can go from `green` to `red` with two waits, or a `power_outage` and `power_on`. What if we give ourselves four actions?

```
?- transition_multi(green, [A, B, C, D], red).
A = B, B = wait,
C = power_outage,
D = power_on ;
A = C, C = power_outage,
B = D, D = power_on.
```

We're left with two options:
* [wait, wait, power_outage, power_on]
* [power_outage, power_on, power_outage, power_on]

## Closing notes

Writing our program as a series of *relations* instead of instructions unlocks the ability to flip our code upside down. Not only can we feed it input and get output, but we can feed it "output" to determine what "input" we require to produce facts. 

When we combine this technique with more interesting data structures like lists, we can *generate* infinite sequences of inputs to get a desired output.

I hope you found this post enlightening, and encourage to fire up [SWI-Prolog](https://www.swi-prolog.org/) to see what other sorts of problems logic programming can help solve.

Until then, see you around [on twitter](https://twitter.com/jdan).
<!--stackedit_data:
eyJoaXN0b3J5IjpbLTExMjU4MTIxMDUsMjEwNDk1MDI5NCwtOT
M3MzE1MjQxLC03MTExMzIyODUsLTEwMzQ5OTEwMzAsMTM2NTMy
NDk3LC0xNTg2OTcwNjg3LDE4Mjg3MTAzNjQsLTk3MzA1NjU3NF
19
-->