 in a world. Oh, we're recording. All right. So category theory for mortal programmers and the reason for the title, because some of us would actually like to grok this stuff in our lifetimes without actually having to obtain a PhD in abstract math. If you Google category theory, you're likely to come up against this thing. A monad is just a monoid in the category of endofunctors. So what's the problem? And I know we all had a prof like this in university, right? Now, disclaimer, speaking of university, I've only got undergrad maths. And I have, and that was 20 years ago. So limit your questions to simple, please. So a little bit about me. You all know me, Stefan. Work at Pivotal Lab Singapore. Shout out for the old Twitter feed there at Beard Papa. And then the blog, which you may wish to visit, even though it was last updated in November of 2015. Right. So what is category theory? In as simple as terms as I could possibly explain it, I would start by saying that as so many things that we programmers love, it's an abstraction. Programmers like to work with abstractions. We like to think in terms of abstractions, because it makes our jobs easier. It allows us to think about the larger issues without having to necessarily drill down into the details. Category theory is exactly that for mathematics. It's a branch of mathematics which was developed specifically to encompass really abstract thinking, where the thinking is ordered at such a high level abstraction that the things which is A, B, and C there, they're called objects, can literally be anything. It can be a person. It can be a human being. It can be an object, like a fan. Or for our purposes, they're going to be elements of our programming languages. Specifically for the purposes of this talk, I will be limiting our discussion to functional programming languages, because that is where category theory is actually most applicable. People do apply function or category theory to languages like C++ and JavaScript. And if you're interested in that, you can actually find quite a lot of information about that out there as well. But we'll only be limiting ourselves to talking about functional programming languages, specifically Scala, because that's the only one I know. So sorry, Clojure and Haskell guys. Objects, anything. They are the A, Bs, and Cs in this little diagram. The arrows are called morphisms by category theorists. We can call them arrows. I will probably keep calling them morphisms, because it makes me sound a lot more intelligent about the stuff than I actually am. So the blue bits are morphisms. They are directed lines between the objects or between the things. And next slide will give us immediately, I'm just going to jump into how this stuff relates to programming, because that's really what we want to know, right? So for our purposes, a thing, F, A, and B, A and B are types for our purposes. F is a function with an input A and an output B. So F takes a parameter A and returns something of B. G is a function that takes an input of B and returns something of C. And then G following F, that little notation at the bottom of the screen there. This is a special mathematical notation that category theorists like to use. G following F, which will make sense in a minute, because it looks like it should be F, little circle, or composition G. But it is actually correct to write it in this way, because you'll see in a second that what it actually translates into is the composition of these two functions, meaning that you call the one and then you call the other. So when you say G following F, the way which we normally do this in programming terms, is we call function F first, pass the output of function F into function G. And that gives us the composition, which translates into a new function, which takes an input of A and gives us a result in C. That bit at the bottom, this composed function, this is the thing that we're interested in. That is actually the thing that makes category theory interesting for programmers. One more thing, domains and co-domains. The input is normally referred to as the domain. So A, in this case, would be the domain of the function. And for those of you guys who did numerical analysis in university, this should be familiar territory. And the co-domain B, same thing. So categories have a definition. So there's a standard definition of what would actually make something a category. Although it is a little bit abstract, there are specific rules which govern what can and can't be a category. And the reason for that is in the universe of category maths, same as with all other branches of mathematics, unless you lay down some rules, things start falling apart. And you can't really have firm proofs, the way that you need this to be able to mathematically prove something. Is you construct a little magical universe constrained within the laws that govern what is what. The little graph on the right-hand side contains pretty much everything that would constitute a category. All of this, so very important to note, this whole thing is a category. It's not that A is a category, or B is a category, or C is a category, although they could be. But that takes the level of abstraction one level higher. We'll keep it simple. We'll just say that all of this stuff is a category. And for our immediate simple purposes, A, B, and C are primitive types, maybe objects, an integer or a string or a sequence could be an A, a B. And a function would be an F or a G that takes those types as inputs. A slightly higher level of abstraction, which I'll get to next, would be where we work with higher-kind types in functional programming, generic types, or templates in C++, where you have a type constructor, meaning that the thing that you pass into the function is not actually a realized type, like a sequence in Scala, for example, or in Java. It could be a list of string, but before it's a list of type string, it would be a list of anything. That list of anything, that unrealized type, that is also something we're interested in. Now, if you haven't done functional programming before, you are maybe not aware of the fact that functions themselves are first-class objects, meaning that functions can be passed as parameters as well. For the purposes of category theory, and for what we want to do, we want our A's, B's, and C's also to be functions, meaning that the input and output potentially of our functions F, G, and G following F potentially also will be functions, not just necessarily data. Let's look at some of the definitions on the left-hand side of the screen there. You have the objects in our category, our A, B, and C. The morphisms, or the arrows, are F, G, and of course G following F, which I didn't write in. Then composition is a very important part of a category. In order for something to be a category, there needs to be, if you have an A that goes to B and a B that goes to C, in order for this to be a category, you must have composition in it. So the little inverted E there is my attempt at sounding, again, a little bit more mathematical than I actually really am. That, of course, is just the mathematical symbol for there exists. So given a function F that takes an input A and returns a result B and a function G that takes an input B and returns a result C, there should exist a composed function G following F that takes as its input A and returns a result in C. That is composition, and that is a requirement in order for something to be a category. Then there is the identity of the category, meaning that the identity function is defined as an A, it's this little arrow right here. So it is the same as the F in the G there as meaning that it is a morphism or a function. It is just a function that takes an input A and returns an A. But very important distinction between the identity function and something that they call endofunctors, meaning I'm not going to talk about endofunctors. I'll talk about that later. The distinction here and the important thing to note, if A is an integer, the identity function is not a function that takes one and returns two. It is a function that takes one and returns the same result, one, meaning that it leaves the, it's a function that when you apply it to your input, the input remains unchanged. That's what we define as identity. Now these things are still all a bit abstract and we're going to get down into the integrity immediately after two or three more slides. So category laws, the associative law and the identity law, again these are just constraints placed on the branch of mathematics so that things make sense and so that we can think and reason about these things in a mathematical manner. Associativity, familiar to all of us, it means that if I have a category diagram that as the one is on the right-hand side here, where I have a g following f, a composition of a g following f, and then, so this one is in here just to draw a similarity with the one from before so that things don't get too weird and then a h following a g following an f, which is just nested function calls. You're calling the one function with the next function with the next function, right? So it's simple composition. This has to compose meaning that if I evaluate h following g first and then evaluate the composition of f, it should return the same, it should be the same and have the same effect as first evaluating h and composing that with the result of the composition of g following f. And then the identity law is just as I previously explained, it is that the composition with identity works both if you apply the identity after or before the actual function and, sorry, let me start again. This is a composition of the function f with the identity function, right? So it is calling the identity function using with f as input or calling f with the identity function as input. Both of them, they should work both ways. And the reason that works is because f takes input as a, the identity function would take input as a and return output as a. So if f takes input as a, I should be able to pass the identity function as a parameter to f and the result of that should be the same as when I pass f into the identity function as input. And the result of all of that should just be f, meaning that the identity function does not mutate the actual composition in any manner. Are there any questions before I continue? Can you make this more real for me now? Okay, we're gonna get into it. So I've laid these things down just so that we can have sort of a baseline with the graphs in a little bit, but that would be the end of the little graphs and the abstract bits. So for our purposes, it is really nice to think of categories as functional design patterns. The origin of these things, since category theory was developed in the 1940s, but it only really became mainstream in the 1990s when the Haskell hackers realized that they don't really, since they don't have mutable state, everything is immutable, they didn't have a clean way of working with mutable objects. And category theory allowed that for them, it allowed them to construct transformations that where you could take an object, wrap it and work on it as if that object or collection or whatever was mutated and then the result is the mutated version of the original thing that you worked with without having had to disturb that immutable original item, right? I'm going to show you guys something that's gonna be very familiar to you. Doesn't matter which programming language you know, this thing is gonna be very familiar to you. The function definition there is in Scala, but it is actually just as applicable to anything else. If I, and the question for you guys is, and you guys will know this immediately, if I wanted to apply a function which takes an integer and returns, or that should be the square, and returns the square of the integer that was passed into it, and I wanted to apply it to each of the elements of that sequence, what function would you actually construct in order to do that, or what is the method that you would call on the sequence? Great. So pretty much every programming language these days, Ruby, Python, most of them have a map method which allows you, sorry? JavaScript. Yeah, JavaScript. They have a map method which allows you to pass in either a lambda or an anonymous function that will operate on each of the individual terms in place, and return you either a copy or a mutated version of the original sequence that you have applied it to. And yeah, map is the correct answer. We're gonna jump straight into an actual implementation of a category. This is a very special category and it's called a functor. And now I suddenly have a sinking feeling that I've actually skipped over something here which was important. And okay, I'll explain, I'll run through the code. Is that legible or should I bring up the ID? Can you guys read that? I could also just, is it okay? Okay. All right, so. Yeah, I'm okay, I'm okay. I should have made sort of like an animated bullet thing that went through it, but. Right, so we are defining, let me first explain what it is that our objective is here. The objective here is, or our use case is, we have this thing called, or this type called a my box. A my box can contain any other type. So you can have a my box of string or a my box of integers or a my box of swams. Anything, right? It could be a future. It's anything that contains something else, an option of future, a sequence. Anything that contains something else. In our case, I've specifically made it a my box. It's not a collection. It holds a single value. That value is the same as the type of the type constructor, right? So my box of T is that's the full class definition in one line in Scala, right there. It's nice and concise and to the point. We've constructed a, it's not a realized type, right? This is a definition of a type. My box, so now I'm gonna define a map function and this is the interesting thing. The first thing that you'll notice is that there is a direct correlation between the map function. The map function is sort of a commonly used word and can be used interchangeably for the categories of functors. So there's a category, a mathematical category called a functor and in programming people have just started using the map method as interchangeable and as sort of the defined method to call anything that is actually a functor. With a flat map and the rest of them have equivalence as well. For a functor, this is called map. The method is called map. So what does my method definition there actually say? Well, I'm defining a method that takes, or it's a typed parameterized method that takes a function and that's an anonymous function F that takes input A and returns output B. And the function, so that's the first parameter. The first parameter is a function without shape. The map method itself returns a function in this case. And the function that the map method returns, so let me just quickly check if I, no I did not. I thought that I added in the, I think I accidentally deleted it. I added in the general shape of the thing that I'm talking about there. That's why I was saying something got left off. Anyway, I'll try to make this clear without, so this is the bit that you should please ask questions because this might easily go over your head as it did mine for so, so many hours. So again, we have a map method. It takes, as the first parameter, input a function. It returns another function. And the function that it returns is my box of A as input and my box of B as output, as the return value. The implementation of that method is that it, the way that, what it actually returns is again, the first parameter is just the definition of the my box A, but then the return type, it actually applies the function that was passed into the map method to the value of the first parameter. And inside the box. Right, so the thing inside the box is getting transformed. Now what this actually allows us to do, and the reason why this convoluted thing without an explanation has been constructed, I'm gonna back up quickly again and talk about the fact that let's say you have a container that has something in it, in our case, a string, and you want to get the length of that string out, but you don't want to go and peek into the container. In fact, let's assume that you can't peek into the container to go and look at the thing that you want to know the length of, right? So what are we gonna do? We're going to use this functor category, which will allow us to obtain the value that we're looking at, but the value will be wrapped inside the same type as what we actually passed in. As if our whole universe were only allowed to really work with my boxes from the outside. That's correct. The value is in them, but they're okay. Yes. But maybe I could look inside what I have, but I can't call methods on it, right? I can only look at pure value only, if I wanted to shoot it as an opaque box that has a value. So I can't go in and push the boxes, it's giving the length of this string, but I have something here that will do that for me and give me a new opaque box and if I look at it, has the length of it in the box. Precisely. It has three values. That's right, that's right. So that is what we are doing here. We are actually constructing a function. So the map method here will be slightly different to the map method that you're used to. The map method that you're used to would actually already return you the wrapped value of the my box, right? The map method that we are actually implementing here, and I'll explain why it's implemented this like this in this naive example, is returning us a function that will give us the eventual result. So the function that is returned will have to be called again with an actual my box instance, which it will then calculate and compute. It will wrap whatever it is you pass into it, which is where we, which is the next line there, create a boxed value. So our boxed string, now we construct a boxed string, which is an actual instance of a string and it has Hello World inside and it's a my box of type string. What do we want back? We want a my box of type int, meaning that we want the length of that string. So what we want back is eventually a my box of type int. So what do we do? We create an instance of the function we want to apply. And the function that we want to apply is a function that goes from string to int. And this function is gonna be called rolling. It's gonna take a string as an input, return int as its output. This function is then going to be used to construct a transformation. And the transformation, so we are working on several levels of indirection here. And this is where it normally gets difficult to follow along. And it's also why your map function in your library will just return you the thing that you asked for without you necessarily having to construct all these several different levels of indirection. Now again, I will explain in a minute why in this naive example we are doing it this way because all these things normally hidden by the library for you, but they're actually important when we reason about our categories. So a transformed length is going to be a new function, which is the function that the map actually constructs for us. So the map method now will take as its input a function, in this case it's our raw length function, right? So we construct a new object or method or value in this case, all of which are by the way equivalent in this instance, since it's called transformed length of. And transformed length of is now the final function which will take as its input a my box string note that we're going to pass the box string into it and return us the my box of value int, which is the thing on the left-hand side there. If we were pairing, I said instead of the name transform length of, I want to call this the result of the map of raw length of, I want to call that variable length of box value. Would you say that made sense? Or like, because that's what I'm thinking of what transformed length of actually is, it's a thing that specifically is going to do, it works on boxes, right? Because map is specifically bound to handle boxes. So can you repeat the question I wouldn't quite follow? Okay, our definition of map above, it only works with this class my box, right? So I kind of feel like raw length of is nice because it, well, I guess he only works with strings. I'm just thinking like, transform length of doesn't tell me that I can only use this function with box things. Oh, absolutely. So now one thing to note about this naive example is the fact that all of this stuff can actually be hidden inside my box. So they've been pulled outside of my box right now. But what I could actually do as a next version of the implementation, for example, would be to take, to put all of the stuff inside my box to actually make map behave in the way that we're all used to. This is just the implementation details. And these are just the implementation details on the inside of it. So that from the client code point of view, it doesn't, it becomes simpler. Cool, okay, thank you. Right, so I want to recap the functor because, and I think actually that this slide should have been the previous slide because it would have been a leading, but I've left off the left-hand side of the stuff there. But we can go back if you guys have more questions about this. The general shape of a functor is what is on the first line there. It is the transformation that you're trying to figure out when your client code will be presenting a function that takes input A and output B. You always, in order for you to construct a functor, need to figure out how to the function, the map function that you want to construct or the mapping function, whether it's hidden or not hidden, will have the shape of the thing on the right-hand side, meaning that it will be the type that you, in our case, my box value or my box, the type of A, a function that takes the type of A as its first parameter and returns the type of B as its second parameter. This is sort of, this is a definition, this is a law. This is the definition of what a functor is. It is not, there are other shapes and that's why they're called design patterns because the shape for the flat map is actually a monad and there are specific, and no matter what you need to do or how you want to reason about the categories that we're working with, that general shape will always be the same for all functors. Every functor that you ever, all map operations always have the shape, meaning that you always have a function that you want to pass in that has the types A and B and then internally, either you yourself have constructed it or the library has constructed it for you, there will be a function that looks like what is on the right-hand side of that arrow. It will first create a typed function or a parameterized function that takes as its input whatever type it is of A, whatever type, and returns whatever type it is of B. And just to bring it back to category theory again on the next line there, the category here goes from A to B and what is on the left-hand side of the category, the object on the left-hand side here is actually the function. Because remember before I wanted to specifically mention the fact that functions are first class objects in functional programming, right? So the function on the left-hand side is the object A and that function can actually be further decomposed into a category of its own where the A and Bs are a string and an int. But at the higher level of abstraction at this point we are talking about A and B where the left-hand side, the A is a function with that signature and the right-hand side for our implementation is another function with the signature that you see there, meaning input of my box string, output of my box int. The F, our morphism or our transformation in this case is actually the map method and that again is what makes this a function. Yeah, Diego, question. Trying to rework this. So the left-hand side of the general state today, if I recall to have an operation, then the function would be something, a function that takes an operator and creates a map method, so to say. You implement the map method. The map method is what the, either the library or the programmer has to implement in order to provide the transformation that's on the right-hand side. Yes. So the function is, so you spit out the map methods that take up the operations on each one of them. Precisely. So the result of the map method will always be another function in this case. It's a function that again needs something to be applied to it and typically the thing that you apply to it is the function, some sort of a function in the shape of the thing that's on the left-hand side, but that's normally left up to you because each individual instance tend to be the same. The functor implementation for applying a map operation on a sequence of objects will need to iterate over those objects and construct a new sequence in order to construct the return type, which will be, for example, if I wanted to go from a sequence of, like the original example where I had a sequence of integers and I wanted to square each of the integers in place, I would have to iterate over them. I would still return a sequence of integers, but they would not be squared. I could just as easily have returned a sequence of strings from that. Any more questions or any other questions about exactly what's going on here with the functor? So in my head, I see map, but what I say in my head is make me a map function, make me a map one. That's right. Okay, so we're almost done. I wanted to keep this as small and simple as possible. The monad has a similar shape to the functor, meaning that again we can think about this and reason about this as something on the left-hand side that goes to something on the right-hand side. Flat map is the monadic operation that is implemented pretty much everywhere. The shape of the thing on the left-hand side for flat map is always looks like that. It's a function that takes as its input A and returns something that's wrapped in a B. Sorry, something that's wrapped in a T. Something that wraps a B. A T that wraps a B, right? And the transformation that you want to implement, the transformation function that you need to implement looks exactly the same as the transformation function for the functor. In fact, the functor, the monad, the applicative, the thing on the right-hand side always looks the same. The thing on the left-hand side always changes. And think of it in this way. The thing on the right-hand side is an intermediary step. It's the transformation function that you need to construct in order for what you are working with to be similar types. Once you're working with similar types, then it becomes much easier for you to compose things. And this is simply because same things compose easily, different things compose difficultly, or not at all. Functions are notoriously difficult to compose, some of them, because the composition sometimes does not actually do what you think it would do, and sometimes it will fail. Okay, I've got a bunch of references, and I'm now open to questions. If there are any gaps in anything that I've said, please ask me now so that I can explain a little deeper. Monads. You want me to dig into monads? No, we don't have time. I thought you were going to take a shot. So, I'll ask you a different question. Sure. We have this little kind of fonturist monad, and I suspect that there's value in understanding these terms because it lets us communicate ideas to each other more efficiently or effectively. Yeah, right? The same way that we're getting to a re-factoring, and I say, right, well, let's replace it like 10, right? That has meaning to people's understanding of some of the common re-factoring, how it really enables. Yeah. Is that why, as a programmer who's learning functional learning, I hear about rocking the words, what is the fontur and what do I want to use it, and what is the monad and what do I want to use it? I would say yes, but I think that there's even a more basic instance of why I think this is useful. The map, flat map, and all these different operations that have been defined on the standard library have been defined on a bunch of different things. You can map over a future, you can map over an option, you can map over a sequence of things, and obviously the implementations of these things are different. But if you understand the general shape of what the map actually tries to achieve, then it becomes immediately obvious what the thing you're working with should end up as, or how to end up using it. So if you understand, if you have a general understanding of the categorical shape of, for example, of the functor, of the map operation, right? Then it becomes obvious that what is actually happening, let's take the example of an option. It's easy to reason about the fact that a sequence might have map, but an option which can either be none or have an actual value, an optional value in Scala, if you think of what the map operation does to that, it does exactly this, which is that it first transforms in the implementation over here, for example, in our implementation of the map method, right? The function that needs to be returned here on the right-hand side will either return the value or it can check for whether or not, it will internally check for whether or not there is actually something to return and then return the non-value for you in its case, which is why I can use map on an option as if, instead of an if statement. It behaves like an if statement. So that behavioral nuance becomes obvious once you understand what the general class of functional shapes or design patterns or what the design pattern looks like underneath. The same for future or in the case of flat map, the fact that the four construct in Scala behaves like a sequence of flat maps that are evaluated either one after the other or in sequence depending on whether or not you're passing values back and forth. They're either composing the flat maps one with the other or running them in parallel depending on, right? So that becomes a little bit more obvious and, yeah. Okay, so again, that comes back to your statement that these are really, these are functional design patterns. So there's a bit of a value to be understanding of the function design pattern says I can take a job when I have a thing, I want to convert it to a one. Again, it's not normalizing the values, right? So that now I can pass those on to other functions that are all here with values under a similar shape. Because one of the things, for example, remember we had the problem that I was trying to solve with the functional composition of the domain model where we ended up needing a lens. Well, we didn't end up using a lens but it ended up learning a lot as a consequence about the stuff and that's why I ended up digging into category theory was the lens looked interesting and I knew there had to be a way somebody came up with this as the consequence of the same problem that I had. They needed to look deeply into something that they didn't have access to, a wrapped value that was nested three levels deep and they wanted it to be easily accessible. And I guess that libraries like Scala Z take a larger domain of functional patterns and have wrapped them within a library and most of those things are actually monadic, monadic operations, so yeah. So have you read the book Introduction to Category Careers that you recommended? I have. It's extremely dense. I haven't read all the way through. I read the bits that I needed in order to make sense of what was going on. It gets, the field of category theory is vast and it gets extremely abstract very quickly. Like mind numbingly abstract. It's serious reading. I think that I've read pretty much as much as I want to read in that book. It's a Kindle book, otherwise I would have passed you my copy but if you have a Kindle account, I can lend you my copy. Yes. So I'm just trying to understand, completely grog the concept of category theory. So from what I understand, most functional patterns can be considered as a category, right? Is that correct? So could you make it more clear by giving me the answer? I would say the other way around. I would say that these, so a function is a category. Monad is a category. And these can also be considered as functional design patterns, rather than the other way around. So give an example of what is not a category. Something that wouldn't be a category would be something that doesn't compose. I would say any method that relies on a statement or it would cease to be a function in the first place and you can't use any of that. Right, so cease to be a function. Cease to be a function. So one of the interesting examples that I saw is if you just, in this relates to monads, is if you just think of first what monoids are. Monoids are the same things, they're a bunch of ways. So if I have the set of all things that can be in A and in this case I'm gonna say it's the set of all integers. That means one, two, three, four, blah, blah, blah, blah. Right? A monoid is basically a binary operation that operates on two things in a set. Meaning that I can add these two things together. So addition is a monoidic operation that I can apply there. And remember before when we were saying that RAs or Bs or Cs in the category. In this case there would be ints. That would be an int. Because it composes but minus or divide would not. Right, minus doesn't compose, it does not compose. And remember that in order to figure out whether or not something composes we have to go back to our composition laws, right? So if we evaluate in terms of the law of associativity does plus evaluate associatively? In other words if you have a function that operator can add things together. One plus two plus three. That composes, right? It composes because that result is exactly the same. It doesn't compose because this doesn't provide the same result as this. So minus, sorry I need to put one operator. Minus does not compose because the order in which you evaluate, and the minus here would be the monadic operation which you're trying to evaluate whether it composes. So that is an example, the minus operation here of something that does not compose. The same holds through for functions as well. Functions on certain types of input compose. Functions on other types of input do not compose. And it's those edge cases where functions on types of input that you're interested in do not compose that you want to go and build your specialized transformation function like a flat map which make them compose. And that is sort of the interesting part of monads. That you can actually construct your own monads that allow your own types to compose once you've figured out that they don't compose. Identity, right? For instance, you would think multiplication is associated and therefore for any integer it should be at a category, but zero screws up because zero times and the identity function is one in this case, is actually zero. And zero times seven is also zero. So in fact, you can't include zero in your set of objects. So if you take zero out and just have one to infinity then it becomes. Any other questions? Is it a wrap? Can you send around those links or the slides? I'll send the slides now.