WEBVTT

1
00:00:00.120 --> 00:00:04.360
<v Speaker 1>Have you ever looked at a massive code base, like

2
00:00:04.679 --> 00:00:08.359
<v Speaker 1>maybe when you inherited from a different team, or honestly

3
00:00:08.400 --> 00:00:10.720
<v Speaker 1>even when you wrote yourself six months ago, and you

4
00:00:10.759 --> 00:00:13.880
<v Speaker 1>just felt like it was this incredibly fragile house of cards.

5
00:00:14.080 --> 00:00:17.079
<v Speaker 2>Oh yeah, I mean it is a universally terrifying feeling

6
00:00:17.160 --> 00:00:18.600
<v Speaker 2>for anyone who writes software.

7
00:00:18.800 --> 00:00:20.960
<v Speaker 1>Right, you get a ticket to update I don't know,

8
00:00:21.679 --> 00:00:25.199
<v Speaker 1>simple user profile field, You make the change, and somehow

9
00:00:25.359 --> 00:00:29.679
<v Speaker 1>completely inexplicably, the payment processing system just goes down in production.

10
00:00:30.039 --> 00:00:32.439
<v Speaker 2>Yeah, and you realize that the different components of your

11
00:00:32.439 --> 00:00:36.320
<v Speaker 2>system are just so deeply entangled, like they're sharing state

12
00:00:36.439 --> 00:00:39.200
<v Speaker 2>modifying data behind the scenes, so you can literally no

13
00:00:39.280 --> 00:00:41.679
<v Speaker 2>longer predict what a single line of code is actually

14
00:00:41.719 --> 00:00:43.880
<v Speaker 2>going to do to the broader application exactly.

15
00:00:44.079 --> 00:00:46.280
<v Speaker 1>And that fear, you know, that fear of breathing too

16
00:00:46.320 --> 00:00:48.920
<v Speaker 1>hard on the keyboard, is exactly what we are tackling today.

17
00:00:49.000 --> 00:00:51.600
<v Speaker 1>So welcome to another deep dive.

18
00:00:51.679 --> 00:00:52.280
<v Speaker 2>Glad to be here.

19
00:00:52.479 --> 00:00:55.280
<v Speaker 1>Our mission today is to really help you understand a

20
00:00:55.399 --> 00:01:00.200
<v Speaker 1>radical premise that solves this exact problem of tangled fragile CD.

21
00:01:00.880 --> 00:01:03.960
<v Speaker 1>We are diving into the world of functional programming and

22
00:01:04.000 --> 00:01:07.680
<v Speaker 1>we're drawing our insights directly from the foundational text Functional

23
00:01:07.719 --> 00:01:11.200
<v Speaker 1>Programming in Skala by Paul Cisano and Runer Janisson.

24
00:01:11.920 --> 00:01:15.879
<v Speaker 2>Yeah, and it's such a remarkable book because it transcends

25
00:01:16.000 --> 00:01:20.560
<v Speaker 2>just teaching a specific programming language. It fundamentally forces you

26
00:01:20.760 --> 00:01:25.640
<v Speaker 2>to adopt a completely different paradigm for reasoning about software architecture.

27
00:01:25.719 --> 00:01:29.599
<v Speaker 1>Definitely, and we are going to like strip away the

28
00:01:29.680 --> 00:01:32.599
<v Speaker 1>intimidating academic jargon that usually surrounds this.

29
00:01:32.519 --> 00:01:35.159
<v Speaker 2>Whole topic, which is definitely needed. The jargon can be

30
00:01:35.159 --> 00:01:35.840
<v Speaker 2>a lot totally.

31
00:01:35.879 --> 00:01:38.120
<v Speaker 1>We're going to uncover exactly what pure functions are, how

32
00:01:38.120 --> 00:01:40.719
<v Speaker 1>they mechanically change the way we write code, and why

33
00:01:40.799 --> 00:01:43.799
<v Speaker 1>abandoning the things we've always relied on, like modifying variables

34
00:01:43.840 --> 00:01:48.680
<v Speaker 1>in place, actually gives you significantly more power, testability and modularity, which.

35
00:01:48.519 --> 00:01:51.719
<v Speaker 2>I mean often sounds wildly counterintuitive to developers trained in

36
00:01:51.760 --> 00:01:55.480
<v Speaker 2>standard object oriented or imperative styles. The immediate reaction is

37
00:01:55.560 --> 00:01:59.599
<v Speaker 2>usually how can I possibly write a useful application if

38
00:01:59.599 --> 00:02:02.599
<v Speaker 2>I am physically constrain from changing the state of anything.

39
00:02:02.680 --> 00:02:04.280
<v Speaker 1>Yeah, it sounds impossible at first.

40
00:02:04.400 --> 00:02:08.280
<v Speaker 2>It does, but as we'll see, that rigid constraint is

41
00:02:08.319 --> 00:02:10.680
<v Speaker 2>exactly the engine that drives modularity.

42
00:02:10.919 --> 00:02:14.439
<v Speaker 1>Okay, let's unpack this with a very practical real world

43
00:02:14.479 --> 00:02:18.280
<v Speaker 1>scenario from the text, because jumping straight into abstract math

44
00:02:18.479 --> 00:02:20.400
<v Speaker 1>is just the fastest way to lose.

45
00:02:20.240 --> 00:02:22.319
<v Speaker 2>The plot completely. Agree, let's ground it.

46
00:02:22.400 --> 00:02:25.039
<v Speaker 1>So we need to define the absolute core rule of

47
00:02:25.039 --> 00:02:28.439
<v Speaker 1>functional programming, which is no side effects. And the authors

48
00:02:28.439 --> 00:02:30.280
<v Speaker 1>actually start us off in a coffee shop.

49
00:02:30.439 --> 00:02:34.240
<v Speaker 2>Ah, yes, the classic buy coffee example. Yes. So, if

50
00:02:34.280 --> 00:02:37.520
<v Speaker 2>you are writing a standard imperative program for a point

51
00:02:37.560 --> 00:02:40.560
<v Speaker 2>of sale system, you might write a function called buy

52
00:02:40.599 --> 00:02:43.319
<v Speaker 2>coffee and inside that function block you instance, she ate

53
00:02:43.360 --> 00:02:45.599
<v Speaker 2>a new cup of coffee, and then you invariably have

54
00:02:45.759 --> 00:02:47.520
<v Speaker 2>a line of code that looks something like you know,

55
00:02:47.919 --> 00:02:51.280
<v Speaker 2>cc dot charge and then cup dout price.

56
00:02:51.159 --> 00:02:54.759
<v Speaker 1>Which from a purely sequential perspective seems completely logical. Right,

57
00:02:54.960 --> 00:02:57.479
<v Speaker 1>The customer buys the coffee, so the function tells the

58
00:02:57.479 --> 00:03:01.000
<v Speaker 1>credit card object to charge the card. Right, But that

59
00:03:01.120 --> 00:03:04.520
<v Speaker 1>cc dot charge line represents a side effect exactly, and

60
00:03:04.599 --> 00:03:07.680
<v Speaker 1>mechanically speaking, a side effect is when a function does

61
00:03:07.879 --> 00:03:12.000
<v Speaker 1>literally anything other than compute and return a result, like

62
00:03:12.199 --> 00:03:15.639
<v Speaker 1>in this case, reaching out to an external payment gateway,

63
00:03:16.080 --> 00:03:20.039
<v Speaker 1>or modifying a bank balance, or even just writing a

64
00:03:20.120 --> 00:03:21.439
<v Speaker 1>log to a database, and.

65
00:03:21.400 --> 00:03:25.199
<v Speaker 2>The authors expertly point out the massive architectural headache this creates,

66
00:03:25.400 --> 00:03:27.000
<v Speaker 2>starting basically with testing.

67
00:03:27.120 --> 00:03:29.080
<v Speaker 1>Oh, testing is a nightmare with this.

68
00:03:29.280 --> 00:03:31.599
<v Speaker 2>It is because if you want to write an automated

69
00:03:31.680 --> 00:03:34.879
<v Speaker 2>unit test for your bi cooffee logic, you cannot have

70
00:03:35.000 --> 00:03:38.000
<v Speaker 2>your test suite actually paying visa and drain a real

71
00:03:38.039 --> 00:03:41.360
<v Speaker 2>bank account every single time the CICD pipeline runs.

72
00:03:41.560 --> 00:03:44.039
<v Speaker 1>Yeah, obviously that would be very bad. So you're forced

73
00:03:44.080 --> 00:03:47.879
<v Speaker 1>to invent these complex, brittle mock objects, right.

74
00:03:47.759 --> 00:03:48.520
<v Speaker 2>Yep, exactly.

75
00:03:48.639 --> 00:03:50.840
<v Speaker 1>You have to use dependency injection to pass in a

76
00:03:50.840 --> 00:03:53.599
<v Speaker 1>fake credit card interface just to trick your own function

77
00:03:53.680 --> 00:03:56.199
<v Speaker 1>into running safely, all just to verify that a customer

78
00:03:56.280 --> 00:03:58.439
<v Speaker 1>can buy a coffee. It's an enormous amount.

79
00:03:58.199 --> 00:03:59.560
<v Speaker 2>Of overhead, it really is.

80
00:03:59.840 --> 00:04:02.479
<v Speaker 1>The text makes a really compelling case that testing isn't

81
00:04:02.479 --> 00:04:06.120
<v Speaker 1>even the fatal flaw here. It's actually reusability. Right.

82
00:04:06.400 --> 00:04:10.360
<v Speaker 2>Think about the business logic for a second. Imagine a customer, Alice,

83
00:04:10.639 --> 00:04:13.479
<v Speaker 2>walks into the shop and orders twelve coffees for her

84
00:04:13.599 --> 00:04:17.040
<v Speaker 2>entire office. Okay, if you want to reuse your existing

85
00:04:17.120 --> 00:04:20.800
<v Speaker 2>buy coffee function, your only real option in an imperative

86
00:04:20.839 --> 00:04:23.519
<v Speaker 2>system is to put it inside a loop that iterates

87
00:04:23.560 --> 00:04:24.319
<v Speaker 2>twelve times.

88
00:04:24.639 --> 00:04:27.720
<v Speaker 1>Oh wow, which means your application is going to reach

89
00:04:27.759 --> 00:04:31.680
<v Speaker 1>out over the network to the payment gateway twelve separate times.

90
00:04:31.959 --> 00:04:32.360
<v Speaker 2>Exactly.

91
00:04:32.399 --> 00:04:36.879
<v Speaker 1>You are authorizing twelve individual microtransactions and incurring twelve separate

92
00:04:36.920 --> 00:04:39.079
<v Speaker 1>processing fees for a single.

93
00:04:38.839 --> 00:04:41.480
<v Speaker 2>Order, which is just a complete architectural failure.

94
00:04:41.600 --> 00:04:44.759
<v Speaker 1>Yeah. That's terrible. So how does functional programming solve this?

95
00:04:45.160 --> 00:04:48.639
<v Speaker 1>It solves it by eliminating the site effect entirely, right, Yes.

96
00:04:48.800 --> 00:04:52.040
<v Speaker 2>The functional implementation of buy coffee does not charge the

97
00:04:52.120 --> 00:04:53.519
<v Speaker 2>cart at all.

98
00:04:53.639 --> 00:04:55.439
<v Speaker 1>Wait, really, it just doesn't charge it.

99
00:04:55.600 --> 00:04:59.600
<v Speaker 2>Nope. Instead, it computes and returns two things, the coffee

100
00:04:59.639 --> 00:05:02.839
<v Speaker 2>itself and a brand new charge data object that just

101
00:05:02.959 --> 00:05:04.079
<v Speaker 2>represents the transaction.

102
00:05:04.480 --> 00:05:08.800
<v Speaker 1>What's fascinating here is how this seemingly minor tweak completely

103
00:05:08.879 --> 00:05:10.600
<v Speaker 1>shifts your system's architecture.

104
00:05:10.800 --> 00:05:14.879
<v Speaker 2>It's massive. We are strictly separating the creation of a

105
00:05:15.000 --> 00:05:19.279
<v Speaker 2>charge from the interpretation, or like, the actual execution of

106
00:05:19.279 --> 00:05:22.000
<v Speaker 2>that charge by returning the charge as a mere value,

107
00:05:22.480 --> 00:05:25.920
<v Speaker 2>just data in memory. The function hasn't mutated the outside

108
00:05:25.920 --> 00:05:26.759
<v Speaker 2>world yet.

109
00:05:26.519 --> 00:05:29.519
<v Speaker 1>So I like to visualize it as getting a stack

110
00:05:29.600 --> 00:05:30.959
<v Speaker 1>of physical paper receipts.

111
00:05:31.079 --> 00:05:32.519
<v Speaker 2>Oh, that's a great way to look at it.

112
00:05:32.600 --> 00:05:34.920
<v Speaker 1>Right, Instead of swiping your card and draining your bank

113
00:05:34.920 --> 00:05:37.120
<v Speaker 1>account the second you take a sip of coffee, the

114
00:05:37.199 --> 00:05:40.199
<v Speaker 1>pure function just hands your receipt exactly and later in

115
00:05:40.240 --> 00:05:43.639
<v Speaker 1>the application flow, you can gather all twelve receipts for

116
00:05:43.720 --> 00:05:47.000
<v Speaker 1>Alice's order and pass them to a completely different function,

117
00:05:47.240 --> 00:05:51.720
<v Speaker 1>maybe something called a coalesce, which batches those receipts by

118
00:05:51.759 --> 00:05:55.000
<v Speaker 1>credit card number, adds up the total, and processes one

119
00:05:55.120 --> 00:05:57.600
<v Speaker 1>single combined payment to the external gateway.

120
00:05:58.000 --> 00:06:02.040
<v Speaker 2>That receipt analogy captures the mechanism perfectly. Thanks, And if

121
00:06:02.040 --> 00:06:04.920
<v Speaker 2>we connect this to the bigger picture of system design,

122
00:06:05.600 --> 00:06:09.399
<v Speaker 2>this is really the holy grail of functional architecture. You

123
00:06:09.439 --> 00:06:13.240
<v Speaker 2>strive to build applications with a pure core. A pure core, yeah,

124
00:06:13.279 --> 00:06:18.360
<v Speaker 2>and that core contains all your complex, highly testable business logic,

125
00:06:18.519 --> 00:06:22.920
<v Speaker 2>like batching those receipts and calculating totals. Then you push

126
00:06:22.959 --> 00:06:25.759
<v Speaker 2>all the side effects to a very thin, isolated layer

127
00:06:26.199 --> 00:06:29.360
<v Speaker 2>on the absolute outer edge of your application that actually

128
00:06:29.399 --> 00:06:30.920
<v Speaker 2>talks to the database or the bank.

129
00:06:31.120 --> 00:06:33.800
<v Speaker 1>Okay, so we've delayed the execution of the side effect.

130
00:06:33.879 --> 00:06:37.199
<v Speaker 1>But what does this mean mathematically? The authors attribute this

131
00:06:37.240 --> 00:06:42.959
<v Speaker 1>capability to a foundational property called referential transparency or RT right.

132
00:06:43.519 --> 00:06:47.240
<v Speaker 2>Referential transparency is the architectural guarantee that makes this whole

133
00:06:47.240 --> 00:06:51.399
<v Speaker 2>paradigm function okay. In mechanical terms, it means that evaluating

134
00:06:51.399 --> 00:06:54.360
<v Speaker 2>an expression doesn't secretly change the state of the world

135
00:06:54.439 --> 00:06:58.399
<v Speaker 2>around it. A function called is mathematically and practically equivalent

136
00:06:58.439 --> 00:06:59.399
<v Speaker 2>to its return value.

137
00:06:59.439 --> 00:07:01.839
<v Speaker 1>So what is this that's all mean? Like to ground this,

138
00:07:02.040 --> 00:07:05.120
<v Speaker 1>the book contrasts two standard data types you'd find in

139
00:07:05.240 --> 00:07:07.959
<v Speaker 1>Java or Scala to show where this property holds up

140
00:07:08.000 --> 00:07:09.439
<v Speaker 1>and where it completely collapses.

141
00:07:09.480 --> 00:07:12.120
<v Speaker 2>Yeah, they compare the standard immutable string with the mutable

142
00:07:12.120 --> 00:07:15.040
<v Speaker 2>string builder class. So if you have an immutable string,

143
00:07:15.399 --> 00:07:18.800
<v Speaker 2>let's say variable x equals hello, and you append the

144
00:07:18.800 --> 00:07:22.000
<v Speaker 2>word world to it, the compiler gives you a brand

145
00:07:22.040 --> 00:07:25.560
<v Speaker 2>new string, a completely new one, exactly the original variable

146
00:07:25.800 --> 00:07:30.480
<v Speaker 2>x remains perfectly untouched. The operation is referentially transparent.

147
00:07:31.000 --> 00:07:33.560
<v Speaker 1>But if you use string builder, which is designed to

148
00:07:33.560 --> 00:07:36.319
<v Speaker 1>be mutated in place for performance reasons, and you call

149
00:07:36.360 --> 00:07:39.959
<v Speaker 1>it a pend method, it physically alters the existing object

150
00:07:39.959 --> 00:07:42.839
<v Speaker 1>in memory, like it destroys the previous state.

151
00:07:42.759 --> 00:07:45.360
<v Speaker 2>And that destroys referential transparency.

152
00:07:45.480 --> 00:07:45.839
<v Speaker 1>Yeah.

153
00:07:46.079 --> 00:07:49.199
<v Speaker 2>Imagine you have a variable y pointing to a string builder.

154
00:07:49.879 --> 00:07:53.800
<v Speaker 2>If you substitute the expression y dot append a into

155
00:07:53.800 --> 00:07:57.000
<v Speaker 2>two different places in your application logic, you will get

156
00:07:57.079 --> 00:07:58.639
<v Speaker 2>drastically different results.

157
00:07:58.319 --> 00:08:01.759
<v Speaker 1>Because the first call mutated underlying state exactly.

158
00:08:01.360 --> 00:08:03.720
<v Speaker 2>So the second call is now operating on a corrupted,

159
00:08:04.000 --> 00:08:05.319
<v Speaker 2>fundamentally different object.

160
00:08:05.439 --> 00:08:07.800
<v Speaker 1>Okay, I see the appeal of local reasoning. But here's

161
00:08:07.839 --> 00:08:09.680
<v Speaker 1>where it gets really interesting for me, and I have

162
00:08:09.720 --> 00:08:10.439
<v Speaker 1>to push back a bit.

163
00:08:10.519 --> 00:08:11.040
<v Speaker 2>Sure, go ahead.

164
00:08:11.279 --> 00:08:15.120
<v Speaker 1>If we are locked inside this pure core, right, this

165
00:08:15.240 --> 00:08:19.480
<v Speaker 1>sterile pipeline, where we are physically forbidden from mutating standard

166
00:08:19.519 --> 00:08:22.759
<v Speaker 1>state variables, how do we actually compute anything?

167
00:08:22.879 --> 00:08:23.839
<v Speaker 2>That is the big question.

168
00:08:24.040 --> 00:08:26.240
<v Speaker 1>I mean, we can't even write a standard WI loop

169
00:08:26.279 --> 00:08:29.920
<v Speaker 1>with a counter variable that increments because incrementing mutates the counter.

170
00:08:30.839 --> 00:08:33.559
<v Speaker 1>Aren't we just making our jobs exponentially harder for the

171
00:08:33.600 --> 00:08:35.000
<v Speaker 1>sake of mathematical theory.

172
00:08:35.399 --> 00:08:38.559
<v Speaker 2>It is honestly the most common friction point for developers

173
00:08:38.600 --> 00:08:42.360
<v Speaker 2>transitioning to this style. But the payoff is that concept

174
00:08:42.360 --> 00:08:45.799
<v Speaker 2>of local reasoning. Okay, when your functions are completely pure,

175
00:08:46.039 --> 00:08:49.600
<v Speaker 2>they act as sealed black boxes. Contamination from one part

176
00:08:49.639 --> 00:08:52.759
<v Speaker 2>of the app literally cannot physically leak into another.

177
00:08:52.559 --> 00:08:55.159
<v Speaker 1>Because there's no shared mutable state exactly.

178
00:08:55.360 --> 00:08:57.200
<v Speaker 2>You never have to mentally simulate the state of the

179
00:08:57.360 --> 00:09:00.399
<v Speaker 2>entire system to understand a single function. Yeah, you pass

180
00:09:00.480 --> 00:09:02.360
<v Speaker 2>data in, you get data.

181
00:09:02.200 --> 00:09:03.200
<v Speaker 1>Out, simple as that.

182
00:09:03.519 --> 00:09:05.519
<v Speaker 2>But to answer your question about how we actually iterate

183
00:09:05.639 --> 00:09:09.519
<v Speaker 2>without mutable counters, we have to leverage recursion, right.

184
00:09:09.399 --> 00:09:12.039
<v Speaker 1>So instead of a wild loop that mutates a local variable,

185
00:09:12.279 --> 00:09:15.559
<v Speaker 1>the text shows how to implement tail recursive functions. You

186
00:09:15.600 --> 00:09:19.360
<v Speaker 1>write a local helper function, usually named go or loop

187
00:09:19.440 --> 00:09:22.759
<v Speaker 1>by convention, and instead of changing an index variable, you

188
00:09:22.879 --> 00:09:26.000
<v Speaker 1>just invoke the helper function again, passing the new computed

189
00:09:26.000 --> 00:09:27.360
<v Speaker 1>state forward as an argument.

190
00:09:27.519 --> 00:09:31.000
<v Speaker 2>Exactly. To iterate over an array, instead of mutating an

191
00:09:31.039 --> 00:09:33.720
<v Speaker 2>index from zero to one, you just call loop one,

192
00:09:34.120 --> 00:09:37.039
<v Speaker 2>which processes the element and then calls loop two and

193
00:09:37.080 --> 00:09:37.559
<v Speaker 2>so forth.

194
00:09:37.720 --> 00:09:41.080
<v Speaker 1>But hold on, if we use recursion for every single

195
00:09:41.120 --> 00:09:44.320
<v Speaker 1>loop in our entire application, aren't we going to blow

196
00:09:44.399 --> 00:09:45.360
<v Speaker 1>up the call stack?

197
00:09:45.759 --> 00:09:45.919
<v Speaker 2>Ah?

198
00:09:46.000 --> 00:09:48.679
<v Speaker 1>Yes, Like anyone who has written a recursive function that

199
00:09:48.720 --> 00:09:53.399
<v Speaker 1>goes too deep knows about the dreaded stack over flower error.

200
00:09:53.559 --> 00:09:56.519
<v Speaker 2>This raises an important question, and it's a critical mechanical

201
00:09:56.600 --> 00:09:59.639
<v Speaker 2>hurdle the author's address immediately. This is where the language

202
00:09:59.639 --> 00:10:02.039
<v Speaker 2>compile itself has to step in and do the heavy

203
00:10:02.080 --> 00:10:05.279
<v Speaker 2>lifting via a feature called tail call elimination.

204
00:10:05.679 --> 00:10:08.240
<v Speaker 1>Okay, walk me through what that actually does physically in

205
00:10:08.279 --> 00:10:09.080
<v Speaker 1>the memory stack.

206
00:10:09.399 --> 00:10:12.840
<v Speaker 2>So, when a standard recursive function calls itself, the computer

207
00:10:12.919 --> 00:10:16.200
<v Speaker 2>has to keep the previous functions memory frame open because

208
00:10:16.240 --> 00:10:19.159
<v Speaker 2>it still has work to do when the recursion finally returns.

209
00:10:18.840 --> 00:10:21.240
<v Speaker 1>Right, it's waiting for the answer exactly.

210
00:10:21.000 --> 00:10:23.840
<v Speaker 2>And that's what causes the stack overflow. But if the

211
00:10:23.879 --> 00:10:26.600
<v Speaker 2>recursive call is in the tail position, meaning it is

212
00:10:26.639 --> 00:10:30.360
<v Speaker 2>the absolute final instruction the function executes before returning of

213
00:10:30.360 --> 00:10:34.559
<v Speaker 2>the scaler, the compiler recognizes that the current memory frame

214
00:10:34.679 --> 00:10:37.799
<v Speaker 2>is no longer needed. Oh wow, Yeah, it physically drops

215
00:10:37.799 --> 00:10:41.080
<v Speaker 2>the old frame and reuses that same memory space for

216
00:10:41.120 --> 00:10:42.200
<v Speaker 2>the next recursive call.

217
00:10:42.360 --> 00:10:45.519
<v Speaker 1>So under the hood, the compiler is silently translating our

218
00:10:45.639 --> 00:10:49.840
<v Speaker 1>pure mathematical recursion into the exact same highly efficient, low

219
00:10:49.919 --> 00:10:52.759
<v Speaker 1>level machine code as an imperative wile loop.

220
00:10:52.919 --> 00:10:56.039
<v Speaker 2>Precisely, you get the safety of immutable state without paying

221
00:10:56.080 --> 00:10:57.279
<v Speaker 2>any performance penalty.

222
00:10:57.440 --> 00:11:00.879
<v Speaker 1>Okay, so tail call optimization saves us from stafflows. That

223
00:11:00.919 --> 00:11:04.159
<v Speaker 1>makes sense, But recursion alone only gets us so far. True,

224
00:11:04.279 --> 00:11:07.639
<v Speaker 1>if we're stuck writing rigid, single purpose recursive functions for

225
00:11:07.799 --> 00:11:11.840
<v Speaker 1>every tiny task, we haven't really gained much flexibility to

226
00:11:11.879 --> 00:11:14.799
<v Speaker 1>build dynamic applications without side effects. We have to figure

227
00:11:14.840 --> 00:11:17.360
<v Speaker 1>out a way to pass the logic itself around, which

228
00:11:17.399 --> 00:11:19.360
<v Speaker 1>brings us to higher order functions.

229
00:11:19.639 --> 00:11:23.159
<v Speaker 2>This is a massive leaping capability In functional programming. Functions

230
00:11:23.159 --> 00:11:24.120
<v Speaker 2>are first class.

231
00:11:23.960 --> 00:11:27.480
<v Speaker 1>Values, meaning they're treated just like data exactly.

232
00:11:27.720 --> 00:11:30.120
<v Speaker 2>That means a function is treated exactly the same as

233
00:11:30.120 --> 00:11:32.960
<v Speaker 2>a string or an integer. You can store a function

234
00:11:33.000 --> 00:11:35.720
<v Speaker 2>in a variable, you can return a function from a function,

235
00:11:36.120 --> 00:11:38.960
<v Speaker 2>and crucially, you can pass a function as an argument

236
00:11:39.279 --> 00:11:40.159
<v Speaker 2>to another function.

237
00:11:40.480 --> 00:11:44.159
<v Speaker 1>The book illustrates this with a function called format result. Right,

238
00:11:44.240 --> 00:11:47.360
<v Speaker 1>instead of hard coding one function that formats the absolute

239
00:11:47.440 --> 00:11:50.480
<v Speaker 1>value of a number for a user interface, and a

240
00:11:50.519 --> 00:11:54.080
<v Speaker 1>completely separate function that formats the factorial of a number,

241
00:11:54.440 --> 00:11:57.799
<v Speaker 1>you write a single generalized format result function, and you.

242
00:11:57.799 --> 00:12:01.639
<v Speaker 2>Pass the mathematical logic the absolute value function itself or

243
00:12:01.679 --> 00:12:05.320
<v Speaker 2>the factorial function as an argument directly into format results.

244
00:12:05.360 --> 00:12:07.320
<v Speaker 1>And you don't even have to define the function beforehand,

245
00:12:07.399 --> 00:12:09.519
<v Speaker 1>do you. Nope, you can pass what the book calls

246
00:12:09.559 --> 00:12:13.080
<v Speaker 1>anonymous functions right in the execution line. You just write

247
00:12:13.080 --> 00:12:16.240
<v Speaker 1>the logic like x erro x equals nine directly in

248
00:12:16.279 --> 00:12:19.320
<v Speaker 1>the argument list. The logic itself becomes portable data.

249
00:12:19.399 --> 00:12:22.679
<v Speaker 2>If we connect this to the broader goals of software engineering,

250
00:12:23.240 --> 00:12:26.240
<v Speaker 2>higher order functions allow us to abstract away the structural

251
00:12:26.279 --> 00:12:30.039
<v Speaker 2>skeleton of our programs from the specific domain details. We

252
00:12:30.080 --> 00:12:32.480
<v Speaker 2>stop writing boilerplate loops entirely.

253
00:12:32.279 --> 00:12:35.639
<v Speaker 1>Which is amazing. But if we're passing logic around, we

254
00:12:35.720 --> 00:12:39.240
<v Speaker 1>want it to be as widely applicable as possible. Hard

255
00:12:39.240 --> 00:12:42.279
<v Speaker 1>Coded data types restrict that they do. Like if I

256
00:12:42.320 --> 00:12:45.200
<v Speaker 1>write a highly optimized recursive function to search and array,

257
00:12:45.240 --> 00:12:47.399
<v Speaker 1>I don't want to rewrite it just because my data

258
00:12:47.480 --> 00:12:49.799
<v Speaker 1>changed from an integer to a string. We have to

259
00:12:49.879 --> 00:12:50.879
<v Speaker 1>let the type system do.

260
00:12:50.879 --> 00:12:55.200
<v Speaker 2>The work, and that leads directly into polymorphic functions or

261
00:12:55.240 --> 00:12:56.720
<v Speaker 2>abstracting over types.

262
00:12:57.159 --> 00:13:01.240
<v Speaker 1>The book walks through generalizing a function called fine. Initially,

263
00:13:01.279 --> 00:13:04.519
<v Speaker 1>it's strictly typed to search through an array of strings, right,

264
00:13:04.559 --> 00:13:07.679
<v Speaker 1>But the actual mechanical logic of searching an array starting

265
00:13:07.679 --> 00:13:10.639
<v Speaker 1>at index zero, checking at tendition, and moving to the

266
00:13:10.679 --> 00:13:13.120
<v Speaker 1>next index has absolutely nothing to do with strings.

267
00:13:13.360 --> 00:13:16.720
<v Speaker 2>The mechanism is identical whether you are searching strings, integers,

268
00:13:17.080 --> 00:13:19.440
<v Speaker 2>or complex nested user objects.

269
00:13:19.639 --> 00:13:22.720
<v Speaker 1>So we strip the specific type out. Instead of array string,

270
00:13:22.759 --> 00:13:25.360
<v Speaker 1>we declare the function to expect an ARRAYA, where A

271
00:13:25.600 --> 00:13:29.200
<v Speaker 1>is just a polymorphic placeholder for any conceivable type exactly,

272
00:13:29.240 --> 00:13:31.440
<v Speaker 1>and we pass in a higher order function that takes

273
00:13:31.440 --> 00:13:33.679
<v Speaker 1>an A and returns a boolean telling us if it

274
00:13:33.720 --> 00:13:34.399
<v Speaker 1>found a match.

275
00:13:34.759 --> 00:13:36.960
<v Speaker 2>And this is where the text gets into some truly

276
00:13:37.080 --> 00:13:38.240
<v Speaker 2>mind bending territory.

277
00:13:38.360 --> 00:13:39.000
<v Speaker 1>Oh yeah.

278
00:13:39.039 --> 00:13:41.720
<v Speaker 2>When you abstract away both the specific data types and

279
00:13:41.759 --> 00:13:46.039
<v Speaker 2>the specific operations, you start encountering these fascinating logic puzzles

280
00:13:46.159 --> 00:13:49.559
<v Speaker 2>where the compiler is so constrained that it practically rights

281
00:13:49.600 --> 00:13:50.639
<v Speaker 2>the application for you.

282
00:13:50.840 --> 00:13:54.399
<v Speaker 1>Yes, the partial one function puzzle. Let's break down the

283
00:13:54.399 --> 00:13:56.840
<v Speaker 1>signature careful because it is wild to think about it

284
00:13:56.919 --> 00:13:59.679
<v Speaker 1>really is. The signature is deaf partial one. And then

285
00:13:59.720 --> 00:14:02.519
<v Speaker 1>it takes types A, B, and C. It takes an

286
00:14:02.600 --> 00:14:05.720
<v Speaker 1>argument a of type A and a function F that

287
00:14:05.799 --> 00:14:07.799
<v Speaker 1>takes an A and a B and returns a C,

288
00:14:08.279 --> 00:14:10.159
<v Speaker 1>and it returns a function from B to C.

289
00:14:10.360 --> 00:14:12.759
<v Speaker 2>Okay, let's translate that out of code and into English.

290
00:14:12.759 --> 00:14:13.120
<v Speaker 1>Please do.

291
00:14:13.240 --> 00:14:16.480
<v Speaker 2>We're defining a function that operates on three entirely unknown

292
00:14:16.559 --> 00:14:20.360
<v Speaker 2>types A, B, and C. The function receives two arguments, first,

293
00:14:20.399 --> 00:14:23.240
<v Speaker 2>a value of type A. Second a function. Let's call

294
00:14:23.279 --> 00:14:25.639
<v Speaker 2>it F, which requires both an A and a B

295
00:14:25.799 --> 00:14:29.159
<v Speaker 2>to execute and produce a C, and our ultimate goal,

296
00:14:29.519 --> 00:14:31.559
<v Speaker 2>we must return a brand new function that takes a

297
00:14:31.559 --> 00:14:32.639
<v Speaker 2>B and returns a C.

298
00:14:32.960 --> 00:14:35.960
<v Speaker 1>So the puzzle is, how do you write the actual

299
00:14:36.000 --> 00:14:39.559
<v Speaker 1>execution body of this function when you have zero context

300
00:14:39.559 --> 00:14:43.639
<v Speaker 1>about what AB or C represent. They aren't database records,

301
00:14:43.679 --> 00:14:45.120
<v Speaker 1>they are complete unknowns.

302
00:14:45.440 --> 00:14:48.039
<v Speaker 2>What's fascinating here is that the lack of information is

303
00:14:48.080 --> 00:14:50.960
<v Speaker 2>your greatest asset. How So, because we only know the

304
00:14:51.000 --> 00:14:54.879
<v Speaker 2>generic types and absolutely nothing about the domain, the universe

305
00:14:54.919 --> 00:14:59.120
<v Speaker 2>of possible implementations shrinks down to a single point. There

306
00:14:59.200 --> 00:15:03.159
<v Speaker 2>is literally on one mathematically possible way to write the

307
00:15:03.200 --> 00:15:05.039
<v Speaker 2>code that the compiler will accept.

308
00:15:05.240 --> 00:15:08.399
<v Speaker 1>The authors use an incredibly intuitive analogy for this.

309
00:15:08.600 --> 00:15:09.440
<v Speaker 2>Oh the carrot one.

310
00:15:09.559 --> 00:15:11.279
<v Speaker 1>Yeah. They say, if I can give you a carrot

311
00:15:11.320 --> 00:15:13.440
<v Speaker 1>for an apple and a banana, and you already gave

312
00:15:13.440 --> 00:15:15.039
<v Speaker 1>me an apple, you just have to give me a

313
00:15:15.080 --> 00:15:16.879
<v Speaker 1>banana and I'll give you a carrot exactly.

314
00:15:17.240 --> 00:15:19.879
<v Speaker 2>Our required output is a function that takes a type B.

315
00:15:20.600 --> 00:15:23.080
<v Speaker 2>So we start by writing an anonymous function that accepts

316
00:15:23.120 --> 00:15:27.879
<v Speaker 2>a parameter B. Okay, Now, inside that anonymous functions execution block,

317
00:15:28.360 --> 00:15:31.120
<v Speaker 2>we have access to both B and the A that

318
00:15:31.200 --> 00:15:34.879
<v Speaker 2>was passed in earlier preserved in enclosure. What is the

319
00:15:34.919 --> 00:15:36.799
<v Speaker 2>only thing in the universe we can do with an

320
00:15:36.840 --> 00:15:37.440
<v Speaker 2>A and a B.

321
00:15:37.840 --> 00:15:40.799
<v Speaker 1>We pass them both into the function F that we

322
00:15:40.919 --> 00:15:44.519
<v Speaker 1>also received as an argument yes, and F magically produces

323
00:15:44.559 --> 00:15:47.840
<v Speaker 1>the C that our anonymous function is required to return.

324
00:15:48.799 --> 00:15:52.360
<v Speaker 1>The types dictate the implementation so strictly that you literally

325
00:15:52.519 --> 00:15:53.799
<v Speaker 1>cannot write a bug.

326
00:15:54.080 --> 00:15:58.600
<v Speaker 2>It's profoundly important for anyone building large scale applications because

327
00:15:58.639 --> 00:16:02.720
<v Speaker 2>these abstract palling morphic functions say absolutely nothing about a

328
00:16:02.759 --> 00:16:05.919
<v Speaker 2>specific domain. They don't know about coffee cups or credit

329
00:16:05.960 --> 00:16:09.679
<v Speaker 2>cards or user profiles. They are universally reusable.

330
00:16:09.799 --> 00:16:10.879
<v Speaker 1>That makes total sense.

331
00:16:11.000 --> 00:16:14.600
<v Speaker 2>Programming in the large becomes indistinguishable from programming in the small.

332
00:16:14.799 --> 00:16:17.440
<v Speaker 2>You're just snapping perfectly typed puzzle pieces together.

333
00:16:17.519 --> 00:16:20.399
<v Speaker 1>Okay, I am entirely sold on the logic pipeline. We've

334
00:16:20.399 --> 00:16:24.080
<v Speaker 1>solved stateful loops without mutation by leveraging tail recursion, and

335
00:16:24.120 --> 00:16:27.840
<v Speaker 1>we've solved rigid code by leveraging higher order polymorphic functions.

336
00:16:27.840 --> 00:16:30.240
<v Speaker 1>We have, but what about the data we feed into

337
00:16:30.240 --> 00:16:34.120
<v Speaker 1>this pipeline? How do we actually store and process massive

338
00:16:34.159 --> 00:16:37.720
<v Speaker 1>collections of information? If absolutely everything in our system is

339
00:16:37.799 --> 00:16:38.799
<v Speaker 1>strictly immutable.

340
00:16:38.919 --> 00:16:42.159
<v Speaker 2>Ah. Right, This requires a major paradigm shift in how

341
00:16:42.200 --> 00:16:45.399
<v Speaker 2>we structure memory. We have to abandon standard arrays and

342
00:16:45.440 --> 00:16:49.600
<v Speaker 2>adopt functional data structures, primarily the singly linked list, which

343
00:16:49.600 --> 00:16:51.759
<v Speaker 2>the authors explore in depth in chapter three.

344
00:16:52.080 --> 00:16:55.559
<v Speaker 1>Yeah, the text builds this functional list completely from scratch.

345
00:16:55.559 --> 00:16:59.399
<v Speaker 1>It utilizes just two data constructors, nil, which represents an

346
00:16:59.399 --> 00:17:03.360
<v Speaker 1>empty list, and cons, which stands for construct correct.

347
00:17:03.519 --> 00:17:06.559
<v Speaker 2>A con cell holds two things, a single piece of

348
00:17:06.640 --> 00:17:09.319
<v Speaker 2>data the head of the list, and a pointer referencing

349
00:17:09.319 --> 00:17:11.880
<v Speaker 2>the rest of the list, the tail. So a simple

350
00:17:11.880 --> 00:17:14.960
<v Speaker 2>list containing the numbers one, two, and three is structurally

351
00:17:15.000 --> 00:17:18.200
<v Speaker 2>represented as cons one pointing to cons two pointing to

352
00:17:18.240 --> 00:17:19.920
<v Speaker 2>cons three pointing to nil.

353
00:17:20.359 --> 00:17:24.200
<v Speaker 1>Now, to traverse this nested structure, we can't use standardifel

354
00:17:24.279 --> 00:17:27.119
<v Speaker 1>statements or list dot getthead getter methods.

355
00:17:27.160 --> 00:17:27.599
<v Speaker 2>No, you can.

356
00:17:27.839 --> 00:17:30.359
<v Speaker 1>Instead, we use pattern matching. And I want to make

357
00:17:30.400 --> 00:17:33.079
<v Speaker 1>sure we explain how this mechanically differs from a standard

358
00:17:33.119 --> 00:17:33.839
<v Speaker 1>switch statement.

359
00:17:33.920 --> 00:17:36.880
<v Speaker 2>Yeah, that's an important distinction. Pattern matching is essentially a

360
00:17:36.920 --> 00:17:40.000
<v Speaker 2>mechanism for destructuring. It doesn't just check the type of

361
00:17:40.000 --> 00:17:43.680
<v Speaker 2>the object. The compiler actually reaches inside the data structure,

362
00:17:44.160 --> 00:17:47.559
<v Speaker 2>unpacks the internal bindings and injects them directly into your

363
00:17:47.559 --> 00:17:48.400
<v Speaker 2>local scope.

364
00:17:48.599 --> 00:17:52.279
<v Speaker 1>So when you write case cons ht, you aren't just

365
00:17:52.400 --> 00:17:55.440
<v Speaker 1>checking if the object is a cons In that exact

366
00:17:55.440 --> 00:17:59.039
<v Speaker 1>same line of code, the compiler physically rips the head

367
00:17:59.079 --> 00:18:02.200
<v Speaker 1>value out and assigns it to a local variable H,

368
00:18:02.240 --> 00:18:05.000
<v Speaker 1>and rips the tail reference out and assigns it to

369
00:18:05.039 --> 00:18:08.799
<v Speaker 1>a variable T. It eliminates all the boilerplate getter methods

370
00:18:08.839 --> 00:18:11.400
<v Speaker 1>we are used to writing in object oriented code.

371
00:18:11.519 --> 00:18:16.599
<v Speaker 2>It makes traversing complex recursive data structures incredibly succinct.

372
00:18:16.680 --> 00:18:17.440
<v Speaker 1>It really does.

373
00:18:17.640 --> 00:18:21.079
<v Speaker 2>But this usually triggers the biggest, most persistent objection to

374
00:18:21.119 --> 00:18:22.799
<v Speaker 2>functional programming.

375
00:18:22.319 --> 00:18:25.480
<v Speaker 1>Which is the memory footprint. Because if this linked list

376
00:18:25.559 --> 00:18:27.880
<v Speaker 1>is strictly immutable, and I have a list of ten

377
00:18:27.920 --> 00:18:30.799
<v Speaker 1>thousand user records and my application just needs to propen

378
00:18:30.960 --> 00:18:34.440
<v Speaker 1>one single new user to the front, doesn't creating a

379
00:18:34.519 --> 00:18:38.319
<v Speaker 1>new list mean the compiler has to physically duplicate all

380
00:18:38.319 --> 00:18:39.839
<v Speaker 1>ten thousand existing records.

381
00:18:40.000 --> 00:18:40.920
<v Speaker 2>It sounds like it would.

382
00:18:41.039 --> 00:18:44.359
<v Speaker 1>Doesn't that absolutely throttle the application's memory and performance.

383
00:18:44.480 --> 00:18:48.200
<v Speaker 2>It is a completely logical deduction if you are thinking imperatively,

384
00:18:48.680 --> 00:18:51.599
<v Speaker 2>But the answer is a definitive no due to an

385
00:18:51.680 --> 00:18:53.599
<v Speaker 2>architectural concept called data sharing.

386
00:18:53.920 --> 00:18:57.319
<v Speaker 1>But if x's is immutable, how does the new list

387
00:18:57.440 --> 00:19:00.640
<v Speaker 1>actually point to it without risking that the garbae collector

388
00:19:00.720 --> 00:19:02.559
<v Speaker 1>or another thread is going to lock it up or

389
00:19:02.559 --> 00:19:03.519
<v Speaker 1>read it incorrectly.

390
00:19:04.039 --> 00:19:07.240
<v Speaker 2>Think about the mechanics of the con structure. If you

391
00:19:07.279 --> 00:19:09.519
<v Speaker 2>have an existing list in memory, let's call it access,

392
00:19:09.759 --> 00:19:12.000
<v Speaker 2>and you want to propen the number one, you do

393
00:19:12.039 --> 00:19:16.160
<v Speaker 2>not copy axis. Okay, You simply instantiate a single microscopic

394
00:19:16.279 --> 00:19:19.599
<v Speaker 2>new cons object. You set it's head to one, and

395
00:19:19.640 --> 00:19:22.039
<v Speaker 2>you said it's tail pointer to point directly at the

396
00:19:22.079 --> 00:19:23.720
<v Speaker 2>existing memory address of X.

397
00:19:24.039 --> 00:19:26.200
<v Speaker 1>Wait. Really, so both the old list and the new

398
00:19:26.240 --> 00:19:29.960
<v Speaker 1>list are silently sharing the exact same ten thousand elements

399
00:19:30.000 --> 00:19:30.480
<v Speaker 1>in memory.

400
00:19:30.519 --> 00:19:34.720
<v Speaker 2>Precisely because functional data structures are persistent and completely immutable,

401
00:19:35.160 --> 00:19:36.640
<v Speaker 2>they are inherently thread safe.

402
00:19:36.759 --> 00:19:38.039
<v Speaker 1>Oh wow, it is.

403
00:19:38.039 --> 00:19:40.680
<v Speaker 2>One hundred percent safe to share references to them across

404
00:19:40.680 --> 00:19:43.640
<v Speaker 2>your entire application. You never ever have to worry that

405
00:19:43.720 --> 00:19:46.359
<v Speaker 2>a background thread or a rogue function is going to

406
00:19:46.680 --> 00:19:50.720
<v Speaker 2>maliciously or accidentally modify x's while you are reading from it.

407
00:19:50.799 --> 00:19:53.960
<v Speaker 1>This completely eliminates the need for defensive copying. It does

408
00:19:54.160 --> 00:19:58.039
<v Speaker 1>like in standard imperative programming, developers are constantly copying a

409
00:19:58.160 --> 00:20:01.720
<v Speaker 1>raise before passing them into third party libraries, just to

410
00:20:01.759 --> 00:20:03.839
<v Speaker 1>protect their internal state from being corrupted.

411
00:20:03.920 --> 00:20:08.079
<v Speaker 2>Exactly, that defensive copying is what actually causes massive memory

412
00:20:08.079 --> 00:20:10.400
<v Speaker 2>bloat in enterprise object oriented systems.

413
00:20:10.519 --> 00:20:11.880
<v Speaker 1>That makes so much sense.

414
00:20:11.680 --> 00:20:15.000
<v Speaker 2>In functional programming, because the entire architecture is locked down

415
00:20:15.039 --> 00:20:19.480
<v Speaker 2>and pure. You just pass lightweight pointers around. So ironically,

416
00:20:19.480 --> 00:20:23.799
<v Speaker 2>in large scale systems, this functional data sharing actually decreases

417
00:20:23.839 --> 00:20:26.400
<v Speaker 2>memory overhead and increases efficiency.

418
00:20:26.440 --> 00:20:29.680
<v Speaker 1>It's a brilliant subversion of expectations. And once you have

419
00:20:29.759 --> 00:20:33.559
<v Speaker 1>these immutable, persistent lists, you can process them by applying

420
00:20:33.599 --> 00:20:37.079
<v Speaker 1>those higher order functions we talked about earlier, specifically utilizing

421
00:20:37.119 --> 00:20:40.480
<v Speaker 1>generalization functions like fold right and fold left. Instead of

422
00:20:40.480 --> 00:20:43.160
<v Speaker 1>writing a custom loop to some a list of numbers

423
00:20:43.359 --> 00:20:46.240
<v Speaker 1>and another custom loop to multiply them, you just fold

424
00:20:46.279 --> 00:20:46.880
<v Speaker 1>the data.

425
00:20:46.640 --> 00:20:48.799
<v Speaker 2>Structure you pass in the logic for what to do

426
00:20:48.839 --> 00:20:52.079
<v Speaker 2>with each item, and the fold function effectively replaces every

427
00:20:52.160 --> 00:20:56.759
<v Speaker 2>CON's constructor with your provided logic, collapsing the entire nested

428
00:20:56.799 --> 00:20:59.519
<v Speaker 2>structure down into a single computed result.

429
00:21:00.000 --> 00:21:02.720
<v Speaker 1>I've covered a tremendous amount of architectural ground today.

430
00:21:02.960 --> 00:21:06.359
<v Speaker 2>We really have. It's a dense text, but incredibly rewarding.

431
00:21:06.720 --> 00:21:09.480
<v Speaker 1>Let's summarize the journey a bit. We started by looking

432
00:21:09.480 --> 00:21:12.960
<v Speaker 1>at a simple, seemingly innocent side effect charging a credit

433
00:21:13.000 --> 00:21:16.440
<v Speaker 1>card in a by coffee function, and we saw mechanically

434
00:21:16.559 --> 00:21:19.799
<v Speaker 1>how that entanglement leads directly to code that is a

435
00:21:19.920 --> 00:21:22.400
<v Speaker 1>nightmare to test and impossible to reuse.

436
00:21:22.640 --> 00:21:26.039
<v Speaker 2>And by making the deliberate architectural choice to simply return

437
00:21:26.119 --> 00:21:29.319
<v Speaker 2>a data value representing the charge rather than executing the

438
00:21:29.359 --> 00:21:32.720
<v Speaker 2>action immediately, we unlocked a completely pure core.

439
00:21:33.000 --> 00:21:37.039
<v Speaker 1>We saw how referential transparency guarantees that our expressions act

440
00:21:37.160 --> 00:21:40.839
<v Speaker 1>like architectural pipelines completely isolated from the outside world.

441
00:21:41.119 --> 00:21:45.640
<v Speaker 2>Yep. We learned how leveraging tail call elimination allows us

442
00:21:45.680 --> 00:21:49.839
<v Speaker 2>to write elegant recursive loops without blowing up the memory stack,

443
00:21:50.359 --> 00:21:53.880
<v Speaker 2>and how higher order polymorphic functions allow the compiler to

444
00:21:54.000 --> 00:21:58.720
<v Speaker 2>literally solve logic puzzles for us based strictly on type constraints.

445
00:21:59.319 --> 00:22:02.599
<v Speaker 1>And finally, we saw how functional data structures use pattern

446
00:22:02.680 --> 00:22:06.799
<v Speaker 1>matching to destructure data elegantly, and how data sharing eliminates

447
00:22:06.839 --> 00:22:10.000
<v Speaker 1>memory blut and thread safety issues without ever mutating a

448
00:22:10.039 --> 00:22:13.559
<v Speaker 1>single variable. It all ties together, It does that terrifying

449
00:22:13.680 --> 00:22:16.400
<v Speaker 1>fragile house of cards codebase we talked about at the beginning,

450
00:22:16.799 --> 00:22:22.160
<v Speaker 1>functional programming replaces it with interlocking, sterile, highly predictable pipelines.

451
00:22:22.480 --> 00:22:24.599
<v Speaker 2>And for you listening, it's worth taking a step back

452
00:22:24.640 --> 00:22:27.640
<v Speaker 2>and realizing that many of the modern best practices you

453
00:22:27.759 --> 00:22:32.920
<v Speaker 2>probably already use, like writing single responsibility functions, utilizing dependency injection,

454
00:22:33.119 --> 00:22:37.640
<v Speaker 2>or explicitly declaring variables as immutable constants, are fundamentally just

455
00:22:37.799 --> 00:22:39.400
<v Speaker 2>fragments of functional programming.

456
00:22:39.440 --> 00:22:40.440
<v Speaker 1>That's a really good point.

457
00:22:40.599 --> 00:22:44.200
<v Speaker 2>What this text does is take those intuitive best practices

458
00:22:44.599 --> 00:22:49.200
<v Speaker 2>to their absolute, rigorous, logical endpoint. It provides a mathematical

459
00:22:49.240 --> 00:22:54.240
<v Speaker 2>shortcut to writing highly modular code that is incredibly resistant

460
00:22:54.240 --> 00:22:54.799
<v Speaker 2>to bugs.

461
00:22:55.119 --> 00:22:58.839
<v Speaker 1>It is undeniably a steep learning curve to completely rewire

462
00:22:58.880 --> 00:23:01.720
<v Speaker 1>how you think about stay. But the view from the

463
00:23:01.720 --> 00:23:06.079
<v Speaker 1>top of that curve is crystal clear architecture. But before

464
00:23:06.079 --> 00:23:07.480
<v Speaker 1>we sign off, I want to leave you with a

465
00:23:07.519 --> 00:23:11.039
<v Speaker 1>final provocative thought. Ooh, okay, it's a puzzle hinted at

466
00:23:11.079 --> 00:23:12.960
<v Speaker 1>near the very end of our source text.

467
00:23:12.680 --> 00:23:15.519
<v Speaker 2>Today, ah, the issue with exceptions exactly.

468
00:23:15.799 --> 00:23:18.839
<v Speaker 1>The authors briefly mentioned that standard exceptions like throwing a

469
00:23:18.920 --> 00:23:22.480
<v Speaker 1>runtime error when an application encounters a problem should actually

470
00:23:22.480 --> 00:23:26.640
<v Speaker 1>only be used for fatal, unrecoverable system crashes, never for

471
00:23:26.759 --> 00:23:27.920
<v Speaker 1>routine control.

472
00:23:27.599 --> 00:23:31.119
<v Speaker 2>Flow, because if you analyze it mechanically, throwing an exception

473
00:23:31.279 --> 00:23:32.519
<v Speaker 2>is the ultimate side effect.

474
00:23:32.519 --> 00:23:33.880
<v Speaker 1>Wait, really it's a side effect.

475
00:23:34.039 --> 00:23:37.599
<v Speaker 2>Yes, it completely breaks referential transparency. If a function throws

476
00:23:37.599 --> 00:23:40.839
<v Speaker 2>an exception, you cannot cleanly substitute it with a return value.

477
00:23:40.920 --> 00:23:43.440
<v Speaker 2>Oh I say, it behaves exactly like a rogue go

478
00:23:43.480 --> 00:23:47.960
<v Speaker 2>to statement. It abruptly halts the execution pipeline and rips

479
00:23:48.000 --> 00:23:51.839
<v Speaker 2>the context out to some hidden, unpredictable catch block miles

480
00:23:51.880 --> 00:23:52.880
<v Speaker 2>away in the call stack.

481
00:23:53.000 --> 00:23:55.839
<v Speaker 1>That is wild. So here's the puzzle for you to ponder.

482
00:23:56.480 --> 00:23:58.759
<v Speaker 1>If throwing an error acts as a go to statement,

483
00:23:58.799 --> 00:24:03.000
<v Speaker 1>that breaks the absolute fundamental rule of pure functions. How

484
00:24:03.000 --> 00:24:06.759
<v Speaker 1>do pure functional programmers handle routine daily.

485
00:24:06.640 --> 00:24:08.240
<v Speaker 2>Errors that it's a great question.

486
00:24:08.119 --> 00:24:11.720
<v Speaker 1>Like if a user inputs invalid data, or a database

487
00:24:11.759 --> 00:24:15.680
<v Speaker 1>connection temporarily drops, or a file goes missing, how do

488
00:24:15.720 --> 00:24:18.240
<v Speaker 1>you handle those failures without ever throwing exception?

489
00:24:18.400 --> 00:24:21.720
<v Speaker 2>It forces you to completely rethink what an error actually

490
00:24:21.799 --> 00:24:23.680
<v Speaker 2>is in the context of your application domain.

491
00:24:23.720 --> 00:24:25.519
<v Speaker 1>So the next time you find yourself writing a standard

492
00:24:25.559 --> 00:24:28.440
<v Speaker 1>trycatch block, challenge yourself to mull that over. How could

493
00:24:28.480 --> 00:24:31.920
<v Speaker 1>you represent that application crash as a pure mathematical data value,

494
00:24:32.000 --> 00:24:34.839
<v Speaker 1>exactly like we did with the coffee shop credit card receipt.

495
00:24:35.240 --> 00:24:36.799
<v Speaker 1>Think about it and we will catch you on the

496
00:24:36.839 --> 00:24:37.720
<v Speaker 1>next deep dive.
