dogear

enter for all results · esc to close

Solving Problems The Clojure Way

youtube.comvideo

transcript

[Music] before i get started i kind of just wanted to get a feel for the kind of skill level with closure that we have in the audience today so i'm just going to ask raise your hand if you have let's say less than 50 hours of coding enclosure okay so we got a good hand with that okay and some people who have a bit more um the stockist talk is is focused on uh kind of the new people new to closure or completely new to closure but maybe for those of you that have been working with it for quite a while you might learn something new as well fantastic all right let's get started so for someone who's new to closure and wants to learn closure and do and work like most experienced pro closures programmers work closure asks quite a lot of them especially if you come from a background working in languages that have a c like syntax that are object oriented and where you just kind of write code compile or restart the thing and see what's going on closure is quite different and almost the entire programming experience is a little different there's the syntax which you know closure has a lot less syntax than most other languages but it is different and so that's an obstacle in something someone new to closure has to kind of get over and learn then like with any language there's figuring out and understanding what are the things that are available to me what are the libraries but even specifically what are the functions available to me in the standard library since closure is a lisp closure programmers don't really think of code as lines in a text editor but instead they usually think of it as shapes and as the s expressions themselves and typically they work with them directly and so one of the things that you should learn is how to work with a like with a structural editing program like paredit so that you can start thinking about your program in terms of structures as opposed to just text also the repel is a core integral part of how you work with closure in some of the previous talks you might have seen people don't just like completely restart their program and then and it's not even like let's say like ruby or or irb or node where you type into this other repple thing that's running typically you want to set up your system so that your can evaluate code at any time inside of your editor and see these are all very kind of different experiences from the mainstream type programming experience and then the other component is idiomatic style it's trying to change how you think about programs and program in the way that a given language intends and in closure that is also not mainstream it's closure is a functional programming language we'll talk about it being data driven and these are both things that are quite different than the rest of other programming languages and that's the part of that i'm going to talk about so it's these big idioms it's how do you think about the entire program how do you organize how do you problem solve less so than like little idioms when we talk about programming languages which is things like oh okay should i be using this kind of function or this kind of function this situation oh should i use partials or like other little tricks that kind of stuff like syntactical level stuff i'm not going to cover i'm mostly going to be thinking talking about kind of uh the the larger idioms of how do you approach generally your programs and kind of what do we like in our programming and what do we avoid so really the you know i call the talk solving problems the closure way i could also call it basically thinking enclosure and when we talk about closure really thinking and closure means thinking in a functional programming way and a data-driven programming way and i'm to try to get you a good intuitive understanding of what these two things are because if you're not used to writing in a functional way or you're not working in a data driven way you might just have a surface understanding like when i ask someone who doesn't really do functional programming oh you know what do you think of what's functional programming they usually either give me two answers one is something something lambda calculus which yes kind of true but as a practitioner i don't know what lambda calculus really is i've been writing closure for like six or seven years and lambda calculus is not you know is not something that i really care about at all and the other thing that people typically say is oh right haskell haskell is functional right and so anything that looks like haskell that's functional programming and that's also not accurate because haskell you know it is functional programming but it's also very pure functional programming and it is uh very strictly typed and working in a strictly typed language is very different than enclosure which which is very is dynamically typed and doesn't really you know we'll talk we'll talk about this later we don't really think about types almost at all and it's a very very different experience so it's sometimes hard to extract you know what in haskell and what about the experience of haskell is functional programming and what is wrestling with the type system so we'll talk about those now whatever i'm going to say is not really authoritative nobody gets to say what closure is and what idiomatic closure is not even rich hickey you know idiomatic style of is kind of decided by the community and by libraries by what people write and what people complain about and things like that so this talk is really my attempt at distilling what closure is or what people like or how people like to write closure in 2019 and i say in 2019 because if i was giving this talk four or five years ago it would have been a little different and probably if i were to give this talk in another three or four years it again would feel a little different because styles of writing change over time and we'll see that actually because this whole data driven thing i probably wouldn't have been talking about it much four or five years ago to better understand these both functional programming and data driven programming i'm going to as part of this talk work through a toy problem so i'm going to throw up some code on the screen and we don't need to understand it all um it's mostly going to be like about the shapes and the look of the code and i'm going to be highlighting certain things and i will be doing these examples actually in javascript and not closure so you might be disappointed you want to see closure code at a closure conference but i'm choosing javascript so that for those of you who aren't familiar with closure syntax the syntax doesn't get in the way it just looks like you know your looks like c looks like java looks like javascript these all these languages kind of have similar concepts and ideas and javascript is fortunately flexible enough that i can show off these various concepts and techniques but then you know if you're interested i encourage you to convert these things to closure afterwards but you know this talk is about thinking and closure but not necessarily writing enclosure okay so the example we're going to work through um is implementing this game called gops it stands for game of perfect strategy it's also called goof spiel it's a pretty simple game it's not really very fun in person but it's simple enough for us to implement and the way it works is that you take let's say a deck of cards and you split it down to the suits and you give one player one of the suits so all the cards in one suit you give another player all the cards in another suit and then you take the third suit and you set it aside for use as bounties and the fourth suit we throw away we don't need it um and this is the way we're setting it up is just as a two-player game and so what happens is that every turn we draw one of the random bounty cards so in this case that's like the ace and then each player simultaneously chooses one of the cards in their hand and plays it and then whoever has the bigger hand but the bigger number wins that bounty so basically you see a bounty and then the players must decide out of the cards that they have which one to choose it's a you know they use this game in like academic settings to like talk about you know strategy and stuff like that um because it's a perfect there's perfect information because you know exactly which cart what numbers you have and you know what numbers the other player has and you're just kind of betting and figuring stuff out and but it's pretty simple now if we were to think about okay how do we solve this in a kind of classic standard imperative way so if we go back you know not even object oriented how do we do this and we might you know set up something to store the cards so the three kind of sets of three arrays of cards and then we might have a loop that goes through each turn and you know does the stuff in order to make the game go so let's actually take a look i did a quick implementation well actually before that our goal for this is going to be to run one game and have it print out something like this you know on turn zero the bounty was one player zero played the six the player one played their one and so on and so forth final at the end we print out the score and who won so that's our goal and we might in an imperative way implement it kind of like this i don't expect you to have to read it but i'll kind of briefly go through the idea you guys can see my cursor nice okay so we have a function called run game it sets up a bunch of variables here you know we're keeping track of the turn because we're going to print it out we keep track of the bounty cards i'm only doing eight for in in each suit the players also each start with eight their scores start at zero and then we have a while loop while we still have bounty cards left to take we kind of play each turn we randomly pop one of the bounty cards that's a helper function over here then we print out what the turn number is and which bounty we just chose and then we've implemented two separate strategies for the players so one player is going to play randomly so they're just going to draw a random card and the other player is going to always play the card that they see these are the kind of i chose like the simplest strategies just so that we don't have to write a lot of code for this one we increment the turn and then based on whose card was higher we give that person the score we increment their score by that bounty card and then we do a bunch of printing at the end to print their scores because this is outside of the loop we print their scores and who whether who won and or if there was a tie because it's possible to have a tie i guess i'll mention just because it's not you know immediately clear that if there is a tie if we both if both players played the same card and the i'm just doing nothing so neither of them get the bounty i mean you could play the game with different variations but that's the way i've chosen here so this makes sense and it's fine and you know if this was a all that you had to do it'd be okay it's not terrible and you know that's the problem with toy examples and short examples is that you don't really feel any of the pain of this kind of style of programming because it's only like 50 lines of code but with imperative programming you know that was the kind of initial style of programming where we would just change things or like every almost line of our code its purpose is just to increment some value or change some variable somewhere but many many years ago as projects got larger people felt that this style has limits especially as programs get larger you start handing hitting kind of limitations in terms of you know we now have state all over the place what's happening here what are my variables are being swapped out by someplace else and like it becomes hard to understand so there's kind of actually a schism in the in the programming community in terms of how to solve this and we kind of have this idea of like okay well a large amount of the community went towards this idea of object-oriented programming and then the folks here would know that there was also another community of people that instead would recommend doing functional functional programming approach instead so let's take a look at how an object-oriented programmer would approach this problem instead so oop practitioners would say yes the problem is state we think you know any of these values and variables if they're being changed all over the place that gets confusing so let's organize that better and to do that we've given you we're giving you a new tool objects so you can combine your state into and some methods you can well you can separate your state into different kinds of things and you can have methods that only work on the those pieces of state and we call those objects and now now that you have objects you start thinking about programs not just as a series of steps and mutations of values but kind of as a system of interacting agents or things that communicate with each other past messages or or provide services for each other like when i worked in object-oriented programming this is kind of what's happening in my head right now if you're being very careful and organized in how you do object-oriented programming you might want to make sure that kind of there's only a single direction in which kind of a hierarchy of your objects but occasionally you do end up in a kind of just like a graph kind of prob situation so as an object-oriented programmer if i looked at the imperative code and looked at that problem i would say you know what we should create a game class that will take care of the outer loop and play the turns we should have players and the players will keep track of their each player will keep track of the scores on each player and we'll have a deck of cards whose job is to just kind of help keep that list of cards and allow you to remove or add things to it and the game would also have this deck of cards and so i've taken the code from before and just highlighted kind of how an object object oriented programmer would say oh you know what you're you're mixing concerns here there's a bunch of stuff here that would better fit with cards and or a bunch of stuff that would better fit into a player and so they would want they would suggest refactoring this and splitting it out into these different classes and objects now thanks to the advances in javascript in quote-unquote advances in javascript we now have classes and class syntax in javascript so we can actually do this so i'm going to magically refactor this code into those classes and so here we have the game class still has the play turn and play game and it's all very very similar looking code when i was doing this it was literally just like copy paste copy paste it's almost all identical except in a bunch of places we now have like this dot whatever for the state so the game state here is a list of players that are player objects the list of cards and the um the turn player objects they just keep track of their score and their cards and there's some methods to help increment and work with the score and we have two separate implementations of the play card method whose job is to pick for each player which card they play and the deck is just really kind of a wrapper around an array that helps work with this kind of list of cards so you can like remove a card randomly like we had before or remove a specific card or check if there are still any cards left in the deck and the oop approach has worked quite well i mean the industry basically like if you think about it industry has been dominated by object-oriented programming to the point where you know javascript added this way of doing things but it's not without its detractors and it's not without its problems i'm not going to go into in-depth what you know the problems with object-oriented programming are other than to just say that you know functional programming does have a completely different set of answers to a similar problem so when you know with object-oriented programming they said let's organize state use objects and think of programs as interacting agents functional programming instead agrees state is confusing but says let's avoid it let's just say state is bad and when i say state i mean any values that change in place so if you have a array and you want to increment it in an imperative programming language you just increment that array and in functional programming we'd say you know what no that's bad because then when this array is being passed to some function and that function changes it and we didn't expect that things go wrong like basically state is the root of all problems so let's try to avoid it now compared to you know classical imperative programming and functional programmer functional programming languages also gave us a new tool and that was the function and that's to distinguish and we use the word function to really distinguish from the idea of a procedure and the main difference between a function and procedure is that a procedure you know you can declare you have to give it a name and you can still pass procedures around maybe by name you can pass the names of procedures around but you can't pass a given function that you create dynamically as a value whereas with functions you can so procedures can't really be passed around by value and you can't create them on the fly and pass them around whereas with functions you can now if you've worked with javascript you know anonymous functions um you know they're they're everywhere and so it's not that crazy of a concept but at the time it was definitely something very different and if you work in like a strict object-oriented programming language they don't really have um they don't have these functions that you can just pass around sometimes they're also called lambdas but they don't have them like java only recently you know had made it possible to pass around functions on their own uh and barely anyone uses that because you know why do you need functions you just have methods on objects but in functional programming we say no no we don't need an object you can just have a function it can just sit on its own and the the model the mental model of functional programming isn't steps to achieve some solution isn't interacting agents working with each other it's really mostly thinking of a program as a pipeline of input to output we think of okay what is the data that our program is going to be receiving over time and how do we transform that data into the outputs that we want now you might be thinking well we want to avoid state but mutable state and side effects are inescapable that's kind of almost the point of programming we want our programs to do something have some side effects if all our program did was just make our laptop hotter and then and then you know contribute to the heat of death of the universe it wouldn't really be useful i mean unless you're cold but you know the purpose of programs is to do something even if at the least it's to print out a single number if all we want to do is take in a bunch of inputs and print out a number that's still some side effect it's printing to the console um but you know this typically you know real programs have other side effects that we think about and it's things like you know communicating to a database and changing things in a database sending an email or triggering some other external api making an http request to another server or writing to the file system these are all kind of effects that a program typically would want a program to have on other parts of your computer system so promotional programming would say okay yes we do want side effects and so it's not saying let's get rid of all state let's get rid of all kind of side effects functional program is saying but let's just do our best to avoid it and how how do we avoid it so how do we avoid immutable states so values that can change how do we avoid actually mutating that state in our programs and how do we avoid all these other kinds of side effects we kind of three functional programming has like three techniques three little tricks that you can use and those are minimizing concentrating and deferring so minimizing is just trying to have less less state so less values that we're keeping around concentrating is saying okay well if we have to have values let's keep them all in one place rather than throughout the program and deferring well we'll get to deferring because in a moment and this minimized concentration also applies to mutations so with mutations we're saying okay let's try to have as little parts of our code that actually change values in places so the places that change state and if we do have to have mutations let's keep them as much together rather than spread throughout the program and similarly side effects let's try to decrease the amount of places that we do side effects concentrate the places that we do side effects and if possible defer them either to kind of the last step in our program or to a completely separate system right if you think too about like a database um you're not dealing with necessarily doing all this fancy work that the database needs to do in order to keep track of the state and do a query and whatever presumably there's a whole bunch of state that is necessary from your point of view of using a database it's just like here's a command you figure it out talking a bit more about minimizing how do we minimize state the tools that functional programming gives us is well there's a few things we can do one is we can derive values if possible so sometimes there might be states that you want to keep but you could actually completely avoid it an example i give is for example if you were trying to play the game of tic-tac-toe or you know program a game of tic-tac-toe you might think okay i'm gonna have to keep track of whose turn is it is it x's turn or o's turn but actually you don't need to keep track of that at all because you can determine whose turn it is based on how many pla how many plays how many grids are already full so if you're being hardcore functional i would say i wouldn't keep track of a variable called turn i would just have a function that based on the state of the grid would tell me if its x's are almost turned another technique is to copy data instead of mutating it in place and you'll see this a lot and this is a really fundamental because you know as part of programs we do want to change data structures but with functional programming we just copy them instead and you might think okay that that sounds crazy if i have an array of a thousand items and i want to add one thing to that array you want me to make a whole new copy of that array and then add that one thing that's that seems incredibly inefficient but thanks to a bunch of math and computer science and really smart people we came up with data structures that allow doing that efficiently which are called immutable data structures in the case of an array in functional programming languages like closure if you have an array and you say okay add an element to that array that new array is not a full copy it's actually just two references a reference to the original array and to that value so there's like almost no new memory being used just like two pointers and that one added value but you might say oh but if you were to do that what if someone changes that original array now you're you know that the if you change the original array then the one that you're the new array is also going to change but no because we're saying you can't change anything so it's okay you can make these derived values of like this array is actually based on this other array that's based on this other array that's based on this other way because the systems the libraries don't let you change those previous arrays and you might think okay well in practice this sounds kind of complicated now i have to think of an array being derived from a previous array being derived from some other thing but in practice you don't it just feels like working with any other thing it's like take an array uh concat it with another array the in closure and most other languages you have a whole a bunch of these data structures and but most importantly you have arrays or vectors you and you have maps or objects or dictionaries depending on what language you're talking about and those two data structures have all the common things that you would want to do with them implemented in an efficient way in a a functional way another technique we have is using lambdas or using these anonymous functions if we create functions dynamically they will remember values in their scope and you can make use of that and we'll see that in a few places and more importantly you can also create higher order functions so typically the examples like map and reduce super super you know everyday functions that you know i will use absolutely every day that i'm programming in closure and they make they like map and reduce for example get rid of more situations where you'd be using loops right if in javascript if you wanted to say okay if you well if you didn't have map and reduce and filter which you do in javascript but if you didn't if i asked okay you know find me all the unique items in this array you'd have to create another array loop through the first one and then like a sock stuff into the into the new array that you created or something like that whereas with map and reduce you just pass the dynamic function and map and reduce do that stuff for you and under the hood they do that recursively and that's kind of our last technique pretty much every loop solution to a problem has an equivalent recursion solution and we just prefer recursion because you don't actually have to keep track of state as much the system you basically defer that problem of keeping track state keeping track of the stack to the to the compiler into um to your language system you to the vm and so that's one less thing that you have to manually keep track of so three techniques minimize concentrate and defer and i'm going to go through an example of libraries and programs that you might have encountered that actually have done this so you can kind of see what the difference is in taking a functional approach so my first example is jquery versus react or the way that jquery works versus how react think how we think and react in the early days of the web and the way when we did things with jquery would be our programs would be just full of imperative um stateful manipulations of the dom so in the browser you just say okay take the find this object change this thing on that object and that would be state inside of the dom and all and if you wanted to change what the page looks like or whatever you'd have to keep track or keep checking what is my current value what do i need to do to change it let's do that to change it and then react came around a few years ago and said you know what let's not do all these mutations let's instead let you write and think about your program as just a series of functions and each function declaring a part of your interface and you chain these functions together to compose your interface and all of that mutation and figuring out what's currently there and what needs to be there we'll let the library take care of that we'll let some external system and so this is an example of trying to defer concentrate and defer the stateful stuff like it's unavoidable working with the dom in a browser to do mutations but we can come up with solutions that let us program as if we didn't have to worry about that we can program in a pure a functional way where all we have to do is declare okay here's a function that describes this part of the ui here's another function that describes this part of the ui as long as you pass some values it just returns the corresponding html and it's all pure and all the stateful stuff is taken care of and another part of the system so this is definitely very very kind of a functional way of thinking about it another example in react is this common question of okay well we have some state we you know in order to make our interface work typically our interfaces have some state to deal with where do we put it and in the early days of react largely because the dominant idea in programming is object oriented programming people thought okay each component or each part of the in the the react component in the interface should keep track of its own state and we want to have as much encapsulation and as much separation of concerns in our in our react systems so here's kind of our tree of you know dom elements or react components and if we had some state that only this component cared about um and if you needed to like show the state and maybe have a function that needed to change that state we'd keep it just down here but the problem with this approach is that if there was any point in time where let's say one component needed to show something but another component needed to update it then our state would have to go to the lowest common denominator or in this case it would be kind of like the highest common denominator um that because you know react only works top down so we can't keep the state here and make and make it accessible to this guy we have to move it up and in larger and larger programs what tends to happen is that the lowest common denominator is the root so there's this effort in this technique of keeping the state as low as possible but the kind of tidal force or the natural force of these programs is to try to actually the state moves up and up and up to the point where a bunch of react practitioners decided you know what if this state is trying to go all the way up to the top and we have all this state up at the top and in a bunch of places kind of near the top of our um our hierarchy why don't we just shift our thinking and instead let's just put it all at the top let's just have one place so let's concentrate our state in one place because then the rest of our application is pure these are pure dumb functional components if you work with react but basically these things are just functions they take inputs and they return html they don't change any state they don't do anything or when they do change the state it's all kept in one place and there has been a shift over the last few years where react started out as the majority of the community doing doing things this way and shifting towards this technique but we can go even further here because people realize okay this is nice we now have just one place for all of our state we don't have to kind of worry about things changing in a whole bunch of different places but it is kind of annoying because now you have to pass everything down like if this guy needs something it needs to be added to the state and pass through all of its parents and it wouldn't be and it would be quite typical actually to see in some larger react applications having like 10 15 20 things being passed to this guy just so that another five or six can be passed down to this guy and and so on so forth and that becomes a little tedious and you know hard to read and hard to understand so the next proposal is okay well why don't we just get it rip it out it's a global we have a single one of them so why not make it a global object and people are resistant to this idea because they're you know people it's been beaten to people's heads that globals are bad and it's true globals are bad when you have a hundred of them but globals are perfectly fine when you only have one and this is the kind of idea that you know when i think most large react programs now use and you and this is the pattern that redux follows which is a library that people use with react and now the neat thing is that this idea actually didn't come up with redux it was one of the things that came out of a closure library called reframe which predates redux so there's there's equivalent kind of there's an equivalent sort of issue in closure where we have libraries that wrap react so the reagent way of doing things which reagent is a kind of a very light wrapper around react start out as thinking about okay let's do things this way where we have lots of little states and lots of little atoms like closure atoms that we can modify but then when we frame came about they said okay well let's just let's just do this the right hand side instead now as a functional programmer what do i think well i think this is very object-oriented the one on the left-hand side i actually think the one in the middle is probably the most functional because even though this makes the ripping it out and moving that state to a global that's accessible everywhere makes it easier to work with these components are no longer pure they now kind of get state and access state kind of out from somewhere else rather than just being passed in directly via like the function arguments or props but as a pragmatic functional programmer i would say you know what it's probably still worth it i'm willing to put up with um a few little impurities in my code if it makes it net simpler so you know if i was a haskell programmer i'd probably do the middle but if i was a closure programmer i'd be happy with the one on the right hand side so i mentioned reframe which is the kind of redux equivalent it's this library that helps manage state in a front-end application and there's also a very interesting example in a change that happened in reframe so originally in one of the first you know the first one or two years of reframes existence you'd have some code like here it's closure code so you can be happy we're at a closure conference if you see some closure code um and this is a code to register an event handler so something a state transition something that like you know you click a button and you want to trigger something to happen you'd uh you'd register one of these things to handle that and so this is a adding product to a cart and in this situation when you know the user clicks that button and triggers this event we want to do three things we want to make an ajax call to tell the server oh hey this person added this product to their cart we might want to dispatch so trigger some other event to happen and we want to update our local global state object which in this case is called db to indicate that that item has been added to cart so this seemed fine but the reframe folks are you know pretty har um you know happy about functional programming and they saw this and they thought you know what this this isn't this isn't functional enough this could be done better because here we have three side effects three things that the this function is doing that is triggering um uh side effects in the rest of our system could we do better and you might think well no but you know what the whole point of this is to have side effects but check this out they eventually came up with something a new idea they said instead of actually calling other side effectful functions in your code like making ajax request or triggering another event or updating the database well actually in this case updating the database this doesn't cause a side effect this just returns a new database so it's these two that are the problem ajax and dispatch let's instead write our functions so that they just return an object that says what i would like to be done so i would like if calling this event for an ajax event to happen with the following information i would like for this other event to be dispatched and i would like for the database to now look like this but it doesn't actually do it this function itself doesn't do those things it just says here is what i want to be done so it's just like declares these are the things i wanted to be done whereas on the left hand side it actually does them and then the reframe system other parts of the system actually take this and do something with it this is another example of deferring instead of having a whole bunch of these events that actually have side effects and do things we have these functions just declare what they want done and we have one part of the system it's a complicated part of the system it has to figure out how to do all these things do all these side effects but at least it's all concentrated one place and the rest of our application is pure and easy to understand and easy to test like if i had to test this i'd have to test okay is the ajax event actually happening so maybe you have to like mock an ajax thing or actually just you know do an integration test um whereas on this side i just have to check if the arg the thing that it's spitting out is the thing that i wanted it's i mean this is usually kind of trivial but you can imagine that there might be some complicated stuff that's going on in here and all you have to do is check is it doing that complicated stuff and then let's hope that the person that implemented or if you implemented the thing that does the ajax bit you know it's a it's a it just has to figure out how to do that and so you can test those things completely separately so again kind of another one of these like example of doing things functionally so let's go back to our toy example here's our object-oriented code i've removed kind of the syntax heading and let's take a look at it from a functional point of view so if i was a functional programmer i would like immediately be looking at it in terms of where do i have state where do i have values that change where do i have mutations that change these values and where do i have code that triggers some other side effects like printing or writing to the file system in our case it's only console logging like logging to the console that is a side effect and so i would look at this and i've highlighted you know mutable state in blue mutations in orange and external side effects and green and what i see is this stuff is spread all over there's um state and a bunch of different objects there's side effects happening all over place there's mutations all over the place and i would say you know what object-oriented programming i don't think you've really made state easier you've maybe kind of well you know you've organized it a bit but it's still a problem you still have stateful stuff going on all over the place taking a look at our imperative example if we kind of take the functional lens again at it it's a little better actually because all of our state is in one place but again we have mutations and side effects all over so what i'm going to do in the net for the next like five minutes is i'm going to try to go step by step and refactor this to be more functional because you know some people ask me okay you know you know if they're trying to work in a functional way they think okay i need to write from the step one figure out how do i do this functionally when i try to teach people how to write functionally i just say you know what it's okay write it however way you want to write it and then you can incrementally tweak it to get it where you want and then eventually you'll develop the habits to kind of get it right the first time so let's let's do some refactoring okay i'm going to kind of you know we don't have to know exactly what's going on here so i'm going to be kind of ripping out the code and and making transitions so i don't talk for 40 minutes but here we go so we have these two console logs here and perhaps let's concentrate them with all the other ones like ideally we would have a bunch of pure functions functions that just take in some values and return values and so having this function these do these two console logs that's not great it'd be better if we maybe move them back into our main run game function so you know we're just moving things we're not decreasing at all but we're concentrating a little bit so let's pop them over and it's a little better it's not great let's try to make pop random pure so this function right now would have taken an array and mutated it to not have the value that was being removed we can well first we should rename it it's no longer going to be popping but if we make this one no longer mutate the array we have to move that mutation somewhere else so we're going to bring the mutation back into our outer kind of loop but if we do that and we're going to rename it in the correct places we now have select randomness pure so basically any code here that is just white with no highlighting that's good it's a good code and you know we're making this one arguably worse but in a whole i we think it's better because we're concentrating and moving all the bad things like state and state mutations into one place i also had added a little helper called without that given an array filter stuff out some of these things like there's a whole bunch of helpers that i have to add in this example because javascript doesn't have a great standard library for doing these sorts of things like half of the functions in javascript are mutate in place and half of them return copies and it's just a complete mess enclosure like i'd say like a good quarter of the code that you're going to see just doesn't need to be there because you have a function that lets you filter or remove an item from a list or do all these sorts of things without having to resort to writing it yourself but anyway we have some helpers there this next step we have our one of our strategies here one of the player strategies um still be stateful it still mutates by splicing an array so it removes the value of an array so let's do the same thing let's move let's have that function just return a value and do excuse me do the mutation back where it was being called so we can do that and now all of our these four functions selecting a random card playing a random card um and then filtering these are all pure now and they're no longer interesting so i'm just gonna collapse them okay because we're gonna need some more space in a sec but these functions now just do what they say they don't affect anything else you can if you were to read one of these functions you can ignore the rest of the universe because all it is is inputs to outputs nothing else and that's good let's let's keep going we have a bunch of console logs and messages here and the thing that i don't like about this is that there's a bunch of logic in the if statements um about here let me turn on the pointer about um how to you know decide which message to show and then actually showing the message but you have this side effect right in the middle so let's instead again move the side effect full part which is the logging outside and have a function that determines which message to show so i'm going to create a sorry so i create a win message helper function it's pure it just takes the scores and it returns a string for what we want to print out and our console log here now just prints that out and now that we've done this we might notice hey we have a two console logs together we might be able to actually just combine them in a similar way so we create an end message function and a score message function that you and that and they the end message combines the two together let's keep going we have our state variables but there's multiple of them and i'm going to suggest that we combine them into one it doesn't seem like a big difference but we'll see in a few moments why that might be helpful so we're just going to plop them all into one thing you might think of this kind of like concentrating and i had to change a bunch of the code just because now you have to do like state.state.state.state.org whatever not a win yet but now i would suggest let's let's maybe create a function whose job is to take the state of the game and give me the next state that's all it's going to do and it's going to be pure so it takes one state and it decides or it determines what the next state of the game should be so if we were to create this function you know we need to we're going to it's going to take some state and then it's going to return that new state and actually we can just like pretty much copy paste most of the code that was in here because a lot of it apart from the mutating parts um is just using these pure functions that we created and so what we're doing here is we're taking that state calculating like the bounty card and the two cards that are being played and then you're just returning a new value and if we do that then you know we don't need most of this and we can replace it with still a mutation but now it's all of our kind of logic for figuring out what to do is in a pure function it's it's kind of complicated but you know this is the meat of our code right here this is what defines this game from another game and uh now we just have a very kind of simple we say okay well the the state for the next turn is the previous state transformed by this next state function but we do have a problem there is a few places here that we are console logging values that no longer exist so we need to add them to our state we realize that we actually had some implicit state that we cared about because it was being generated inside of this loop so we're going to explicitly put it in our state object so i'm going to put those few in the state object modifier things and now this is working again this is looking better you know now we have the majority of our code is pure and we have just a little bit of code here that still just side effects and some state and you know what i might stop here even if i was writing closure you could do this with an atom that gets mutated but i'm going to keep going because there's still more to be done uh once again we see like these console logs here a bunch of console logs together so we could combine the content that they're logging and have a function generate that and have the console log then just call the function to figure it out so i create a turn message function whose job is to figure out what is it that i want to show on each turn and then i console log it again anytime we're moving code from a stateful messy mutation heavy part of the system to a place where it's just pure and perfect it's better so we might not we we're just we're shuffling things around but on the right hand side everything is pure and we can take each of these things on their own and understand them exactly on their own rather than what we had previously which is we could we have to understand them as part of a large hole whereas here we can understand them independently not done yet um i'm going to suggest and this is another one of these like maybe crazy suggestions instead of just keeping track of one state let's make it into an array and so i just turned into an array and now instead of mutating the one state value we're just going to append to it so every time we do a turn we're just going to append to that array the new state that happened and again you might say oh man this is crazy if this was a big game you'd have a giant data structure of thousands of states and memory but in a with functional data structures it's it's reasonably efficient now you might not want to do this on a system that's processing way more data than you can store or whatever but in this kind of system that this might be a reasonable choice for you to do now that we have an array i'm going to suggest why not split our program into the two two steps one step that just generates the states and another step that logs does the actual stateful things so i'm gonna have i'm gonna what i'm gonna do is i'm just going to have run game return the states i'm going to create a reporting function whose job is to take in states map over them so kind of loop over them using some function that we're going to invent in a minute so some function that it receives that tells it what to do on each turn and some other function that tells it what to do with the last state so this is our reporting function and then the way we would run our code is we just have a run game which just generates a bunch of states and then our two functions we already have turn message and end message these are our message helpers that we created before this one decides what to print on each turn and what to print at the end and once we have that all this stuff here all this like console logging we were doing before is no longer necessary so this is nice now we have no side effects in here and just side effects in here so we've concentrated and separated so it's good and then the last step is you know we have this while loop that's adding to an array we could actually do this with recursion and completely get rid of having any sort of mutable state now again this part is kind of optional and it's really just like depending on what you're doing this might be kind of a minor trivial thing and in closure you actually have like a while loop with a recur so it looks like you're writing a while loop but it's implemented with recursion under the hood or maybe then under the actual in the jvm and might be to be doing it with a loop it depends on how you think about it but we're trying to get rid of our code actually doing mutation it's fine if the system under the hood does mutation so we can have a function that just recurs so it takes some states um it takes a state change function and an end condition and it does some recursive calling and if we use that then here's what we end up with a c of white you know if we were to expand this would be a lot more code but now like 95 of our code is pure all it takes is input transforms inputs to outputs by doing this process we've also semantically kind of identified little responsibilities that are useful little kind of things like oh converting next state having an end message determined and all of these are so easy to test you just take this function pass it some state and check if it returns the value you want you don't have to mock out some console object or some db object or whatever and then you know some side effects are inevitable so we have them over here in just one little place so we've gone from this thing that was a whole mess of side effects and mutations and state to no mutating state at all and one little place that takes care of the side effects and then why is this good in general it's because pure functions are good and so we want pure functions everywhere right if if i had to leave you with one core concept of what is functional programming it's this this image okay burn this into your mind functional programming is trying to program with as many pure functions as possible and then figuring out the details and like i mentioned you know pure functions are good because they're so easy to test they are so easy to understand because if i look at this function or if i look at any one of these functions i could ignore the rest of the universe and i just have to figure out how this function is doing what it's doing they're very easy to test they're really easy to use in a parallel system if you're trying to parallelize and they can also be trivially memoized so because given an input the outputs are always the same you could create a system that says that will cache all of these responses so there's you can look up a fun example of solving like fibonacci um recursively and the recursive solution is like you know n squared and it's terrible and it's pretty slow if you give it like a large number but the moment you just add caching which in closure actually there's a single word you can you can have a function and just say memoize function it turns it into kind of the most efficient approach to that problem and it makes it kind of instant how's my time okay now i also want to talk a little bit about data-driven programming i'm running a little over time and we started late so i'm going to go through this a bit faster than i wanted to but closure is also called data driven and what does this mean and there's kind of like data driven if you were to ask people they don't like it's not a well-known concept and it's not really clear what data driven is and even people will argue about it and and it's not it's there's no consensus really it's not one of these like it's it's early days in the data-driven world but there are kind of three things that we mean when we talk about data driven one is this idea that when we design our programs we think about the data you know it's we're doing pipelines of data let's think about what is our data where do we need to move it how do we transform it so it's this kind of like data first thinking approach but it's kind of like very high level the second definition or way of thinking about it is the fact that closure just uses plain data structures when you write closure code you don't type you don't have objects you don't have like typed structs or things you're literally just using you know vectors and maps and you're passing them around all over the place now people from the typed programming background would think oh my god that's crazy how can you know that things work but it does and you know there might be some trade-offs but closure is firmly in the camp of it's better to have just a common simple way of transferring data around your system rather than typing it all and the third definition which is one of the more kind of ones that people typically think of when they do data-driven programming is this this idea of programming where data structures define some of our control flow and so an analogy to this is let's say macros you might say code is data so you know you can take some code and manipulate it with other code to change what you want to do but actually the closure community is not a big fan of macros there's a whole other talk that you could um that i'm going to point you to to kind of learn more about that and instead what the closure community seems to be moving towards this idea of let's use data structures to describe our logic and then have some other code manipulate those data structures and do the things we want because data structures are the purest thing you can have they don't even have behavior you don't concat things you know things it's just data um so i like calling this like my analogy for this and my word that i have for it when i try to explain to people it's like configuration driven development like what if you could put more of your program just in a config file of plain data and less of it in like touring complete code and a few examples of that like my favorite example of this is actually um the aws sdk so aws if you want to work with aws um you know you'd probably be using the sdk and you know they amazon would need to write that same sdk for like 20 30 different languages so they came up with this idea of you know what let's just describe all the things that you can do with the aws system in json so they have these j so they have this giant list of like 1 000 json files um and each of them describes a part of amazon's web services and it's literally json that says okay well there's this operation you can do on s3 called abort multi-part upload and here's a bunch of metadata about it here are the inputs it takes here are the outputs here the errors and so on and so forth and then in order to write the ruby library or the node library they just need to write a compiler or some sort of system that translates these json files into an sdk or library you can use but they don't actually have to implement every one of these functions in that library and in this way you know that that's kind of that's what they do when you're using their libraries they're actually just um look being built from these json data structures it's crazy but it's so much easier to write something that translates this json into some code than to have to write all that code and maintain all of that code and so aws has a bunch of them that they've written and then a few months ago cognitect wrote the equivalent version for for enclosure two other quick examples of data driven are how we how we do how we write html and css enclosure if you think about let's say react in jsx you have this weird mangle of javascript code that turns into html code and then there's you know you switch back and forth and there's a whole bunch of syntax and about how to do that which you know people who work with it are like oh yeah this is fine it makes total sense i teach students react and i tell you it is not trivial but in closure land we came up with this syntax called hiccup where you just use keywords and strings and maps and objects to create your structure of uh that corresponds to the html and it is absolutely trivial to work with it makes like perfect sense there's like no ambiguity and you can just use all the stuff you're used to using enclosure like for loops and things um or i guess this is like four is kind of really implemented kind of like as a map to do to generate the data structure that you want and then later deferred hiccup converts it into the html that you expect and similarly for css and the reason this is good is because it's tangible it's fungable i can take this data structure and i can manipulate it and as closure programmers we love manipulating data that's what we do that's that's what we live and breathe and everybody just it's super trivial and closure makes it super super trivial to work with and so having these things as data structures rather than some strings and some compiler like whatever um it's super super easy to work with and so we're seeing this more and more more and more libraries are trying to turn to this way of describing the functionality that you want from the library just as data structures that you can either just write out by hand or you can write and manipulate write and manipulate and then pass it off to the library an example of this is composure which was the solution for doing http routing um which was heavily like in 2008 when it came out it's still one of the most popular ones and it relies on using macros and functions but once you write it you can't you can't do anything with it it just gives you a function but now libraries instead have come out that do it in a data structured way now if you say you know why is data better than code like i said it's tangible you can work with it i recommend the talk transparency through data by james reeves which is worth watching and he spends another 40 minutes talking about it all right okay we're gonna have to kill it up all right thank you guys um and uh just a reminder you know when we think about closure it's functional it's data driven but lastly it's also pragmatic these are the ideals we want the the ideals are functional programming pure functions the ideals are using data as much as possible but in reality sometimes the world gets in the way and problems are hard so you can use stateful things as well but we just try to optimize for functional programming and data-driven programming thank you [Applause]

from
Talks
added
2026-10-10
likes
0

Talks › Categories > Functional Programming: “by Rafal Dittwald (Clojure/North 2019) [01:02:25]”