Notes

Defunctionalization

Defunctionalization is the art of turning a functional program into a defunctionalized one. You can do this by first converting everything to CPS and then turning every continuation into an enum case. The captured variables are values of the enum case. You can also do this without CPS but replace similar calls with an enum + apply function. In order for this to work, all calls need to have the same type.

In essence, this is what actions in something like TEA (The Elm Architecture) or TCA represent: defunctionalized method calls (or callbacks). For example, instead of executing some code on a button handler, you return an action (enum value) which then gets interpreted separately. This can make testing and tracing easier.

You can also use this technique to serialize continuations or turn recursive programs into iterative ones. When combined with CPS you can turn a complicated recursive program into a state machine. For example, you could use this to take something like Wadler’s pretty printers and turn the (lazy Haskell) functional version into an iterative one.

We have used this at least twice in the Swift Talk backend: once to separate route parsing from interpreting (through an intermediate route enum) and once to defunctionalize tasks that are to be run at a later time (the enum captures the closure context).

You can think of SwiftUI view structs as defunctionalized view-building functions. This helps explain some behavior. For example, Color is a function that uses the environment as its input to produce a resolved color.

Techniques

You can create a big enum. What SwiftUI does instead is interesting as well: they create a protocol instead. This will not allow you to check the different cases easily (e.g. like you would do with a parser enum or regex enum) but it does allow you to have an apply method that takes in additional context (e.g. the environment or other things).