WEBVTT

1
00:00:00.080 --> 00:00:04.759
<v Speaker 1>Imagine you're curled up in your favorite chair right deeply

2
00:00:04.799 --> 00:00:06.759
<v Speaker 1>engrossed in this fantastic mystery novel.

3
00:00:06.799 --> 00:00:08.279
<v Speaker 2>Oh it sounds like a great afternoon.

4
00:00:08.359 --> 00:00:11.560
<v Speaker 1>Yeah, exactly. You're following the plot, picturing the characters. But

5
00:00:11.640 --> 00:00:15.279
<v Speaker 1>there's just one massive problem.

6
00:00:15.359 --> 00:00:16.199
<v Speaker 2>Okay, what is it?

7
00:00:16.320 --> 00:00:18.839
<v Speaker 1>Someone else is standing right next to you frantically flipping

8
00:00:18.839 --> 00:00:21.239
<v Speaker 1>the pages of your book back and forth while you're

9
00:00:21.280 --> 00:00:21.960
<v Speaker 1>trying to read it.

10
00:00:22.039 --> 00:00:24.839
<v Speaker 2>Oh wow, that sounds incredibly frustrating.

11
00:00:24.440 --> 00:00:26.879
<v Speaker 1>Right, I mean, one second you're on chapter three and

12
00:00:26.879 --> 00:00:28.640
<v Speaker 1>then boom, you're staring at the epilogue.

13
00:00:28.679 --> 00:00:30.760
<v Speaker 2>Yeah, that would drive me crazy, exactly.

14
00:00:31.199 --> 00:00:36.399
<v Speaker 1>And that chaotic, unpredictable, pull your hair out mess that

15
00:00:36.520 --> 00:00:41.039
<v Speaker 1>is exactly what happens inside your software when concurrent programs

16
00:00:41.240 --> 00:00:42.479
<v Speaker 1>share mutable state.

17
00:00:42.799 --> 00:00:46.920
<v Speaker 2>It really is the ultimate concurrency nightmare. You know, you

18
00:00:46.960 --> 00:00:49.640
<v Speaker 2>have one thread trying to read a sequence of data,

19
00:00:49.960 --> 00:00:53.119
<v Speaker 2>and then another thread is mutating or like physically changing

20
00:00:53.240 --> 00:00:56.039
<v Speaker 2>that exact same data right out from under you. Yeah,

21
00:00:56.119 --> 00:00:59.359
<v Speaker 2>and it's the root cause of so many unpredictable bugs.

22
00:01:00.200 --> 00:01:03.079
<v Speaker 1>Today's deep dive is all about stopping that page flipping.

23
00:01:03.600 --> 00:01:07.840
<v Speaker 1>We're exploring the surprisingly practical world of functional programming, but

24
00:01:08.599 --> 00:01:12.120
<v Speaker 1>we're doing it inside language. Most people associate strictly with

25
00:01:12.319 --> 00:01:16.280
<v Speaker 1>object oriented programming, which is C shark, right, and we're

26
00:01:16.319 --> 00:01:19.840
<v Speaker 1>pulling our insights from Enrico Banana's book Functional Programming in

27
00:01:19.920 --> 00:01:23.359
<v Speaker 1>C Sharp. The mission here is to basically act as

28
00:01:23.359 --> 00:01:26.359
<v Speaker 1>a shortcut for you, because you absolutely do not need

29
00:01:26.359 --> 00:01:28.959
<v Speaker 1>a PhD in mathematics to use these concepts.

30
00:01:29.120 --> 00:01:30.159
<v Speaker 2>No, you really don't.

31
00:01:30.319 --> 00:01:32.519
<v Speaker 1>We're going to look at how functional techniques can you know,

32
00:01:32.640 --> 00:01:37.159
<v Speaker 1>drastically reduce bugs, handle concurrency almost effortlessly, and just make

33
00:01:37.200 --> 00:01:38.879
<v Speaker 1>your code based so much more readable.

34
00:01:39.040 --> 00:01:41.560
<v Speaker 2>Yeah, and I think that's important because functional programming often

35
00:01:41.560 --> 00:01:45.799
<v Speaker 2>gets this unfair reputation for being overly academic.

36
00:01:46.079 --> 00:01:49.079
<v Speaker 1>Yeah, totally, like it's just Ivory Tower stuff exactly.

37
00:01:49.439 --> 00:01:52.280
<v Speaker 2>But when you actually apply it to real world line

38
00:01:52.319 --> 00:01:55.359
<v Speaker 2>of business applications in C sharp, it becomes this highly

39
00:01:55.480 --> 00:01:59.319
<v Speaker 2>pragmatic tool set. It really boils down to changing two

40
00:01:59.519 --> 00:02:02.079
<v Speaker 2>ingrained like how we treat our data and how we

41
00:02:02.120 --> 00:02:02.959
<v Speaker 2>treat our behavior.

42
00:02:03.200 --> 00:02:05.760
<v Speaker 1>Well, let's start with behavior then, because that ties right

43
00:02:05.799 --> 00:02:07.719
<v Speaker 1>into how we manage the flow of an application.

44
00:02:08.639 --> 00:02:09.360
<v Speaker 3>In c sharp.

45
00:02:09.759 --> 00:02:14.560
<v Speaker 1>Functions aren't just invisible blocks of code anymore. They are

46
00:02:14.560 --> 00:02:17.319
<v Speaker 1>what we call first class values, which means you can

47
00:02:17.360 --> 00:02:20.240
<v Speaker 1>take a function, assign it to a variable, or pass

48
00:02:20.280 --> 00:02:23.199
<v Speaker 1>it as an argument into another function, just like you

49
00:02:23.199 --> 00:02:26.120
<v Speaker 1>would with a regular string or an integer.

50
00:02:26.400 --> 00:02:29.240
<v Speaker 2>Yeah, and we use delegates and lambed expressions to do this,

51
00:02:29.639 --> 00:02:32.280
<v Speaker 2>and treating functions as data. It unlocks one of the

52
00:02:32.319 --> 00:02:34.319
<v Speaker 2>most foundational tools in functional.

53
00:02:33.960 --> 00:02:35.400
<v Speaker 1>Programming, a higher order function.

54
00:02:35.560 --> 00:02:38.439
<v Speaker 2>Exactly the higher order function people usually just shorten it

55
00:02:38.479 --> 00:02:42.199
<v Speaker 2>to HOF. HOF is simply any function that either takes

56
00:02:42.240 --> 00:02:46.400
<v Speaker 2>another function as an input parameter or returns a function

57
00:02:46.560 --> 00:02:47.360
<v Speaker 2>as its output.

58
00:02:47.639 --> 00:02:50.840
<v Speaker 1>Right, And thinking about this in real world terms, using

59
00:02:50.879 --> 00:02:54.520
<v Speaker 1>a HOF feels a lot like hiring a subcontractor oh

60
00:02:54.560 --> 00:02:58.039
<v Speaker 1>I like that? How so Well, take the lanq wear method,

61
00:02:58.080 --> 00:02:59.599
<v Speaker 1>which we use to filter lists all the time in

62
00:02:59.639 --> 00:03:02.800
<v Speaker 1>C sharp. In this scenario, where's the manager. It handles

63
00:03:02.800 --> 00:03:05.439
<v Speaker 1>all the tedious border plate stuff, setting up a loop,

64
00:03:05.599 --> 00:03:08.479
<v Speaker 1>managing the index. You know, all the mechanical parts.

65
00:03:08.360 --> 00:03:10.159
<v Speaker 2>Right, the stuff you don't want to write over and

66
00:03:10.199 --> 00:03:11.000
<v Speaker 2>over exactly.

67
00:03:11.199 --> 00:03:13.439
<v Speaker 1>Yeah, But the manager doesn't actually know what you're looking for,

68
00:03:13.800 --> 00:03:18.039
<v Speaker 1>so it subcontracts the decision making logic asking like should

69
00:03:18.039 --> 00:03:21.759
<v Speaker 1>I keep this specific item? It delegates that to the

70
00:03:21.800 --> 00:03:23.080
<v Speaker 1>function that you pass into it.

71
00:03:23.159 --> 00:03:25.879
<v Speaker 2>That is a perfect analogy. The division of labor is

72
00:03:25.960 --> 00:03:29.800
<v Speaker 2>so clear. The hof handles the mechanics and you supply

73
00:03:29.879 --> 00:03:31.199
<v Speaker 2>the unique business logic.

74
00:03:31.360 --> 00:03:31.560
<v Speaker 1>Yeah.

75
00:03:31.879 --> 00:03:36.280
<v Speaker 2>Another incredibly practical application for this is encapsulating set up

76
00:03:36.479 --> 00:03:41.680
<v Speaker 2>and tear down logic. Think about how often you interact

77
00:03:41.680 --> 00:03:44.120
<v Speaker 2>with the database. Oh, all the time, right, and you

78
00:03:44.159 --> 00:03:46.800
<v Speaker 2>always have to do the same dance. You acquire a connection,

79
00:03:47.120 --> 00:03:49.960
<v Speaker 2>open it, do your work, close it, and then dispose

80
00:03:50.000 --> 00:03:50.719
<v Speaker 2>of the resource.

81
00:03:50.840 --> 00:03:54.000
<v Speaker 1>Oh man, the endless using blocks You end up copying

82
00:03:54.000 --> 00:03:56.840
<v Speaker 1>and pasting those same five lines of code across like

83
00:03:56.960 --> 00:03:58.159
<v Speaker 1>fifty different repositors.

84
00:03:58.319 --> 00:04:00.680
<v Speaker 2>So repetitive. But with a higher order function, you can

85
00:04:00.759 --> 00:04:04.599
<v Speaker 2>just write a custom using function one single time. Oh right.

86
00:04:04.719 --> 00:04:08.000
<v Speaker 2>That one function handles opening the connection and disposing of

87
00:04:08.039 --> 00:04:10.800
<v Speaker 2>it in a finally block, and you just pass it

88
00:04:10.840 --> 00:04:13.240
<v Speaker 2>a function that represents the specific query you want to

89
00:04:13.280 --> 00:04:13.960
<v Speaker 2>run in the middle.

90
00:04:14.120 --> 00:04:18.560
<v Speaker 1>That's brilliant. It completely eliminates all that boilerplate duplication exactly.

91
00:04:18.759 --> 00:04:21.959
<v Speaker 1>So Okay, the manager handles the subcontractors, the code gets cleaner.

92
00:04:22.439 --> 00:04:26.240
<v Speaker 1>But even the best manager can't fix a job site

93
00:04:26.399 --> 00:04:29.600
<v Speaker 1>if the blueprints keep magically changing while the crew is

94
00:04:29.639 --> 00:04:30.120
<v Speaker 1>reading them.

95
00:04:30.240 --> 00:04:32.360
<v Speaker 2>Right back to the page flipping problem.

96
00:04:32.000 --> 00:04:34.519
<v Speaker 1>Exactly which brings us to this second habit.

97
00:04:34.600 --> 00:04:35.399
<v Speaker 3>We have to change.

98
00:04:35.519 --> 00:04:38.600
<v Speaker 1>We have to avoid state mutation, right.

99
00:04:38.639 --> 00:04:42.560
<v Speaker 2>And mutating state simply means changing and existing value in

100
00:04:42.600 --> 00:04:45.439
<v Speaker 2>place and memory. For instance, if you have a list

101
00:04:45.480 --> 00:04:48.759
<v Speaker 2>of numbers in C sharp and you call list sort,

102
00:04:49.240 --> 00:04:53.199
<v Speaker 2>the framework physically rearranges those items in your computer's memory.

103
00:04:53.279 --> 00:04:55.360
<v Speaker 1>Right, it's a destructive update. The original order is just

104
00:04:55.399 --> 00:04:55.959
<v Speaker 1>gone forever.

105
00:04:56.199 --> 00:04:58.800
<v Speaker 2>But if you use LA and Q's order by method,

106
00:04:59.240 --> 00:05:02.000
<v Speaker 2>it behaves complete lely differently. It doesn't touch your original list.

107
00:05:02.079 --> 00:05:04.879
<v Speaker 2>It reads it and then creates a brand new list,

108
00:05:04.959 --> 00:05:07.800
<v Speaker 2>a new view of the data that happens to be sorted.

109
00:05:07.600 --> 00:05:09.639
<v Speaker 1>Leaving the original completely intact.

110
00:05:09.800 --> 00:05:13.480
<v Speaker 2>Exactly, And that distinction might seem minor, but it is

111
00:05:13.560 --> 00:05:17.680
<v Speaker 2>exactly what saves you from the concurrency disaster. Buanano's book

112
00:05:17.759 --> 00:05:19.560
<v Speaker 2>has this terrifying example of this.

113
00:05:19.639 --> 00:05:21.120
<v Speaker 1>Oh yeah, the math there one, right.

114
00:05:21.639 --> 00:05:25.439
<v Speaker 2>Imagine a background task Task one. It's trying to calculate

115
00:05:25.480 --> 00:05:28.120
<v Speaker 2>the sum of a massive list of numbers running from

116
00:05:28.199 --> 00:05:30.920
<v Speaker 2>positive ten thousand all the way down to negative ten thousand.

117
00:05:31.000 --> 00:05:33.560
<v Speaker 1>Okay, so logically, if you add all those up, the

118
00:05:33.639 --> 00:05:35.639
<v Speaker 1>sum should perfectly balance out to zero.

119
00:05:35.800 --> 00:05:39.360
<v Speaker 2>It should, But imagine that at the exact same millisecond.

120
00:05:39.680 --> 00:05:42.560
<v Speaker 2>Task two is concurrently running on a different thread, and

121
00:05:42.600 --> 00:05:45.560
<v Speaker 2>it calls list sort on that exact same list.

122
00:05:45.600 --> 00:05:49.399
<v Speaker 1>Oh no, because list sort mutates the list in place.

123
00:05:49.759 --> 00:05:52.519
<v Speaker 1>Task one is trying to do math, while task two

124
00:05:52.639 --> 00:05:54.879
<v Speaker 1>is aggressively moving the numbers around exactly.

125
00:05:54.959 --> 00:05:57.199
<v Speaker 2>Task one might read the number five thousand, and then

126
00:05:57.240 --> 00:05:59.560
<v Speaker 2>task two moves that five thousand further down the.

127
00:05:59.480 --> 00:06:01.759
<v Speaker 1>List, and this one just reads it a second time.

128
00:06:01.879 --> 00:06:04.240
<v Speaker 2>Right, so the result isn't zero, the book points out,

129
00:06:04.279 --> 00:06:08.319
<v Speaker 2>You get these bizarre, totally unpredictable results, like returning a

130
00:06:08.360 --> 00:06:11.639
<v Speaker 2>sum of nine, three hundred thirty nine and seventy.

131
00:06:11.800 --> 00:06:12.480
<v Speaker 3>That is wild.

132
00:06:12.759 --> 00:06:15.519
<v Speaker 2>And this is why concurrency and traditional imperative programming is

133
00:06:15.560 --> 00:06:18.360
<v Speaker 2>so notoriously difficult. You have to introduce complex locks, You

134
00:06:18.360 --> 00:06:20.360
<v Speaker 2>have to freeze the entire list just to read it,

135
00:06:20.360 --> 00:06:21.800
<v Speaker 2>which just destroys your performance.

136
00:06:22.120 --> 00:06:26.360
<v Speaker 1>But if you embrace immutable data, where sorting creates a

137
00:06:26.399 --> 00:06:30.600
<v Speaker 1>new list instead of destroying the old one, concurrent reads

138
00:06:31.000 --> 00:06:33.240
<v Speaker 1>don't cause any inconsistencies whatsoever.

139
00:06:33.480 --> 00:06:37.279
<v Speaker 2>Right, you get parallelization essentially for free, because nobody is

140
00:06:37.319 --> 00:06:39.439
<v Speaker 2>flipping the pages of the book you are trying to read.

141
00:06:39.560 --> 00:06:42.600
<v Speaker 1>I mean, if you've ever stared at a massive, tangled

142
00:06:42.680 --> 00:06:46.079
<v Speaker 1>legacy code base and felt sheer panic at the thought

143
00:06:46.079 --> 00:06:49.959
<v Speaker 1>of making it thread safe. Realizing that avoiding mutation gives

144
00:06:49.959 --> 00:06:53.240
<v Speaker 1>you a free pass to parallel execution is just a

145
00:06:53.319 --> 00:06:54.639
<v Speaker 1>massive light bulb moment.

146
00:06:54.800 --> 00:06:55.639
<v Speaker 2>It really is.

147
00:06:55.959 --> 00:06:59.360
<v Speaker 1>And taking this idea of avoiding mutation to its logical

148
00:06:59.399 --> 00:07:02.680
<v Speaker 1>extreme brings us to the concept of the pure function.

149
00:07:03.360 --> 00:07:06.439
<v Speaker 2>Yes, the pursuit of purity is central here. A pure

150
00:07:06.480 --> 00:07:10.160
<v Speaker 2>function closely resembles a mathematical equation. It operates under two

151
00:07:10.360 --> 00:07:14.720
<v Speaker 2>very strict rules. First, its output depends entirely and exclusively

152
00:07:14.759 --> 00:07:18.160
<v Speaker 2>on its input arguments okay, And second, it causes absolutely

153
00:07:18.160 --> 00:07:18.920
<v Speaker 2>no side effects.

154
00:07:19.040 --> 00:07:21.560
<v Speaker 1>Now, side effects is a really broad category, right, We

155
00:07:21.600 --> 00:07:25.240
<v Speaker 1>aren't just talking about mutating global variables. In functional programming,

156
00:07:25.360 --> 00:07:27.759
<v Speaker 1>throwing an exception is considered a side effect. Yes, it

157
00:07:27.839 --> 00:07:31.120
<v Speaker 1>is performing any kind of IO like printing to the console,

158
00:07:31.639 --> 00:07:34.439
<v Speaker 1>querying a database, or even just checking the system clock.

159
00:07:34.600 --> 00:07:36.720
<v Speaker 3>Those are all side effects.

160
00:07:36.319 --> 00:07:38.800
<v Speaker 2>Right, And people always ask why is checking the clock

161
00:07:38.839 --> 00:07:41.560
<v Speaker 2>a side effect? It's because anytime you rely on the

162
00:07:41.600 --> 00:07:46.680
<v Speaker 2>external physical world, your function loses its determinism. Oh, if

163
00:07:46.680 --> 00:07:49.240
<v Speaker 2>a function asks the system for the current time to

164
00:07:49.480 --> 00:07:52.759
<v Speaker 2>validate a token, it will return a different result tomorrow

165
00:07:52.800 --> 00:07:55.040
<v Speaker 2>than it did today, even if you pass in the

166
00:07:55.079 --> 00:07:56.319
<v Speaker 2>exact same token.

167
00:07:56.199 --> 00:07:59.480
<v Speaker 1>Right, whereas a truly pure function guarantees the exact same

168
00:07:59.519 --> 00:08:03.240
<v Speaker 1>output every single time until the end of the universe exactly.

169
00:08:03.720 --> 00:08:07.600
<v Speaker 2>The book contrasts two string manipulation functions to show why

170
00:08:07.639 --> 00:08:10.439
<v Speaker 2>this matters for scaling up. One is a pure function

171
00:08:10.519 --> 00:08:12.720
<v Speaker 2>called toe sentence case. It just takes a string and

172
00:08:12.759 --> 00:08:15.759
<v Speaker 2>formats the capitalization super simple. The other is an impure

173
00:08:15.800 --> 00:08:18.920
<v Speaker 2>function called pre pend counter. It takes a string, but

174
00:08:19.000 --> 00:08:21.639
<v Speaker 2>it also reaches outside itself to use the plus plus

175
00:08:21.720 --> 00:08:25.319
<v Speaker 2>operator to increment a shared global counter variable, so it

176
00:08:25.360 --> 00:08:26.560
<v Speaker 2>can number the strings.

177
00:08:26.920 --> 00:08:29.360
<v Speaker 1>And if you try to run that impure pre pen

178
00:08:29.480 --> 00:08:34.000
<v Speaker 1>counter in parallel across a million strings, it falls apart

179
00:08:34.279 --> 00:08:37.879
<v Speaker 1>because at the CPU level, plus is actually multiple steps

180
00:08:37.919 --> 00:08:40.159
<v Speaker 1>read the value, add one, write it back.

181
00:08:40.240 --> 00:08:43.559
<v Speaker 2>It's not atomic, right, Multiple threads read the shared counter

182
00:08:43.600 --> 00:08:45.960
<v Speaker 2>at the exact same millisecond, they all add one to

183
00:08:46.000 --> 00:08:48.960
<v Speaker 2>the same base number and write it back. They trample each.

184
00:08:48.799 --> 00:08:50.759
<v Speaker 1>Other, so you lose thousands of updates.

185
00:08:50.879 --> 00:08:53.919
<v Speaker 2>Yeah, your final count will be completely wrong. But the

186
00:08:53.919 --> 00:08:56.440
<v Speaker 2>pure to sentence case function, you can spread that across

187
00:08:56.480 --> 00:08:58.639
<v Speaker 2>one hundred different CPU cores instantly.

188
00:08:58.720 --> 00:09:00.960
<v Speaker 1>Because it doesn't rely on anything else side itself, it

189
00:09:01.039 --> 00:09:05.600
<v Speaker 1>is completely isolated, which means it's thread safe by default exactly.

190
00:09:06.159 --> 00:09:09.360
<v Speaker 2>And that complete isolation is why pure functions are an

191
00:09:09.399 --> 00:09:12.519
<v Speaker 2>absolute dream to unit test. You don't need to configure

192
00:09:12.639 --> 00:09:16.200
<v Speaker 2>dependency injection or complex mock objects. You just feed it

193
00:09:16.240 --> 00:09:18.320
<v Speaker 2>an input and assert the output.

194
00:09:18.120 --> 00:09:21.000
<v Speaker 1>Whereas testing in pure io is usually a nightmare. To

195
00:09:21.080 --> 00:09:23.519
<v Speaker 1>test a service that happens to check the time, developers

196
00:09:23.600 --> 00:09:28.080
<v Speaker 1>usually resort to creating these totally hollow header interfaces. You

197
00:09:28.159 --> 00:09:30.879
<v Speaker 1>end up creating an I date time service and wiring

198
00:09:30.879 --> 00:09:33.759
<v Speaker 1>it up everywhere solely so you can inject a fake

199
00:09:34.120 --> 00:09:37.759
<v Speaker 1>frozen clock into your unit test to mock DateTime dot

200
00:09:37.840 --> 00:09:38.720
<v Speaker 1>ut as nail.

201
00:09:39.000 --> 00:09:42.039
<v Speaker 2>It hadds so many layers of boilerplate just to test

202
00:09:42.080 --> 00:09:46.120
<v Speaker 2>a simple date validation. The functional approach solves this by

203
00:09:46.159 --> 00:09:48.120
<v Speaker 2>pushing the pure boundary outwards.

204
00:09:48.440 --> 00:09:49.480
<v Speaker 3>What do you mean by outwards?

205
00:09:49.600 --> 00:09:52.799
<v Speaker 2>Instead of injecting a massive mocked interface that goes out

206
00:09:52.799 --> 00:09:55.679
<v Speaker 2>and gets the date, just inject a hard coded daytime

207
00:09:55.759 --> 00:09:58.440
<v Speaker 2>value directly into your validator as an argument.

208
00:09:58.679 --> 00:10:01.320
<v Speaker 1>Oh, so the val itself stays.

209
00:10:01.080 --> 00:10:04.120
<v Speaker 2>Pure exactly, Or if you need to validate against a

210
00:10:04.159 --> 00:10:07.120
<v Speaker 2>list of valid bank routing numbers from a database. Don't

211
00:10:07.120 --> 00:10:10.399
<v Speaker 2>inject an entire I repository interface. Just in ject a

212
00:10:10.440 --> 00:10:13.240
<v Speaker 2>simple function like a delegate returning a list of strings

213
00:10:13.240 --> 00:10:14.519
<v Speaker 2>that the validator can invoke.

214
00:10:14.720 --> 00:10:17.440
<v Speaker 1>Okay, wait, I'm trying to picture an application built entirely

215
00:10:17.480 --> 00:10:19.759
<v Speaker 1>like this, a program with absolutely no IO.

216
00:10:19.960 --> 00:10:21.320
<v Speaker 3>It's basically just a space heater.

217
00:10:21.440 --> 00:10:24.480
<v Speaker 1>A space heater, yeah, I mean it consumes electricity, gets

218
00:10:24.480 --> 00:10:27.399
<v Speaker 1>a CPU hot, and does absolutely nothing useful. We have

219
00:10:27.440 --> 00:10:28.799
<v Speaker 1>to have side effects, right, we have to save to

220
00:10:28.879 --> 00:10:30.559
<v Speaker 1>databases and display things on screens.

221
00:10:30.759 --> 00:10:34.320
<v Speaker 2>That is a very valid reality check, and functional programming

222
00:10:34.360 --> 00:10:38.960
<v Speaker 2>absolutely acknowledges that the goal was never to eliminate IO entirely.

223
00:10:39.399 --> 00:10:40.519
<v Speaker 2>It is to isolate it.

224
00:10:40.840 --> 00:10:41.200
<v Speaker 3>Okay.

225
00:10:41.360 --> 00:10:45.120
<v Speaker 2>The book uses a document converter application called pandock to

226
00:10:45.159 --> 00:10:49.720
<v Speaker 2>illustrate this architecture. You isolate the pure computational core, the

227
00:10:49.879 --> 00:10:54.200
<v Speaker 2>complex logic of parsing, markup, translating, syntax, formatting, the layout

228
00:10:54.240 --> 00:10:56.960
<v Speaker 2>that is all pure, and then you wrap that core

229
00:10:57.159 --> 00:11:00.840
<v Speaker 2>in an impure IO shell. The shell handle reading the

230
00:11:00.879 --> 00:11:03.360
<v Speaker 2>original file from the hard drive and writing the final

231
00:11:03.399 --> 00:11:04.799
<v Speaker 2>PDF back to the disc.

232
00:11:04.919 --> 00:11:07.840
<v Speaker 1>Oh I see, so you actively push the MESSI impurity

233
00:11:07.879 --> 00:11:10.399
<v Speaker 1>to the very edges of your application exactly.

234
00:11:10.440 --> 00:11:12.759
<v Speaker 2>You keep the messy real world on the outside and

235
00:11:12.799 --> 00:11:15.200
<v Speaker 2>your core business logic pure on the inside.

236
00:11:15.480 --> 00:11:18.840
<v Speaker 1>But that raises a major question about error handling. If

237
00:11:18.879 --> 00:11:21.879
<v Speaker 1>our pure functions aren't allowed to throw exceptions, how do

238
00:11:21.919 --> 00:11:23.159
<v Speaker 1>we handle bad data?

239
00:11:23.240 --> 00:11:23.480
<v Speaker 2>Right?

240
00:11:23.639 --> 00:11:26.159
<v Speaker 1>Like, if someone passes my pure function and negative age

241
00:11:26.279 --> 00:11:28.360
<v Speaker 1>and I can't throw an argument exception, what are we

242
00:11:28.360 --> 00:11:28.879
<v Speaker 1>supposed to do?

243
00:11:29.240 --> 00:11:31.480
<v Speaker 2>This brings us to the concept of making our function

244
00:11:31.600 --> 00:11:35.799
<v Speaker 2>signatures honest. A signature, its name, inputs, and return type

245
00:11:35.919 --> 00:11:37.799
<v Speaker 2>is essentially a contract, right.

246
00:11:38.120 --> 00:11:40.759
<v Speaker 1>It tells the caller what inputs it requires and what

247
00:11:40.840 --> 00:11:42.399
<v Speaker 1>output it guarantees.

248
00:11:42.120 --> 00:11:45.559
<v Speaker 2>Exactly, and the book highlights how easily we lie in

249
00:11:45.600 --> 00:11:48.679
<v Speaker 2>these contracts. There's an example of a dishonest signature, a

250
00:11:48.759 --> 00:11:52.480
<v Speaker 2>function named calculator risk profile that takes the standard integer

251
00:11:52.600 --> 00:11:53.679
<v Speaker 2>for the age parameter.

252
00:11:53.919 --> 00:11:54.240
<v Speaker 3>Okay.

253
00:11:54.360 --> 00:11:56.639
<v Speaker 2>The signature promises that if you give it any integer,

254
00:11:57.000 --> 00:12:01.080
<v Speaker 2>even negative one thousand, it will successfully return a risk profile.

255
00:12:01.279 --> 00:12:04.159
<v Speaker 2>But inside the body it checks if the age is negative.

256
00:12:04.200 --> 00:12:06.440
<v Speaker 2>If it is, it throws an argument exception and halts

257
00:12:06.480 --> 00:12:07.000
<v Speaker 2>the program.

258
00:12:07.159 --> 00:12:11.000
<v Speaker 1>It's a completely dishonest contract. The caller reads the signature

259
00:12:11.399 --> 00:12:14.480
<v Speaker 1>assumes they're safe providing any integer, and then at runtime

260
00:12:14.519 --> 00:12:15.919
<v Speaker 1>the app violently crashes.

261
00:12:16.080 --> 00:12:19.799
<v Speaker 2>Exactly functional programming actively avoids hidden traps like this.

262
00:12:20.120 --> 00:12:23.399
<v Speaker 1>So how do we fix a dishonest signature without throwing

263
00:12:23.440 --> 00:12:26.440
<v Speaker 1>an exception? We use a pattern called a smart constructor.

264
00:12:26.919 --> 00:12:30.159
<v Speaker 1>Instead of accepting a raw integer for age, we define

265
00:12:30.200 --> 00:12:33.919
<v Speaker 1>a custom immutable age type and we write a smart

266
00:12:33.960 --> 00:12:37.799
<v Speaker 1>constructor method like age two seat. This runs the validation

267
00:12:37.960 --> 00:12:40.960
<v Speaker 1>logic before the age object is ever allowed to be created.

268
00:12:41.159 --> 00:12:46.279
<v Speaker 2>By enforcing that validation upfront, you make invalid states unrepresentable

269
00:12:46.320 --> 00:12:50.399
<v Speaker 2>in your system. Once your code holds an instantiated age object,

270
00:12:50.759 --> 00:12:54.039
<v Speaker 2>you know for a fact it contains a valid positive age.

271
00:12:54.320 --> 00:12:58.240
<v Speaker 1>So your calculat risk profile function changes its signature to

272
00:12:58.320 --> 00:13:00.480
<v Speaker 1>require an age type instead of integer.

273
00:13:00.759 --> 00:13:04.480
<v Speaker 2>Yep, the signature becomes completely honest and the function never

274
00:13:04.519 --> 00:13:07.480
<v Speaker 2>has to worry about throwing an exception for negative numbers. Ever.

275
00:13:07.519 --> 00:13:10.320
<v Speaker 1>Again, that makes perfect sense for a validation. But what

276
00:13:10.440 --> 00:13:14.480
<v Speaker 1>about retrieving missing data? Because the standard dot net collections

277
00:13:14.559 --> 00:13:18.159
<v Speaker 1>drive me a little crazy with their inconsistency here how so, Well,

278
00:13:18.200 --> 00:13:20.320
<v Speaker 1>if I use a name value collection and ask for

279
00:13:20.320 --> 00:13:24.480
<v Speaker 1>a dictionary key that doesn't exist like green, it quietly returns.

280
00:13:24.080 --> 00:13:25.120
<v Speaker 3>A null value.

281
00:13:25.320 --> 00:13:27.559
<v Speaker 1>But if I use a standard dictionary and ask for blue,

282
00:13:27.840 --> 00:13:29.320
<v Speaker 1>it throws a keynot found exception.

283
00:13:29.440 --> 00:13:33.320
<v Speaker 2>Oh yeah. Under the functional paradigm, both those outcomes, returning

284
00:13:33.360 --> 00:13:37.240
<v Speaker 2>a silent null or throwing a loud exception are considered dishonest.

285
00:13:37.879 --> 00:13:39.879
<v Speaker 2>If a function goes looking for a value and might

286
00:13:39.879 --> 00:13:43.120
<v Speaker 2>not find it, its signature needs to explicitly declare that

287
00:13:43.159 --> 00:13:46.480
<v Speaker 2>failure is a distinct possibility. This is where we introduce

288
00:13:46.559 --> 00:13:47.480
<v Speaker 2>the option type.

289
00:13:47.639 --> 00:13:50.799
<v Speaker 1>The option type you might hear it called maybe in

290
00:13:50.840 --> 00:13:55.200
<v Speaker 1>other languages. It essentially models data that might be absent yes.

291
00:13:55.399 --> 00:13:58.919
<v Speaker 2>It has two possible states, some and none.

292
00:13:59.360 --> 00:14:02.600
<v Speaker 1>I like to vision the option type as a cardboard

293
00:14:02.639 --> 00:14:06.440
<v Speaker 1>shipping box. You order a new gadget online. When the

294
00:14:06.480 --> 00:14:09.720
<v Speaker 1>delivery arrives, the box either contains the gadget you wanted.

295
00:14:09.919 --> 00:14:12.720
<v Speaker 1>That's the sum state, or there was a warehouse mix

296
00:14:12.799 --> 00:14:15.919
<v Speaker 1>up and the box is empty, which represents the nun state.

297
00:14:16.120 --> 00:14:19.840
<v Speaker 1>That's a great visualization, but crucially, either way, the delivery

298
00:14:19.919 --> 00:14:23.039
<v Speaker 1>driver still handed you a physical box. You can safely

299
00:14:23.080 --> 00:14:25.639
<v Speaker 1>hold it, pass it to another department, and write code

300
00:14:25.639 --> 00:14:27.720
<v Speaker 1>to handle the box without your program crashing.

301
00:14:27.879 --> 00:14:31.120
<v Speaker 2>Exactly, instead of returning null, which is just a ticking

302
00:14:31.159 --> 00:14:34.039
<v Speaker 2>time bomb that causes a null reference exception. The second

303
00:14:34.039 --> 00:14:36.480
<v Speaker 2>you try to access it you return an option. It

304
00:14:36.519 --> 00:14:39.519
<v Speaker 2>explicitly communicates to the developer the statu might be absent.

305
00:14:39.759 --> 00:14:42.080
<v Speaker 2>You are forced to handle the empty box scenario.

306
00:14:42.120 --> 00:14:42.679
<v Speaker 3>Hang on, though.

307
00:14:43.320 --> 00:14:46.960
<v Speaker 1>Let's say a business requirement changes marketing decides that the

308
00:14:47.000 --> 00:14:50.360
<v Speaker 1>first name field on a massive newsletter sign up form

309
00:14:50.879 --> 00:14:53.759
<v Speaker 1>should now be optional. If I go into my C

310
00:14:53.879 --> 00:14:56.720
<v Speaker 1>sharp domain model and change the first name property from

311
00:14:56.759 --> 00:14:59.840
<v Speaker 1>a regular string to an option string, every single piece

312
00:14:59.879 --> 00:15:02.960
<v Speaker 1>of code in my application that touch's first name is.

313
00:15:02.879 --> 00:15:05.399
<v Speaker 3>Going to break immediately. It won't even compile.

314
00:15:05.960 --> 00:15:08.200
<v Speaker 1>How is breaking the bild a good thing?

315
00:15:08.399 --> 00:15:12.360
<v Speaker 2>It is actually a fantastic thing, because you're trading silent,

316
00:15:12.600 --> 00:15:17.399
<v Speaker 2>unpredictable run time crashes for loud immediate compile time errors.

317
00:15:17.799 --> 00:15:20.399
<v Speaker 2>Think about the alternative. If you just allowed that string

318
00:15:20.480 --> 00:15:23.000
<v Speaker 2>to be null, your code would compile perfectly fine, but

319
00:15:23.039 --> 00:15:25.440
<v Speaker 2>it would crash in production a week later the very

320
00:15:25.440 --> 00:15:28.279
<v Speaker 2>first time user didn't enter their name and a downstream

321
00:15:28.320 --> 00:15:30.960
<v Speaker 2>system tried to call to Hupper on it ah right.

322
00:15:31.080 --> 00:15:33.559
<v Speaker 2>By changing the signature to an option, the compiler becomes

323
00:15:33.600 --> 00:15:36.639
<v Speaker 2>your personal assistant. It flags every single place in your

324
00:15:36.679 --> 00:15:39.679
<v Speaker 2>massive codebase that needs to be updated to acknowledge this

325
00:15:39.879 --> 00:15:41.240
<v Speaker 2>new optional reality.

326
00:15:41.480 --> 00:15:43.720
<v Speaker 1>Okay, I see the safety in that. Yeah, but it

327
00:15:43.759 --> 00:15:45.679
<v Speaker 1>brings up a huge logistical headache.

328
00:15:45.720 --> 00:15:46.159
<v Speaker 2>What's that?

329
00:15:46.639 --> 00:15:47.919
<v Speaker 3>If we safely pack our.

330
00:15:47.799 --> 00:15:51.720
<v Speaker 1>Missing data into these option shipping boxes, how do we

331
00:15:51.759 --> 00:15:54.039
<v Speaker 1>actually do any work on the data inside the box.

332
00:15:54.679 --> 00:15:57.559
<v Speaker 1>Like if I have a pure function that expects a

333
00:15:57.600 --> 00:16:00.720
<v Speaker 1>regular string, but my database returned in an option string,

334
00:16:01.200 --> 00:16:03.759
<v Speaker 1>I can't just pass the box to the function. Do

335
00:16:03.879 --> 00:16:06.720
<v Speaker 1>I have to write an if statement every single time

336
00:16:06.759 --> 00:16:09.159
<v Speaker 1>to check if the box is empty, unpack it, run

337
00:16:09.200 --> 00:16:12.519
<v Speaker 1>the function, and repack it. That sounds exhausting it.

338
00:16:12.480 --> 00:16:14.559
<v Speaker 2>Would be, but you don't have to do that manually.

339
00:16:15.120 --> 00:16:18.279
<v Speaker 2>That is exactly where we introduce the core functional design

340
00:16:18.360 --> 00:16:22.000
<v Speaker 2>patterns functors and monads, which are operated by the map

341
00:16:22.039 --> 00:16:23.120
<v Speaker 2>and bind functions.

342
00:16:23.679 --> 00:16:27.120
<v Speaker 1>I know, the terms functors and monads sound incredibly intimidating.

343
00:16:27.120 --> 00:16:29.240
<v Speaker 1>They sound like advanced category theory.

344
00:16:29.320 --> 00:16:32.399
<v Speaker 2>They do carry a lot of academic baggage, but stripped

345
00:16:32.399 --> 00:16:34.799
<v Speaker 2>down to their basics, they are just patterns for structures

346
00:16:34.840 --> 00:16:37.159
<v Speaker 2>you already use every day. An option or an I

347
00:16:37.279 --> 00:16:41.799
<v Speaker 2>innumerable list. Those are containers. They represent values existing at

348
00:16:41.799 --> 00:16:43.360
<v Speaker 2>an elevated level of abstraction.

349
00:16:43.759 --> 00:16:46.759
<v Speaker 1>So let's demystify the functor first, which relies on a

350
00:16:46.759 --> 00:16:49.919
<v Speaker 1>function called map. The map function reaches into your container,

351
00:16:50.200 --> 00:16:53.559
<v Speaker 1>takes the inner value out, applies your regular business logic

352
00:16:53.559 --> 00:16:56.600
<v Speaker 1>function to it, and then reboxes the new result back

353
00:16:56.600 --> 00:16:59.440
<v Speaker 1>into a new container, and you never have to write

354
00:16:59.480 --> 00:17:02.600
<v Speaker 1>an if statement to check if the value is actually there.

355
00:17:03.200 --> 00:17:05.279
<v Speaker 2>If the option box happens to be in the nun state.

356
00:17:05.400 --> 00:17:07.960
<v Speaker 2>If it's empty, Map is smart enough to just bypass

357
00:17:08.000 --> 00:17:11.720
<v Speaker 2>your function entirely and return a new none. If it

358
00:17:11.759 --> 00:17:14.680
<v Speaker 2>is in the sum state, it applies the function and

359
00:17:14.759 --> 00:17:18.440
<v Speaker 2>wraps the result in a new sum. It completely abstracts

360
00:17:18.440 --> 00:17:21.240
<v Speaker 2>away the boilerplate of checking for the absence of data.

361
00:17:21.920 --> 00:17:24.799
<v Speaker 1>The book has an amazing analogy to explain map. Think

362
00:17:24.799 --> 00:17:28.119
<v Speaker 1>of your aunt who absolutely loves baking apple pies.

363
00:17:28.279 --> 00:17:29.359
<v Speaker 2>Okay, I love this one.

364
00:17:29.519 --> 00:17:30.119
<v Speaker 3>You leave a.

365
00:17:30.079 --> 00:17:33.880
<v Speaker 1>Basket of apples on her front porch. The basket itself

366
00:17:33.920 --> 00:17:36.920
<v Speaker 1>is the option container. The apples are the concrete value

367
00:17:36.920 --> 00:17:41.599
<v Speaker 1>inside her baking skills represent your business logic function. You

368
00:17:41.720 --> 00:17:45.039
<v Speaker 1>leave the basket, She takes the apples out, bakes a pie,

369
00:17:45.279 --> 00:17:48.119
<v Speaker 1>and puts the pie back into the basket. What started

370
00:17:48.119 --> 00:17:50.319
<v Speaker 1>as an option of apples is now an option of pie.

371
00:17:50.559 --> 00:17:52.240
<v Speaker 2>And how does she handle an empty basket?

372
00:17:52.279 --> 00:17:54.799
<v Speaker 1>That's the brilliance of map. If you leave an empty

373
00:17:54.799 --> 00:17:58.559
<v Speaker 1>basket of bandit yeah, yeah, none, she doesn't freak out,

374
00:17:58.759 --> 00:18:00.920
<v Speaker 1>She doesn't throw a null reference exception or call you

375
00:18:00.960 --> 00:18:02.960
<v Speaker 1>in a panic. She just smiles and leaves the empty

376
00:18:03.000 --> 00:18:04.880
<v Speaker 1>basket right there on the board exactly. You don't have

377
00:18:04.920 --> 00:18:06.759
<v Speaker 1>to explain to her what to do with empty space.

378
00:18:07.319 --> 00:18:10.319
<v Speaker 1>The map process handles the empty state automatically.

379
00:18:10.920 --> 00:18:14.759
<v Speaker 2>That analogy perfectly captures how a functor works, but we

380
00:18:14.880 --> 00:18:18.400
<v Speaker 2>inevitably run into a more complex scenario. What happens if

381
00:18:18.440 --> 00:18:21.839
<v Speaker 2>the function you are applying also returns an option container?

382
00:18:22.000 --> 00:18:22.680
<v Speaker 3>Oh? I see?

383
00:18:23.000 --> 00:18:25.039
<v Speaker 1>Like if I have an option of a strength from

384
00:18:25.039 --> 00:18:27.759
<v Speaker 1>a database and I pass it to a parsing function

385
00:18:27.880 --> 00:18:30.559
<v Speaker 1>that tries to turn it into an integer, that parsing

386
00:18:30.559 --> 00:18:34.119
<v Speaker 1>function might fail, so it returns an option of an integer, right,

387
00:18:34.200 --> 00:18:36.640
<v Speaker 1>and then I pass that integer to our smart constructor

388
00:18:36.640 --> 00:18:38.839
<v Speaker 1>for age, which also returns an option of age.

389
00:18:38.839 --> 00:18:42.079
<v Speaker 2>Precisely, if you use the standard map function to string

390
00:18:42.119 --> 00:18:44.559
<v Speaker 2>those operations together, you have a structural problem.

391
00:18:44.799 --> 00:18:47.319
<v Speaker 1>Let me think about how the generic types would stack up.

392
00:18:48.359 --> 00:18:51.279
<v Speaker 1>Map always puts the result in a new box. So

393
00:18:51.319 --> 00:18:53.480
<v Speaker 1>if my function returns an option age and Map puts

394
00:18:53.480 --> 00:18:55.599
<v Speaker 1>that result in a box, I end up with an

395
00:18:55.599 --> 00:18:59.319
<v Speaker 1>option containing option containing an age. Yeah, it's like Russian

396
00:18:59.359 --> 00:19:03.359
<v Speaker 1>nesting dolls of day option age. That sounds impossible to

397
00:19:03.400 --> 00:19:03.680
<v Speaker 1>work with.

398
00:19:03.799 --> 00:19:07.319
<v Speaker 2>It is very clunky, and that exact problem nested containers

399
00:19:07.359 --> 00:19:10.279
<v Speaker 2>is where monads and the bind function come to the rescue.

400
00:19:10.440 --> 00:19:11.839
<v Speaker 3>Okay, so what does bind do.

401
00:19:12.359 --> 00:19:16.519
<v Speaker 2>Bind does the exact same conditional execution as MAP, but

402
00:19:16.599 --> 00:19:20.359
<v Speaker 2>it has a special power. It flattens the result. Instead

403
00:19:20.359 --> 00:19:23.920
<v Speaker 2>of handing you nested boxes, Bind unwraps the inner box

404
00:19:23.960 --> 00:19:26.160
<v Speaker 2>and hands you a single flat optionage.

405
00:19:26.240 --> 00:19:26.960
<v Speaker 3>Oh wow.

406
00:19:27.319 --> 00:19:29.880
<v Speaker 1>To adapt the ant analogy, it's like if the ant

407
00:19:29.920 --> 00:19:31.839
<v Speaker 1>bakes the pie, but you put the pie into her

408
00:19:31.880 --> 00:19:34.960
<v Speaker 1>own special pie basket. If you used map, you'd come

409
00:19:35.000 --> 00:19:37.920
<v Speaker 1>back to find your porch basket containing her pie basket

410
00:19:37.920 --> 00:19:38.440
<v Speaker 1>containing the.

411
00:19:38.440 --> 00:19:40.039
<v Speaker 2>Pie, right, which is just silly.

412
00:19:40.319 --> 00:19:43.680
<v Speaker 1>But Bind is the process where she realizes that's ridiculous,

413
00:19:43.880 --> 00:19:46.079
<v Speaker 1>takes the pie out of her basket and just puts

414
00:19:46.079 --> 00:19:46.920
<v Speaker 1>it directly into yours.

415
00:19:47.000 --> 00:19:48.440
<v Speaker 3>You get one flat basket.

416
00:19:48.559 --> 00:19:51.559
<v Speaker 2>That is a brilliant way to conceptualize flattening. So map

417
00:19:51.640 --> 00:19:54.720
<v Speaker 2>is for applying regular functions and bind is for applying

418
00:19:54.720 --> 00:19:57.119
<v Speaker 2>functions that themselves return Containerska.

419
00:19:57.160 --> 00:19:57.759
<v Speaker 3>It makes sense.

420
00:19:57.799 --> 00:19:59.839
<v Speaker 2>And finally there's four each which we use right at

421
00:19:59.880 --> 00:20:02.079
<v Speaker 2>the very end of the pipeline. We use map and

422
00:20:02.160 --> 00:20:05.480
<v Speaker 2>bind to run all our pure logics safely inside the containers,

423
00:20:05.720 --> 00:20:08.160
<v Speaker 2>and then we use four each to safely execute our

424
00:20:08.200 --> 00:20:11.359
<v Speaker 2>impure side effects like physically writing to a database.

425
00:20:11.640 --> 00:20:13.960
<v Speaker 1>But it only executes if the data actually made it

426
00:20:14.000 --> 00:20:17.599
<v Speaker 1>through the entire pipeline without turning into a nun exactly.

427
00:20:17.759 --> 00:20:19.440
<v Speaker 1>So we have all the building blocks on the table.

428
00:20:19.480 --> 00:20:23.279
<v Speaker 1>Now we have pure functions, honest signatures via smart constructors,

429
00:20:23.839 --> 00:20:27.039
<v Speaker 1>option types to handle missing data, and map and bind

430
00:20:27.039 --> 00:20:30.960
<v Speaker 1>to process logic without if statements. You do, how do

431
00:20:31.000 --> 00:20:33.799
<v Speaker 1>we actually string all of this together to build a

432
00:20:33.920 --> 00:20:35.920
<v Speaker 1>real end to end architecture.

433
00:20:36.000 --> 00:20:40.039
<v Speaker 2>Well, we build functional workflows using function composition and method chaining.

434
00:20:40.400 --> 00:20:42.839
<v Speaker 2>We're used to this in object oriented code, where you

435
00:20:42.960 --> 00:20:46.680
<v Speaker 2>chain methods together with dot notation. The data flows linearly

436
00:20:46.799 --> 00:20:47.839
<v Speaker 2>reading from left to right.

437
00:20:48.079 --> 00:20:50.839
<v Speaker 1>Li inq is probably the best example of this workflow

438
00:20:50.880 --> 00:20:53.640
<v Speaker 1>mindset that see sharp developers already use. Definitely, if you

439
00:20:53.640 --> 00:20:55.720
<v Speaker 1>want to find the average earnings of the richest core

440
00:20:55.799 --> 00:20:58.799
<v Speaker 1>tile of a population. You don't write nested for loops

441
00:20:58.799 --> 00:21:02.359
<v Speaker 1>and temporary variables. You write a declarative chain you call

442
00:21:02.519 --> 00:21:05.680
<v Speaker 1>order by descending then dot, take dot, select dot average.

443
00:21:05.920 --> 00:21:09.000
<v Speaker 1>It's flat readable and defines the pipeline perfectly.

444
00:21:09.240 --> 00:21:12.039
<v Speaker 2>Now, let's elevate that LA and Q mindset and apply

445
00:21:12.079 --> 00:21:14.759
<v Speaker 2>it to a real server side scenario from the book.

446
00:21:15.039 --> 00:21:17.119
<v Speaker 2>Dealing with the fictional bank of code Land.

447
00:21:17.519 --> 00:21:19.000
<v Speaker 3>Okay, Bank of code Land.

448
00:21:19.440 --> 00:21:22.720
<v Speaker 2>We receive a web request to process a make transfer

449
00:21:22.799 --> 00:21:24.960
<v Speaker 2>command between two bank accounts.

450
00:21:24.559 --> 00:21:28.400
<v Speaker 1>Right, and in a traditional imperiadive web controller, that code

451
00:21:28.440 --> 00:21:31.440
<v Speaker 1>gets messy fast. You have an if statement to check

452
00:21:31.440 --> 00:21:34.480
<v Speaker 1>if the request is null, then a trycatch block around

453
00:21:34.480 --> 00:21:38.279
<v Speaker 1>the database lookup inside that. Another is statement checking if

454
00:21:38.279 --> 00:21:39.759
<v Speaker 1>the account balance is high enough.

455
00:21:39.839 --> 00:21:42.720
<v Speaker 2>Yeah. It creates this deeply nested arrow code that is

456
00:21:42.799 --> 00:21:43.720
<v Speaker 2>really hard to scan.

457
00:21:44.079 --> 00:21:47.599
<v Speaker 1>But by utilizing our functional building blocks, the entire web

458
00:21:47.640 --> 00:21:50.960
<v Speaker 1>controller can be rewritten to read like a single flat sentence.

459
00:21:51.519 --> 00:21:53.079
<v Speaker 2>It really is striking to look at.

460
00:21:53.319 --> 00:21:56.480
<v Speaker 1>Translating the code into plain English. The pipeline flows exactly

461
00:21:56.559 --> 00:21:59.000
<v Speaker 1>like this. You lift the raw transfer request into an

462
00:21:59.000 --> 00:22:01.799
<v Speaker 1>option box. You map it through a function that normalizes

463
00:22:01.839 --> 00:22:04.200
<v Speaker 1>the text, you filter it through a wear clause that

464
00:22:04.319 --> 00:22:08.640
<v Speaker 1>validates the accounts, and if and only if it survives

465
00:22:08.720 --> 00:22:11.799
<v Speaker 1>all those steps without turning into a hote none, you

466
00:22:11.839 --> 00:22:14.400
<v Speaker 1>call for each to execute the side effect that physically

467
00:22:14.400 --> 00:22:15.240
<v Speaker 1>books the transfer.

468
00:22:15.240 --> 00:22:15.799
<v Speaker 3>In the ledger.

469
00:22:16.079 --> 00:22:19.640
<v Speaker 2>Every step is isolated. You never have to manually check

470
00:22:19.680 --> 00:22:22.079
<v Speaker 2>for nulls, and you never have to catch a validation

471
00:22:22.160 --> 00:22:25.480
<v Speaker 2>exception because the option context handles the flow control for you.

472
00:22:25.920 --> 00:22:29.920
<v Speaker 1>It is incredibly elegant. But when we look at the

473
00:22:29.960 --> 00:22:33.119
<v Speaker 1>domain model for actually booking that transfer, things get a

474
00:22:33.119 --> 00:22:34.599
<v Speaker 1>big controversial.

475
00:22:34.000 --> 00:22:36.240
<v Speaker 2>Out the OP versus FP modeling.

476
00:22:36.359 --> 00:22:36.720
<v Speaker 3>Yeah.

477
00:22:37.039 --> 00:22:41.240
<v Speaker 1>In the book's comparison, the traditional object oriented account object

478
00:22:41.599 --> 00:22:44.960
<v Speaker 1>has a debit method. You pass it an amount, it

479
00:22:45.039 --> 00:22:48.119
<v Speaker 1>checks its own balance, it mutates its internal state to

480
00:22:48.160 --> 00:22:50.920
<v Speaker 1>lower the balance, and it throws an exception if you

481
00:22:50.960 --> 00:22:52.200
<v Speaker 1>don't have enough money.

482
00:22:52.119 --> 00:22:55.000
<v Speaker 2>Which, as we've established, breaks all our rules. It relies

483
00:22:55.000 --> 00:22:58.079
<v Speaker 2>on hidden state mutation and throwing an exception makes it

484
00:22:58.119 --> 00:23:00.720
<v Speaker 2>an impure, dishonest signature.

485
00:23:01.000 --> 00:23:01.240
<v Speaker 3>Right.

486
00:23:01.720 --> 00:23:05.240
<v Speaker 1>But the functional replacement they propose strips the behavior completely

487
00:23:05.319 --> 00:23:07.880
<v Speaker 1>out of the object. We get an account state object

488
00:23:07.920 --> 00:23:12.279
<v Speaker 1>that is essentially just dumb immutable data with public properties. Yes,

489
00:23:12.359 --> 00:23:14.920
<v Speaker 1>and the actual debit logic is moved to a pure

490
00:23:15.279 --> 00:23:17.720
<v Speaker 1>static function that takes the old state as an argument

491
00:23:18.079 --> 00:23:20.759
<v Speaker 1>and returns an option account state containing the new balance.

492
00:23:21.079 --> 00:23:24.000
<v Speaker 1>I have to say I struggle with this. For decades

493
00:23:24.079 --> 00:23:27.839
<v Speaker 1>we've been taught that OP is fundamentally about encapsulating data

494
00:23:27.880 --> 00:23:32.279
<v Speaker 1>and behavior together inside objects. Splitting the account state.

495
00:23:32.160 --> 00:23:34.680
<v Speaker 3>Data away from the debit logic feels like we are.

496
00:23:34.559 --> 00:23:37.880
<v Speaker 1>Abandoning OOP and going backward to procedural programming from the

497
00:23:37.960 --> 00:23:42.559
<v Speaker 1>nineteen seventies. Why is tearing encapsulation apart considered better?

498
00:23:42.720 --> 00:23:45.400
<v Speaker 2>It is a very common reaction to feel like separating

499
00:23:45.480 --> 00:23:48.440
<v Speaker 2>data and behavior is a regression, but in this context

500
00:23:48.519 --> 00:23:51.720
<v Speaker 2>it's actually an elevation of your architecture. How So, in OOP,

501
00:23:52.039 --> 00:23:55.839
<v Speaker 2>tightly binding data and behavior inside a single object often

502
00:23:55.920 --> 00:23:59.440
<v Speaker 2>leads to hidden side effects and unpredictable state mutation over time,

503
00:24:00.039 --> 00:24:02.799
<v Speaker 2>and as we saw with the sorting concurrency disaster, mutable

504
00:24:02.799 --> 00:24:06.640
<v Speaker 2>state causes massive bugs when systems scale. By separating them,

505
00:24:06.759 --> 00:24:10.440
<v Speaker 2>account state becomes a pristine, thread safe snapshot in time.

506
00:24:10.599 --> 00:24:12.720
<v Speaker 2>It can never be corrupted by a background threat.

507
00:24:12.799 --> 00:24:13.759
<v Speaker 3>Oh, I see, And the.

508
00:24:13.680 --> 00:24:17.400
<v Speaker 2>Pure static debit function doesn't need tate anything. It simply

509
00:24:17.440 --> 00:24:21.240
<v Speaker 2>evaluates the pristine snapshot runs the math and securely calculates

510
00:24:21.279 --> 00:24:23.839
<v Speaker 2>the next step in the pipeline. It makes your workflows

511
00:24:23.920 --> 00:24:25.359
<v Speaker 2>highly composable.

512
00:24:25.039 --> 00:24:27.920
<v Speaker 1>Because you can pipe the output of debit directly into

513
00:24:27.960 --> 00:24:31.640
<v Speaker 1>the next function without ever worrying that some internal property

514
00:24:31.720 --> 00:24:35.119
<v Speaker 1>was secretly altered or that a hidden exception is about

515
00:24:35.119 --> 00:24:36.400
<v Speaker 1>to blow up your call stack.

516
00:24:36.720 --> 00:24:40.799
<v Speaker 2>Exactly so, it's not about regressing to procedural spaghetti code.

517
00:24:40.799 --> 00:24:45.640
<v Speaker 2>It's about making the business logic mathematically predictable and completely

518
00:24:45.759 --> 00:24:46.960
<v Speaker 2>testable in isolation.

519
00:24:47.599 --> 00:24:50.200
<v Speaker 1>That makes a lot of sense. Functional programming in C

520
00:24:50.359 --> 00:24:53.720
<v Speaker 1>Sharp isn't about throwing away the language features. It's about

521
00:24:53.799 --> 00:24:58.359
<v Speaker 1>using them to build a highly pragmatic, incredibly resilient tool set.

522
00:24:58.640 --> 00:25:00.000
<v Speaker 2>That's the perfect way to summarize it.

523
00:25:00.319 --> 00:25:02.799
<v Speaker 1>Let's quickly recap the mission we set out on today.

524
00:25:03.240 --> 00:25:05.960
<v Speaker 1>By treating our functions as data that can be passed around,

525
00:25:06.279 --> 00:25:10.160
<v Speaker 1>by rigorously avoiding state mutation, and by utilizing honest structures

526
00:25:10.200 --> 00:25:12.759
<v Speaker 1>like the option type, paired with map and bind, you

527
00:25:12.799 --> 00:25:16.200
<v Speaker 1>can make your c shark code incredibly concise. Absolutely, you

528
00:25:16.240 --> 00:25:19.720
<v Speaker 1>can almost entirely eliminate null reference exceptions because the compiler

529
00:25:19.759 --> 00:25:23.279
<v Speaker 1>forces you to acknowledge missing data and most importantly, you

530
00:25:23.359 --> 00:25:26.839
<v Speaker 1>make concurrency a breeze because immutable data simply does not

531
00:25:26.880 --> 00:25:28.200
<v Speaker 1>suffer from raise conditions.

532
00:25:28.480 --> 00:25:31.680
<v Speaker 2>It definitely requires a paradigm shift in how you view

533
00:25:31.839 --> 00:25:35.839
<v Speaker 2>dataflow and state mutation, but the payoff in reliability and

534
00:25:35.880 --> 00:25:37.599
<v Speaker 2>reduce debugging time is immense.

535
00:25:37.759 --> 00:25:39.640
<v Speaker 1>To wrap up, I want to leave you with a

536
00:25:39.680 --> 00:25:44.240
<v Speaker 1>provocative thought to molover. If we can elegantly represent the

537
00:25:44.319 --> 00:25:48.880
<v Speaker 1>total absence of data using just the option type Wait,

538
00:25:48.920 --> 00:25:51.240
<v Speaker 1>I mean think about that. And if we can completely

539
00:25:51.279 --> 00:25:54.920
<v Speaker 1>remove nested if statements from our web controllers using map

540
00:25:54.960 --> 00:25:59.039
<v Speaker 1>and wear pipelines, what other traditional imperative control structures could

541
00:25:59.039 --> 00:26:00.000
<v Speaker 1>we entirely replace?

542
00:26:00.200 --> 00:26:01.039
<v Speaker 2>That's a great question.

543
00:26:01.119 --> 00:26:04.720
<v Speaker 1>Could functional wrappers entirely eliminate the need for trycatch blocks

544
00:26:04.720 --> 00:26:08.039
<v Speaker 1>in your application? Could they replace traditional wile loops? How

545
00:26:08.079 --> 00:26:10.720
<v Speaker 1>much shorter and how much more bulletproof could your legacy

546
00:26:10.720 --> 00:26:14.559
<v Speaker 1>codebase become? If you started thinking impure functions today, maybe

547
00:26:14.519 --> 00:26:16.880
<v Speaker 1>it is finally time to stop the frantic page slipping

548
00:26:16.920 --> 00:26:18.960
<v Speaker 1>in your software and just enjoy the book.
