Functional Programming in an Imperative World. Maybe by Derik Pell

This video features Derik Pell at DjangoCon US 2017 in Spokane, Washington, USA.

Functional Programming in an Imperative World. Maybe by Derik Pell
0:43:26
Published September 6, 2017
1,034 views

DjangoCon US 2017 - Functional Programming in an Imperative World. Maybe by Derik Pell

Let’s start by looking at the core concepts that differentiate FP from the OO / imperative style most programmers are familiar with. Along the way I’ll introduce you to:

  1. Immutable data structures. Having data structures that don’t change makes your code safer, especially when dealing with concurrency and parallelism, but they require you to approach solutions in a different way than you would with mutable data.

  2. “Pure” functions. Pure, or idempotent, functions do not mutate state or cause other kinds of side effects. As a result, you are guaranteed that every time you call a function with the same parameters, you will always get the same value.

  3. Recursion: While recursion is something most of us know about, it’s not something we tend to use often in imperative programming, and with good reason. Nonetheless, it’s a worth knowing about it’s various forms.

  4. Function composition. When you have pure functions that handle only one task, you can build larger, more complex and more beneficial programs by composing functions together to form new functions.

  5. First class functions: passing around functions as parameters and return values, just like any other object.

  6. The holy trinity: map, reduce, filter. These three functions are the work horses of FP, helping us manipulate and transform data quickly and elegantly.

FP in python

Now, let’s take a look at how we can or cannot apply these concepts in python.

  1. While most data structures in python are mutable, tuples are a built in immutable data structure that we have at our disposal. We’ll see that tuples have a solid place in python, but they’re not as easy to work with as we might like.

  2. Recursion isn’t really well developed in python (on purpose) so let’s take a look at it’s pitfalls and how to avoid them.

  3. Function composition is something you probably already do some in python and perhaps don’t even know it.

  4. The trinity:

Filter is easy, we just call it “list comprehension”

Reduce. Let’s try to get beyond flattening nested lists and doing tricks with math.

Map. You probably don’t use this enough in python so let’s see if we can change that.

FP is great! Maybe.

Now that we’ve seen how FP can be used, we really need to decide if it should be used. Python is not a functional programming language, despite the tools it has. We’ve talked about some of the technical drawbacks to these tools, but we also need to decide if working in an FP paradigm is right for our work environment. We’ll look at some examples of where running into FP can be jarring and talk about the additional cognitive load on co-workers who aren’t used to seeing these tools in place.

This talk was presented at: https://2017.djangocon.us/talks/functional-programming-in-an-imperative-world-maybe/

LINKS:
Follow Derik Pell 👇
On Twitter: https://twitter.com/_gignosko_
Official homepage: http://blog.gignosko.me
Github: https://github.com/gignosko/

Follow DjangCon US 👇
https://twitter.com/djangocon

Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/

Summary

Functional programming is a way of transforming data with pure functions that avoid side effects, especially in the middle of an application; it can make code more expressive, safer, efficient, and easier to parallelise. Python already provides many functional tools, including first-class and higher-order functions, decorators, tuples and other immutable structures, recursion, lambdas, and the map, filter, and reduce functions. Derik Pell explains how these tools work with database callbacks, dynamic function selection, immutable data, and list-processing examples, while stressing Python’s limitations around immutable data performance and recursion. His recommendation is to use functional techniques selectively in Python—often alongside object-oriented code—rather than trying to adopt functional programming as the sole paradigm.

Key takeaways

  • Higher-order functions can be passed, returned, or stored like any other value, enabling patterns such as database callbacks and decorators.
  • Pure functions reduce hidden side effects, which helps prevent bugs caused by shared mutable data.
  • Immutable structures make data safer to share, but Python’s built-in approaches can be slower because they create new structures.
  • Recursion can replace loops in functional languages, but Python lacks tail-call optimisation and therefore makes deep recursion risky.
  • Python’s map, filter, and reduce return iterators and provide efficient ways to transform, select, and combine collection values.
  • Functional programming fits Python best as a set of selectively applied techniques, including composed data pipelines, rather than as a complete replacement for object-oriented programming.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction to Functional Programming Derik Pell frames functional programming as a practical set of tools and a mindset available in Python.
  2. 1:46 Benefits of Functional Programming The talk covers expressiveness, efficiency, safety, and concurrency as motivations for using functional techniques.
  3. 3:20 Pure Functions and Side Effects The speaker defines functional programming through pure functions, data transformation, and reduced side effects.
  4. 4:52 Higher-Order Functions Examples show how Python functions can be passed around, returned, stored, and used to build database helpers and decorators.
  5. 12:09 Immutable Data Structures The talk explains immutability, the risks of shared mutable data, and the performance tradeoffs of copying and persistent structures.
  6. 17:45 Recursion in Python Recursion is presented as an alternative to loops in functional languages, along with Python’s lack of tail-call optimization and recursion limits.
  7. 24:04 Map, Filter, and Reduce The speaker demonstrates Python’s core functional tools for filtering collections, transforming values, and combining items.
  8. 36:25 List Comprehensions List comprehensions are compared with map and filter as familiar Python idioms that express functional operations.
  9. 37:11 Using Functional Techniques The talk weighs the benefits and drawbacks of adopting functional programming in Python and recommends using it selectively within teams.
  10. 38:46 Questions The speaker answers questions about mixing functional and object-oriented styles and composing functional data pipelines in Python.

Transcript

7,866 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:14

Speaker 1: Hi everybody, thanks. I appreciate it. So my name is Derek Pell. I'm an engineer at Emma Email Marketing. That's me on uh GitHub and Twitter. Actually if you go out to my GitHub uh and if you go out to that URL to the DjangoCon 2017 This is my entire presentation is gonna is being done in a in a Jupyter notebook and I've uploaded it to GitHub so if you can't see the slide you can pull it up in GitHub and follow along. Um it won't run the code in GitHub, but you can look at it all, clone it, run it. It's Python 3. Um quick show of hands. How many people showed up hoping I was going to explain monads

1:00

Speaker 1: No, sorry. As far as I know, they're still just burritos. So if you don't know what I'm talking about, Google it. Anyway, so what I want to do is talk a little bit about functional programming. Functional programming has become sort of the hot new buzzword, right? And coming from an object-oriented language like Python. Sometimes we might see functional programming as something mysterious. I want to kind of break that up a little bit for you and introduce you to some of the tools that help functional programming you know, do its job and recognize that most of these tools exist in Python. Um not always to the best uh they're they're not always the best tool to use, but they're all there

1:46

Speaker 1: And so I just want to sort sort of get everybody just an overview of what functional programming is, right? But let's why would we use functional programming? Why would we stop talking about object-oriented programming and look at this new style of programming. Well functional programming is very expressive. As we will see through some of the code examples, you can do a lot with a very little bit of code. It can you can just very mu you can shrink your code base pretty considerably in the right places. It can be very efficient. Things run really quickly when your language uh actively supports a functional programming paradigm. And we'll see some places in in Python where that does where it does that very well, some places where it doesn't quite so well. Uh it can be safer.

2:31

Speaker 1: Hyphenated that on purpose to sort of uh emphasize the fact that it's still not safe, like there's still problems, but it's still it does it can be safer. It can overcome a lot of the problems, a lot of the bugs that we see in day-to-day object-oriented programming. And uh it's easier to work with concurrent and parallel programming. Uh we won't actually directly dig into that, but that's really the sort of the reason that functional programming has become so popular the last several years as we've stopped getting uh core processors that can go faster and faster and instead using more and more cores being able to effectively take our code and spread that across a couple of different cores is the next step in making our our applications run faster. Functional programming can help lead into that.

3:20

Speaker 1: But what is functional programming? It's really just a style of programming that utilizes pure functions, which are functions that don't have side effects. And it uses those functions to transform data. So functional programming actually thinks about data in a very different way than object-oriented programming. Object-oriented programming, we hide our data away and we're very particular about how we use it and whatever and who can access it and that sort of thing. Functional programming is a more open data model. It's like, no, like there's no need to hide your data away. Let's, you know, take it and work on it in a more mathematical sense. That's where we have the we try to have pure functions, things that don't have these side effects, because they don't really

4:07

Speaker 1: They don't always make sense to have side effects like in the middle of your code. On the I. O. boundaries, of course, you have to have side effects. You have to write to a database, print to a screen, read from a database. that type of thing. But in the middle of your code, there's a lot of places where we do side effects where we don't necessarily have to. And functional programming is a way of thinking that helps you sort of reduce those, because a lot of times those side effects are places where you have bugs. So what I want to do though is start looking through some of the tools that help you get to functional programming. There's like functional programming is not It's like saying, you know, if you were to explain object-oriented programming, you might start with, okay, this is how you build an object, but an object it building an object or a class is not the totality of object-oriented oriented programming.

4:52

Speaker 1: In the same way there's not one part of functional programming. It's really just a set of tools and a mindset on how you use them. So with that, let's start looking at the tools. The first one is higher order functions. If you've never come across this phrase before, and a lot of this stuff you guys may have seen, you just may not realize that it is sort of like hand in hand with a with more functional thinking. But higher order functions just means that functions get treated like first-class citizens. They can be passed to other functions as parameters. They can be returned from other functions as values and they can be stored in variables. So, you know, they're just like any other piece of data in your application.

5:41

Speaker 1: So let's take a couple of look at a couple of examples. So here is something that's sort of real-worldish. We do this kind of thing quite a bit at work. We need to pass we we will make a function that knows how to talk to our database. It knows how to make the initialization to the database, or maybe it's just pulling a connection Off a pool or something or other, but at the end of the day, we have one function that knows how to grab a cursor to our database. Then we have all these other types of functions that need that cursor. So we may have inserts That will return a row ID, right? But that's going to be different than using a query that's going to return you an entire result set, right? So instead of trying to make

6:28

Speaker 1: some in instead of trying to uh make each individual either make each each individual query like make its own connection to the database or make some function that connects to the database in a way that's versatile enough to like return a row ID in this situation, but return an entire row set in this or result set in this other. What we do is we make a um a function that knows how to connect to the database, grabs the cursor, and then we make other functions that use that cursor And each individual function, all it needs to know is how to use the cursor. It doesn't need to know how to connect to the database. So what we do is we have something along the lines of call database, which takes a function Somewhere in there it makes the cursor and then as its return value it calls the function that we passed in, giving it the cursor, right?

7:19

Speaker 1: So uh one example might be where you query a database, we The query database function takes a cursor and then it knows how to loop over the result set or grab the result set and pass it back or whatever it needs to do. It it just depends on your business logic. But the idea is that we're passing when we do this. Um We're gonna like this next line down here. We're gonna take the results. We're gonna get them by calling directly to the call database function and passing it in the query database function, right? So we can see here Call database is doing its thing to initialize the database, then it gets a cursor and it passes that cursor into whatever function we called it with, which in this case is query database, so it's querying the database with our cursor, right

8:10

Speaker 1: But if we also need to do something slightly different, we can do something like returning a new um the new row value, right? So we you we can reuse this same call database function because all it does is one thing, work on the on the cursor, and it just passes a cursor in whenever we uh call whenever we pass it a function, it calls that function, passes in the cursor. Right? So that's something that that's a place where we use quite often passing a function into another function. You can also return functions. So this is a little more contrived example. This is probably not something that would actually happen quite a bit, but if for instance, let's say you were you had some data coming in somewhere and you had

8:58

Speaker 1: it could potentially come in from several different sources. Right, it could come in from a source that needs to read off S3 or another source that needs to read out of a SQL database. You can create these functions that do whatever they need to do. Like you have an S3 function that knows how to call out to S3 and retrieve the data. Or another function that like we just saw that calls out to the database and retrieves data from the database But you need to dynamically choose which of those functions based on the in the the source coming in. So it's this is this essentially becomes like almost like a a poor man's polymorphism, but um it's a re it's a pretty straightforward example. What we do is we have a source that comes in, in this case S3

9:43

Speaker 1: We ask for a new function by calling out to this give me a function. Give me a function takes our source and says, okay, well it's S3. I'm going to return this complex S3 function to you, right? It doesn't call it, it doesn't do anything with it, it's just passing the function back to us. And then it's up to us, once we get this new function, it's up to us to actually call it with whatever we need to call it with Right? So we get this, you know, we essentially saying, here's it we're we're coming in from S3, what do I do with it? Give me some function that I know how to handle that will know how to handle S3 Or give me some function that will know how to handle SQL. And because these are actually what it's passing back are functions We don't actually have to even do this where we assign it to a value and then call it.

10:33

Speaker 1: You can just call it straight away this way, right? So we're calling into sorry, calling in to give me a function, passing it in our source, and whatever comes back, we immediately call it, just like we would any other function, because that's all it is. It's pat it's just passing us back a function. Now anyone who's and like I said you may have seen this sort of stuff, especially this get this type of thing we use a lot when we're doing JavaScript. If you have to do that but um we actually use it a lot in decorators um Who has worked with, created, looked at decorators? A lot of people? Yeah, oh yeah, the entire room, right. So um so yeah, that's all a decorator really is. You're passing in a function to a decorator function, in this case

11:20

Speaker 1: decorated. That has a nested function, which we typically call wrapper. And then it does whatever kind of work you need to do before calling your function, whatever kind of work you need to do after calling your function. So yeah, this should be pretty straightforward to everybody who's ever used decorators before. And that and that's all it is. So without higher order functions, Python couldn't have decorators. So we have to be able to pass those functions around. And so you see it's not just usable in functional programming. We use it all the time in object-oriented programming in certain languages. And that's sort of the key, is like functional programming isn't really a mystery. It's really just a set of tools that you use in a slightly different way.

12:09

Speaker 1: The next set of tools I want to look at is our immutable data or immutable data structures. Now this is something we don't use as much in Python because mutable data is sort of the bread and butter of object-oriented programming. But for immutable data, it's a structure that never changes itself. So like you have a list, you don't actually add something new to the list. Instead, your list can't be changed. Like you're you're sorry about that. Uh your list is sort of set in stone. Um and we'll look in a little bit about how you uh overcome that because that seems like a limitation, right? Like you create a list, you can't add to it, you can't delete from it. What good is it? How do you make any changes. You have to think about your list in a completely different way.

12:55

Speaker 1: Because whenever you make some update, we're used to passing in a list, adding something to it, getting that list back. With immutable data, you don't do that. You pass in a list, if you add something to it, you get a brand new list back. It's a completely different data structure. Same is same idea as if you take something off the list You know, you pass in the list to a function, it takes something off that list. What you get back, completely different list. Though that's a very safe way of handling things, though. That means that list isn't going to change out from under you Right? How many bugs, how many times have we all done this, track tried to track down a bug where because we've passed off a list, we don't realize that whatever function we're passing it to is mutating that list before it comes back to us? Well, with immutable data structures, you don't have to worry about that.

13:41

Speaker 1: You can pass them a list, they can do whatever they want to with it. Your list never changes. Their list, it's adjusted to a new list. Now we don't do this a lot because historically they have been very inefficient. What you have to do is take that list, it's sitting in memory, the operat the uh Language has to then copy the entire list to a new part of memory so that you essentially have two lists. It's very slow to do, but there have been new data structures called persistent data structures that make this much more efficient If you download this, I've got a link to persistent data structures to the description in Wikipedia. I'm not going to dive into them, but they're much more efficient at this sort of thing. So it lets you use an immutable data structure without paying the the additional cost of like all that copying over in memory.

14:30

Speaker 1: Um if you're not familiar with the problem with mutable data structures So what and probably almost all of you have seen something like this, this is like almost every language I've ever looked at has this sort of warning of This kind of thing where you create a list and then you create a second list, you point a second list to the first list Anytime you change that first list, you've automatically changed that second list, right? So that's not a safe way of making sure your data structure your your list doesn't change. You can't do that. typically. You can see here list one and list two they are the same thing. They're basically the same list because what we have are two pointers to the same piece of memory

15:15

Speaker 1: And this can be a problem here in that sort of situation, but you can also create some really interesting problems if you're not thinking about it. So this is just a quick function that goes through, takes a list uh of integers and then sums up that list, right, and gives you the re the results. So if you pass in the list one, two, three, three, the summation of that is six. But if somewhere in there you accidentally Start appending to your list, what you're gonna get are some really weird results, right? Um you're in the middle of working your way through a list in a way that we do every day. It's just a for loop But somehow we've appended to that list in the middle of that loop and now our list is going to continue to grow. Added this if clause in there because if I don't catch for a list of a certain size, this will grow until I run out of memory

16:06

Speaker 1: So that's one of the things that we end up having to track down sometimes when we work with mutable data. But if you work with immutable data A lot of those situations disappear. So right here, we're doing the same thing that we did above with the list, right? We're creating a tuple instead of creating a list. We're creating a tuple, adding some items to it, and then creating another another tuple pointer to the same tuple, right? So we've got two pointers to the same tuple just like we did with the the list above. But now when we update the one tuple it does not update the second one because in Python, whenever you try to update, whenever you try to concatenate an item into a tuple, you get back an entirely new data structure, right? So before we do the concatenation, we look at these two, tuple 1 and tuple 2.

16:54

Speaker 1: They are the same. After we do the concatenation, they're no longer the same because Python has created a brand new data structure for us. It's a safe way to do it, but it's slow because these were not created as persistent data structures. And you can do you um typically if we want an immutable data structure we go to a tuple. You can do something along the same lines with pretty much any uh with lists or dictionaries. You can use a deep copy And instead of actually just making two pointers, you make a deep copy of the uh the list. That way when you cur when you change one, it does not change the other because you start off with two different lists. But again, it's slow because it has to take that list copy it over in memory. So those are some of the problems that we can see with immutable data that we can hopefully solve with more mutable data structures.

17:45

Speaker 1: But working with uh immutable data structures means we have to start thinking about things slightly differently. often. Like we don't want to do a for loop on an immutable list because it doesn't loop quite uh as w well it'll loop in in python. Um in a lot of functional programming languages though they just pull the loop out of there because if you aren't mutating your list you don't necessarily need loops. You can come up with other ways of uh looping of getting through everything in your list and one of those ways is recursion or the big way is recursion. If you've never worked with recursion Most of you have probably, I assume, seen it, but recursion is when you call a function from within itself.

18:32

Speaker 1: It takes the, like I said, it takes the place of loops in many programming languages, and we'll see that here in just a moment, how we can use recursion instead of a loop. The problem with recursion is that every time you make a call back to the function, you add a frame to the call stack, and that call stack can grow and grow and grow. In languages that embrace recursion, they often do this thing called tail call optimization, which lets you more efficiently do recursion without adding to the call stack. But Python does not have tail call optimization. Guido has, if you've ever looked at it, he's sort of famously come out against tail call optimization in Python because it really does, it hides your stack trace. What tel call optimization does is it instead of building up every stack, anytime you call back,

19:20

Speaker 1: if you do your recursive call properly, it just overwrites the existing frame on your stack so that you don't build, you just sort of keep reusing the same frame. It's much more memory efficient, but you do lose your call stack if something happens you know five or six calls deep, you don't know exactly where it happened, you don't have all the context for it. So we probably will never get tail call optimization But regardless, let's take a quick look at recursion. So this is a recursive version of the summation list that we did above. So instead above we're giving it the list and looping through the list. Here, what we're doing is we're giving it the list and then instead of looping through each item, we send the list back into the function

20:06

Speaker 1: itself. So the function in this case has to take a list, but it also has to take the summation. Because we are recursively calling back into itself, it's harder to keep track of the state. you have to pass this you typically end up having to pass the state back into the function along with whatever uh Like well in this case we're passing the list in. We also are passing in the cumulative sum. There are ways, depending on what algorithm you're using. using their getting through their ways to do this without having to pass the uh the state back in. But in this case we are. For recursive functions you almost always have to have a base case Um base case is the point that tells the recursive function, stop recursing, we're done, just return a value, don't call back into the function again.

20:55

Speaker 1: So in this case, we're done when the list is empty. When there's no when it's just an empty list, we have summed up everything in the list, just return the summation. If that's not the case, what we do is we pop the last item off the list, add it to the current sum as the new sum, return everything back into the loop again, right? So that's our sum list, our function. And here we're calling it, we're calling it with a list one, two, three. We get our result of six. Now, we still have the same issue that we had with the iterative approach, which is if this list gets changed while we're doing the summation. it's going to you know mess up our recursive calls.

21:41

Speaker 1: This does not fix that problem. That's a mutable data structure problem. But If you were to try to solve or try to implement this in a more functional language, this is the kind of thing you would do. You would loop through it using a recursive call rather than an actual for loop. Now, one of the problems with recursion inside uh Python is if you try to go too big, if you try to recurse too many times you can get a recursion error. Now, in a lot of languages you don't have something called a recursion error. Python has built in a limit on the number of frames you can put in a stack, which by default is a thousand. You can up that if you need to, but in

22:27

Speaker 1: it's sooner or later you're going to run out of memory. If you if your list is big enough, if you recurse deep enough, you're going to run out of memory because you know the language has to keep track of everything on the stack That's where you get a stack overflow. So in most other languages, if they don't limit your recursion depth like Python does, then you'll just recur and keep going and going until you run out of memory and get a stack overflow. flow. So without tail call optimization, recursion is actually really dangerous to do. So you don't see it a lot. I've never seen it in the wild in Python. And there's a really good reason. So if you don't know how deep your your recursion is gonna go, come up with a different solution. You can always use an iterative solution. There are no problems you can solve with recursion that you can't solve with just a loop in some capacity.

23:17

Speaker 1: So in Python, we definitely tend to favor loops over recursion. So anyway, those are some of the ways that recursion gets used, and that's how we kind of get around some of the problems of mutable data in I don't think I pointed that out well, but essentially what you're doing is you're taking that list and sending a new version of the list back through the recursive loop each time. In this case we're popping the the last item off. But in other situations you A lot of functional languages have this concept of the head and the tail of a list. The head of the list is the first item, the tail is everything else. And a lot of recursive calls will use the head and pass the tail back along. So that's sort of essentially what we're doing here.

24:04

Speaker 1: So yeah, those are if you've looked into uh functional programming just sort of at a surface level, those are probably the minor players that we've gone through over so far. The big three, the holy trinity of functional programming are map, filter, and reduce. Um and this is where the more functional style really shines out well in fun in program in um Python. If you've never used any of these in Python. If you get nothing else out of this talk, I hope you can walk away with a sense of I need to check into Map, Filter, and Reduce and figure out how to use them because they make your code, once you get used to how they work, they're beautiful. So a lot of times though to really

24:49

Speaker 1: use these tools, map filter and reduce, we a lot of times end up using lambdas. Lambdas are anonymous functions. And that really just means it's a function that doesn't, that we don't name, right? Instead of saying def you know call db, we just have uh we well this is the uh This is how it falls out in Python. You use the keyword lambda. You pass it whatever variables you need, in this case X and Y. And then there's a colon that separates the variables from the body. Lambdas are used for typically one-liners. If your lambda gets much more complex than you can fit on one line, you're probably better off just making a named function. and going the typical route there.

25:34

Speaker 1: But lambdas are really good for these inline one -offs where it doesn't even make sense to make an entire function just to add two numbers together, right? You can just throw it into a lambda In this case, we're assigning that lambda to a variable named sum. We call sum just like we would any other function, and it sums up whatever we give it. You don't usually use lambdas this particular way, but you do use them quite a bit in Map, Filter, and Reduce. So let's start looking at some of those three tools. Filter does exactly what you think it would do. You pass in a filtering function, which we'll go over in a moment, and a collection to operate on. The filter, the actual filter function

26:20

Speaker 1: takes whatever filtering function you pass in and applies it to every item in the list one at a time. So it takes the first item, runs it through your filtering function. The filtering function needs to evaluate to either true or false. So if it evaluates to true, the filter will then take that item out of the list and add it to your new list. If it evaluates to false, that item does not get added to your new list. list. Technically map, filter, and reduce all return iterators now. In Python 2 they would return lists, but now they're returning iterators, which is much better. That way you can kind of evaluate that as you want. It doesn't build your entire list at one time. it uh yields back one item at a time so you can loop through it however you need to.

27:06

Speaker 1: But essentially um If you take something like that list, if we look at our filter right here, we're going to create a new list, iterator, using filter. We pass it in a lambda, that's basically just saying uh looking to whether or not the value that we're looking at is it uh even or odd, right? If it's even then we assume modulo two is equal to zero Then it gets added into the iterator. If modulo 2 doesn't equal 0, it doesn't get added to the iterator. We're passing it list 1 as the collection that we want to iterate over And so it's filtered out all of our uh I guess you can say it's filtered out all the non, all the odd

27:54

Speaker 1: integers, it's filtered in the even integers, I don't know semantics. But um any case that's what filter does. Uh You can pass it a lambda like this, or you can do something much more complex, as long as the filtering function ends up evaluating to true or false, so filter knows whether or not to add that value into the new list. The same kind of thing is true for map. Map takes a mapping function and a collection to operate on, and then it applies that function to every item in the collection one at a time. It does not filter out any items. It is not, you know, if you pass in a 10 item list, you will get back a 10 item list, or you should get back a 10 item list. So

28:39

Speaker 1: but what it does is it says, you know, I have this whole list of users and I want to transform them in this particular way. You can use map and give it whatever trans whatever uh function that's going to transform your list of users and then give it the list and then it will go through and do the transformation for you. So you it's a a simpler way of you just don't have to call it all in a loop, right? Like normally if you're not using map, you might, you know, just for every item in this list, call out to this function manually. Map does it more for you. Map is actually a lot more efficient at it. It'll run a lot faster. So map is if you have one of those sort of simple situations where you just need to pass every item in a list one at a time to a function.

29:24

Speaker 1: Map is a really good alternative to using a for loop. And same kind of thing here. We're calling map, we we're using list one. We're calling map. We're passing it this lambda, this anonymous function that all it does is take whatever item I give it and multiply it by two and then put it back in the new list. So there we've taken list one and we've multiplied it by two, every item in there by two, and get back a new list. And you'll notice Even though we did a uh filtering on the first on the list one earlier, we didn't actually adjust list one at all. We got a brand new list back. Again, uh because this is happening, because These functions were built, map filter, and reduce, were built to work this way.

30:11

Speaker 1: They're much more efficient than if we tried to reproduce them ourselves. So this is actually a safe, efficient way to go through a list of items and do something with it rather than just sort of manually looping through it, risking the the possibility of adjusting your list in line or anything like that. So this is a really safe way of doing this type of thing. And the last of these is reduce. Reduce is a little bit different than the other two. Sorry. So what reduce does It starts off the same. It takes a reducing function, all right, and it takes a collection to reduce. But the reducing function is a little bit different.

30:56

Speaker 1: It doesn't just act on one item in the list, it acts on two items in the list at a time. You can actually send it an optional initial initial value. So the reducing function instead of acting on the first and second item, it'll start off acting on the initial value that you send it and then the first item And the way it works is this. Your reducing function takes these two items, does something to it, so that the output is one item of the same type that comes in. Now it we're not strictly using type, you know, like integer versus string versus whatever. But essentially What's going to happen is it will take the reducing function. The reducing function will take two values, do something with it, spit back out a new value.

31:43

Speaker 1: That new value then becomes the first parameter when it calls the reducing function again and the next item in the list becomes the second parameter. And so let's take a quick look here. And this reduce reduce was the one that for whatever reason was hardest for me to wrap my head around exactly what it's doing. Map and filter were pretty straightforward for me, reduce was a little harder to overcome, so I've got a couple of examples that might help it solidify. But in any case, this first one is the sort of standard reducing example We're calling reduce on this anonymous function that just takes two variables and adds them together, right? So just like we did above when we were looking at lambdas

32:29

Speaker 1: initially, we're just taking x and y and adding them together and returning that value. What this is going to do though is it's going to start with 1 and 2 out of the list. That's going to be the first X and Y. It's going to add them together and you're going to get 3. 3 is then passed back in as the X. And the next item in the list, which is also 3, gets passed in as y. Those two get added together to get 6. 6 gets passed back in as the X, and the next item in the list is 4, gets passed in as Y. And at the end of the list, you end up with your sum of 10, right? Now This is the same kind of thing that we saw above, right? We saw an iterative approach and recursive approach to taking a list of numbers and adding them all together.

33:19

Speaker 1: This is using a reducer just to do the same thing and it has it's so much better. If this is the kind of thing you need to do, you need to start looking at reduce For one thing, before when we tried to use the recursive approach, and we did a we added a thousand, we blew the sack, right? Or you had that recursive error. Here, because of the way it's built behind the scenes, it adds the first thousand numbers altogether without blowing through the stack. It knows what it's doing. But It also does it really fast. That's the first thousand numbers. It's the first ten thousand numbers. It's the first hundred thousand and one numbers. I mean it's super speedy. It's incredibly efficient. This is probably much faster than we could manually go through.

34:06

Speaker 1: It is much faster because when we were doing the uh recursive approach above I started it off and we had to wait for a moment for it to blow through the stack. This is going through it just lightning fast. These things were made to do this type of work. They're very efficient on the back end You really need to start looking at map, filter, and reduce as viable options for whatever kind of uh like wherever you think you can find you can put them, it's probably a good place to put them because they really do make things so much easier and so much faster. I want to go through one more quick example. There's a couple of examples you can pull them down and look at them. if you want. I like this example. Essentially what this is going to do is it's going to go through and count, it'll sum

34:53

Speaker 1: up all of the A's. In all three of these strings, right? So this is, you can imagine this is a DNA sequence. It's got three A's here. The next sequence has two, and the last sequence has none. The reducing function in here is taking a and x. In this case, we're starting with initial value of zero. So the first time it goes through, the a is zero, and the x Is whatever the next item is. In this particular case, x is that particular string. What the reducing function does is it takes that string does a count of the A's, adds that count back into whatever the A, whatever the A value is, and kind of sums it up that way.

35:39

Speaker 1: So this is the same kind of problem we were trying to solve uh above uh more or less we're we're um anyway I forgot which one it was but um in any case this is a really efficient way of like you can give it this huge uh s list of DNA strains and it will go through and tell you all these markers in it just by using a reduce. You can also mimic, map, and filter inside Python using list comprehensions. We list comprehensions are something I'm sure every one of us has done day in and day out. You can throw a lambda inside the list comprehension. If the lambda is too ugly, you can pull it out. You can also

36:25

Speaker 1: throw a filtering uh function more or less inside a fill inside a um list comprehension And uh anyway, so these are tools that are already built into Python into Python that we use day in and day out, but we don't think of them as being functional programming, perhaps, but they really are. They're at their core they do the same type of thing that we would do in functional programming. So that leads us to the question of should we bring functional programming into our code? Because it is a really different paradigm. I think of course you should. Functional like I think I've shown in a couple of different places, functional programming can be less verbose, it can be much more efficient, and it can help you think through problems in a way that maybe you weren't

37:11

Speaker 1: you weren't able to think through before, so maybe solutions become more obvious when you're using a different set of tools. But there are some very real drawbacks, as we've also seen, uh working with immutable data can be slower, especially in Languages like Python. Again, we have there's a persistent data structure library that's in Python now that someone's building. I don't know anything about it. I haven't had a chance to look at it If it turns out it's uh it works well, then I'm going to start trying to use it more in my code because it's much safer. But in the but immutable data is still slow in Python, uh as it's at the built-in versions. Recursion can blow through the stack very quickly, as we've seen, so recursion is not a good solution in most cases. And functional code looks sort of weird. Uh I have a coworker who loves to throw functional code into our Python.

38:01

Speaker 1: And every time I hit it, even though I like looking at functional code, every time I hit it, it's still a bit of a speed bump. When I first started hitting it, it was a brick wall. So it like totally slowed me down and stopped me. But now that I understand what's going on with it, I can scan through it more easily and understand it. So if you're going to bring functional programming into your code, probably something you need to discuss with your team or your management, if you're working on a team, and decide as a team, hey, is this something we really want to approach? Because if you If you just start throwing it in there and your teammates don't know anything about it, they're going to have quite a learning curve, but you can help them learn and that's kind of fun. At least I think. So that's our uh speed run through uh functional programming in Python. Um how much time do I have?

38:46

Speaker 1: Okay, all right. Um does anyone have any questions?

38:55

Speaker 2: Uh thank you for the talk. So you mentioned that you know in the In some situations it might be good to bring functional programming into your code. But uh I was wondering, it sounds like you are saying it can be possible to just have a bit sprinkled in here and there. Is that right? Is it kind of do you recommend uh you know having it basically be the paradigm like choosing one or the other or do you think it can succeed if it's half and half or sprinkled in?

39:21

Speaker 1: Yeah, I would definitely in Python it 's better to sprinkle it in. Like Python, while it supports all of these tools and And a whole slew of others. There's the funk tools library in Python, which is really great for doing these things efficiently. Python is at heart an object-oriented language. And so making that full transition transition into a functional paradigm probably isn't really going to work out. There are a lot of good places where you can put it where you might not think you can. One thing I didn't I didn't get a chance to touch on is um the idea of data pipelines. So one of the things uh you can do is compose functions which is sort of like if you ever done any Unix programming where you pipe uh output from one function call to another, or if you've done uh

40:06

Speaker 1: like maybe node working with promises where you can then one function the results of one function call into the into the next that's composing functions um you can build really great data pipelines doing that and you can do that in pipe in python and python are really great with like uh data science y kind of things so there are a lot of places where you can use a more functional mindset but it's probably not the paradigm you're gonna run with completely inside python. You might be able to and if you can let me know because I want to know how that works out. But my uh I've never I I've never been able to do that

40:39

Speaker 3: Hi. Great talk. Thank you. I was wondering other other than considerations of who else is reading the code, whether or not they're familiar with functional programming, um are there technical differences differences between the two and how should you decide which one to use?

40:59

Speaker 1: Oh I don't know the technical differences. I I there there are. I'm just going to assume that there are. I know the the difference that you're going to run up against would be efficiency. and speed. My guess, and I again this is just a guess, but my guess is now maps are probably they're not going to be any slower than doing a list comprehension. I can almost safely say that. They may be a little more efficient, a little faster because of the funk tools that have been brought in. Map and filter are still a core part of uh Python uh reduced has to be brought, has to be imported. Um but I I'm gonna I'm gonna go out on a limb and say it's a coin toss

41:45

Speaker 1: probably more or less. You might get a little more efficiency with one one or the other, but you're probably not going to notice a huge difference. If I'm wrong about that, I'm sure the internet will let me know.

41:55

Speaker 4: Has incorporating functional programming in your code changed your caching strategies at all?

42:01

Speaker 1: Um It's not changed our caching strategies. Uh I can see how it would. We just we haven't gotten to that place yet. Really, um there's there's only one or two other people at my office. who uh start implementing functional programming so we're still sort of like kind of I'm not even like not even as a company we're building it Yeah, you know, like it's just something that some of us know about and like, oh yeah, this is this is a really good place to you know throw a filter or throw a map in or whatever. Um our former architect loved functional programming and he's the one that's first started putting it in and he's got weird functional stuff all over the place and that's where I sometimes hit a brick wall uh looking at functional code but I can definitely see how

42:48

Speaker 1: it can change our um uh caching strategies especially since it's uh a lot of these like map and filter are returning iterators now so they're lazy evaluations we don't have to like cache this whole huge you know change of uh of a list or whatever we can sort of slowly build those changes and use them as needed. and then kind of get rid of them.

43:12

Speaker 4: All right, great. Thank you very much.

43:13

Speaker 1: Thanks. Thanks, guys.

Questions this talk answers

Why use functional programming?

The speaker says it can make code more expressive and concise, improve efficiency, reduce certain bugs, and make concurrent or parallel programming easier. Its benefits are strongest in languages that actively support the paradigm.

Discussed at 1:46

What is functional programming?

It is a programming style centered on pure functions—functions without side effects—that transform data. Unlike object-oriented programming, it generally treats data more openly and tries to minimize unnecessary side effects in the middle of the program.

Discussed at 3:20

What are higher-order functions in Python?

Higher-order functions treat functions like ordinary values: they can be stored in variables, passed as arguments, or returned from other functions. This is the mechanism that makes Python decorators possible and is useful for separating database setup from database-specific operations.

Discussed at 4:52

How do immutable data structures work, and why are they useful?

An immutable data structure is never changed in place; an update produces a new structure instead. This prevents one part of a program from unexpectedly changing data used elsewhere, although traditional immutable structures can be slower because of copying—persistent data structures reduce that cost.

Discussed at 12:09

What do map, filter, and reduce do in Python?

`filter` keeps the items for which a predicate is true, `map` transforms every item while preserving the collection’s size, and `reduce` combines items into a cumulative result. In Python they return iterators and are often a concise, efficient alternative to manually writing loops for these operations.

Discussed at 24:04

Should you use functional programming everywhere in a Python codebase?

No. The speaker recommends sprinkling functional techniques into Python rather than trying to make the entire codebase functional, because Python is fundamentally object-oriented and a complete transition is unlikely to work well. Functional techniques are especially useful in places such as composed data pipelines.

Discussed at 39:21

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos from DjangoCon US