|
| 1 | +--- |
| 2 | +title: 'Imperative, Functional, Functional Reactive: Do you know the difference?' |
| 3 | +date: 2025-02-16 |
| 4 | +tags: ['Swift', 'Combine', 'Functional Reactive Programming'] |
| 5 | +cover: |
| 6 | + image: 'images/cover.png' |
| 7 | + alt: 'Do You Know the Difference Between Imperative, Functional, and Reactive Programming?' |
| 8 | +--- |
| 9 | + |
| 10 | +# Paradigms |
| 11 | + |
| 12 | +## Imperative |
| 13 | + |
| 14 | +In the imperative approach, we have a sequence of instructions that describe step by step how the program's state is modified. |
| 15 | + |
| 16 | +Let's look on the example ⤵️ |
| 17 | + |
| 18 | +```swift |
| 19 | +var value = 0 |
| 20 | + |
| 21 | +func increment() { |
| 22 | + value += 1 // Mutating the state |
| 23 | +} |
| 24 | + |
| 25 | +print(value) // 0 |
| 26 | +increment() |
| 27 | +print(value) // 1 |
| 28 | +``` |
| 29 | +In the code we have a mutable variable `value` and a function `increment` that mutates the state (`value`). We first print the initial value, then increment it, and finally print the updated value. |
| 30 | + |
| 31 | +Simple, right? Let's jump into functional programming! |
| 32 | + |
| 33 | +## Functional |
| 34 | + |
| 35 | +What is functional programming? Is it only about writing functions? 🤔 |
| 36 | + |
| 37 | +Not really. |
| 38 | + |
| 39 | +Of course, functions are a foundation of functional programming, but they need to follow an important rule ⤵️ |
| 40 | + |
| 41 | +In functional programming, functions must not have side effects. |
| 42 | + |
| 43 | +What's the side effect? - A side effect occurs when a function modifies any state outside of its own scope. |
| 44 | + |
| 45 | +Looking at the imperative programming example, we can clearly see a side effect inside the increment function ⤵️ |
| 46 | + |
| 47 | +```swift {hl_lines=[4]} |
| 48 | +var value = 0 |
| 49 | + |
| 50 | +func increment() { |
| 51 | + value += 1 // Mutating the state (Side effect) |
| 52 | +} |
| 53 | + |
| 54 | +print(value) // 0 |
| 55 | +increment() |
| 56 | +print(value) // 1 |
| 57 | +``` |
| 58 | + |
| 59 | +How can we re-shape this code to align with functional programming principles? - we need to get rid of state mutation both from main program flow and inside the `increment` function. |
| 60 | + |
| 61 | +Let's fix the function first by passing the state (`value`) as an argument and returning the result of the operation as function's output. |
| 62 | + |
| 63 | +```swift |
| 64 | +var value = 0 |
| 65 | + |
| 66 | +func increment(_ value: Int) -> Int { |
| 67 | + value + 1 |
| 68 | +} |
| 69 | + |
| 70 | +print(value) // 0 |
| 71 | +value = increment(value) |
| 72 | +print(value) // 1 |
| 73 | +``` |
| 74 | + |
| 75 | +Now, `increment` is a pure function. It returns a new state by adding 1 to the input without mutating any external state. To manage state changes in functional programming, we create new values instead of modifying the old state. |
| 76 | + |
| 77 | +Having pure functions always returning the same output for a given input makes them easier to test and debug. |
| 78 | + |
| 79 | +Now we focus on mutability which functional programming aims to avoid. Instead of mutating the state - `value`, we transform the old state and return a new one. |
| 80 | + |
| 81 | +The code refactored to the functional form ⤵️ |
| 82 | + |
| 83 | +```swift |
| 84 | +func increment(_ value: Int) -> Int { |
| 85 | + value + 1 |
| 86 | +} |
| 87 | + |
| 88 | +let initialState = 0 |
| 89 | +let incrementedState = increment(initialState) |
| 90 | + |
| 91 | +print(initialState) // 0 |
| 92 | +print(incrementedState) // 1 |
| 93 | +``` |
| 94 | + |
| 95 | +By adding the `increment` method as an extension to `Int`, we can use method chaining to apply multiple transformations in a clean, readable way. This is a common practice in functional programming, known as function or method composition ⤵️ |
| 96 | + |
| 97 | +```swift |
| 98 | +extension Int { |
| 99 | + func increment() -> Int { |
| 100 | + self + 1 |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +let initialState = 0 |
| 105 | +let incrementedState = initialState |
| 106 | + .increment() |
| 107 | + .increment() |
| 108 | + |
| 109 | +print(initialState) // 0 |
| 110 | +print(incrementedState) // 2 |
| 111 | +``` |
| 112 | + |
| 113 | +## Functional Reactive |
| 114 | + |
| 115 | +functional reactive programming is a paradigm that combines functional programming with publisher and subscriber pattern. It emphasises streams of events carrying data that can be transformed along the way to the subscriber. FRP enables better handling of concurrency and asynchronous operations, making code more expressive. |
| 116 | + |
| 117 | +If we’d like to showcase the above example using FRP, it would look something like this ⤵️ |
| 118 | + |
| 119 | +```swift |
| 120 | +var incrementSubject = PassthroughSubject<Void, Never>() |
| 121 | +var valueSubject = CurrentValueSubject<Int, Never>(0) |
| 122 | + |
| 123 | +let disposableStream = Publishers.CombineLatest(incrementSubject, valueSubject) |
| 124 | + .map { _, value in value + 1 } |
| 125 | + .subscribe(valueSubject) |
| 126 | + |
| 127 | + |
| 128 | +print(valueSubject.value) // 0 |
| 129 | +incrementSubject.send() |
| 130 | +print(valueSubject.value) // 1 |
| 131 | +``` |
| 132 | + |
| 133 | +### Final Thoughts |
| 134 | + |
| 135 | +Having a deeper understanding of FRP, beyond just describing use cases like observing keyboard input to trigger requests, can truly make you stand out in an interview. The ability to explain the differences and principles behind Combine or RxSwift will position you as a more senior candidate in the eyes of the interviewer. |
| 136 | + |
| 137 | +# Quiz |
| 138 | + |
| 139 | +{{< include-quiz "imperative_functional_frp.md" >}} |
| 140 | + |
| 141 | +{{< footer >}} |
0 commit comments