 A few weeks ago, I made a video about creating our very first GTK application using Haskell. And we created this very simple program, or I did, on camera. I created this very simple logout and shutdown menu here that only took about 30 minutes or so on camera to create this very simple program. All it is, it's this seven buttons here, these seven icons that perform various actions such as cancel, which exits out of the program, logout, which logs us out of our window manager, reboot, shutdown, suspend, hibernate, and lock, of course, as a screen locker. And this program, which I called bye-bye, again, we only spent about 30 minutes writing this thing. Let me show you the source code for it. Let me open up Doom Emacs and see if I can find bye-bye. This was the original version of it. Let me zoom way in here. And I tried to keep this program as simple as possible for those that don't know anything about either GTK or about Haskell. So I didn't really do anything complicated. I just showed you some of the standard GI-GTK commands, and the GI-GTK is a library in Haskell for the G object introspection bindings, the GTK bindings for Haskell. And of course, I purposely kept this simple again as a very first example for brand new users, right, for people that don't know any programming at all. But we're going to go a little deeper, because a lot of people that do know a little bit about programming immediately spotted that there's a lot of redundancy here, right? There's a lot of duplicated code. For example, I created all these labels. Do I really need to create these seven labels like this? Couldn't I just use some kind of for loop or a while loop of those familiar with things like bash scripting? Couldn't I, instead of creating each button one at a time with essentially the exact same settings minus the command they execute? Other than that, they're all basically the exact same settings. Couldn't I just write a function to take care of that? And of course I could. I could probably make the code for this program, which isn't a very long program. Again, it's a very simple application, 194 lines, and that's with comments because I wrote this in org mode. So it's about 194 lines. I could probably put it in 50 lines or less by reducing some of the duplicated lines in this program. So I think that's what we're going to do today. But before we get into that, again, assuming that you've been following along and you're kind of a brand new user, we should actually just start with the basics on how to create functions in general using Haskell. So I'm going to break it down in real simple terms. And one of the things I really want to focus on today is how to create recursive functions in Haskell. Because especially with these GTK applications, a lot of input output stuff, which your GTK applications are going to do a lot with input output with IO. Typically you're going to need some kind of looping mechanism for various things, not all the time. Certainly this bye-bye program that I wrote. We don't really need to fool with recursion in it, but many programs you're going to have to use recursion. And because of that, it's best to go ahead and start talking about that now. And for those that are not familiar with that term of recursive function or recursion, recursive function is a function that defines itself in terms of itself. Let me show you what I mean. So let me switch back over to my desktop here and I'm going to create a new file here. I'm going to call it Haskell, recursion.org. I'll make it an org document here and let me zoom in. And I'm going to go ahead and just create a source block here. And of course it needs to be Haskell. So a basic template for recursion would be some function, give it a name. I'll call this one Funk. And then you pass along some parameters. A lot of times with recursion you're working with a list. And typically the format for a list as an argument for a function is x colon x's, where x is the head. That's the very first item and x's is the rest of the list. Of course, these are variables that you could label anything. I could label it y colon y's. Typically though, you do want to keep it in the format of some letter and then colon some letter s because that's very easy to recognize that this is the head and this is the rest of the list, right? This is a and this is the rest of the a's, think of it in those terms. The colon is very important. That's the cons operator. It's basically taking the a value and prepending it to the rest of the a's. So that's why this is the head of the list, colon, and this is of course the tail of the list, the rest of the list. So I'm gonna put this back to x colon x's because that's the most common variables for these kinds of lists. And then I'm going to do equals. And then so a recursive function typically is you're gonna do something with the x value. So I'm not even gonna write legit code. I'm just gonna say, do something with x and you may do some other stuff after that that doesn't involve x or x's. But then eventually what you wanna do is funk x's where you do something to the head of the list and maybe some stuff in between. But eventually you want to call the function again on the rest of the list. And that's how the recursion happens, that's what performs the magic. So you do something to the very first item and then you rerun funk on x's, which is everything but the first item. Which again, the recursion happens and then it runs the function on x's. The rest of the list now without the first two items because you've already run through the first two items, yada, yada, yada. And eventually you will get to an empty list and then of course the loop should stop. Now here's the thing with a recursive function on a list in Haskell. The empty list, when it gets to the empty list, the Haskell compiler is going to have absolutely no idea what to do when it tries to run funk on an empty list because we defined funk as x colon x's. So x is the head of a list that involves x's, right? But an empty list of course doesn't have any of that. So you have to have also funk on an empty list. And then what you wanna do is funk on an empty list. You probably just want that to equal an empty list. So funk on the empty list, this is what is called the base statement. This is once the loop finally gets to a point where it needs to terminate itself, that's the base statement. Until funk gets to the empty list though, it is going to be running this here. So let's show a real world example of creating a function. Let's take the take function here in Haskell. Haskell has a base function called take. What take does is you give it a number, an integer. So you can say take three of some list of items. For example, if I did a range of one to five, take three is going to take the first three in that list of one through five. So it's gonna return one, two, three. And let me open a terminal to show you this, what I'm gonna do, zoom in in this terminal. And I'm gonna do ghci, which is the interactive Haskell compiler. Just to show you this in action, if I do take three on a range of one to five, I get a list of one, two, three because it takes the first three items of that list. It drops off four and five. Now, if we were writing this function ourselves take, how would we write that? Well, I would probably use recursion to actually define take. I don't know what the official definition for take looks like in the Haskell libraries. But I would probably define it in this way here. I would do take in for a number, in is typically used to signify numbers as arguments. But again, you could use anything, you could use I or A, B, C, D, whatever. It takes a list and once again, I'll do the standard x cons x's. And then if I was doing a recursive definition of this, what I would do is in that list, I would take the x, the head of the list. And then I would probably cons it to take and then the function in minus one because when you're involving integers in these arguments, typically the way you want to do the recursion, you could do recursion on the list. You could also do recursion on the numbers themselves because you can either increment or decrement the numbers, which also will cause the loop to go forward until, of course, you give it a base statement of when to terminate that loop. And it needs to run take in minus one on x's, the rest of the list. Now let me actually make that full screen so you can see that line. So can you see what this is going to do? And sometimes it's good to actually write this out in long form. So let's actually do take three on one to five. And what this would do, it's going to take x, which is the head of the list. But first it's going to take one and it's going to cons it to the rest of the list, meaning that's going to be put at the front of the list. And then it's going to run take on n minus one. So since that's three minus one on the rest of the list. Now the rest of the list, we took one out and propended it. So the rest of the list now is two to four. Now take three minus one on the range of two to four. What that's going to do, of course, is take two and prepend it to take two minus one on the new list, which the new list now is simply going to be three and four, the only digits remaining. And then if you want to keep on with the recursion here, of course, then that's going to take three out of the list and cons it to take one minus one. I should wrap that in parentheses there and then to the rest of the list, which is now just four. Now actually, you can already see this would go on to infinity, right? Because we would then get take zero of four and heck, we could even keep going with negative numbers, right? So this would be an infinite loop. We have to stop somewhere. And where do we want to stop? Well really we want to stop where we're at, right? Once we get to take one minus one, which is of course zero, and really we don't want four because this would be the fourth item in the list. And of course take three. We want one, two, three and then stop. So we need some kind of guard that actually is some kind of base statement that actually stops this. So I'm going to say that take in should always be greater than zero. So it always should be one or more because once you get to zero, then we're getting extra digits that we no longer need and of course we don't want to go into negatives either. So we want to go ahead and define a base statement. So I'm going to say take in and then I'm going to give this wild card character, meaning I don't care about the list. It could be X cons X's. It could be an empty list. It doesn't matter. What we're focused on here mainly is the numeral, the in. And I'm going to do a pipe symbol. So what this is in Haskell, this is called a guard. You can think of this as a condition that has to be met. And the condition of course is that in has to be less than or equal to zero. And if it is, just return an empty list. So once it gets to zero or less, which would be negative numbers, just return an empty list meaning stop this loop. That way, with this guard, this here would reach the base statement and now that would return just an empty list. And now we would simply get one, two, three return, which is exactly what we would expect with take three from a range of one through five. And I can go ahead and tell you from experience anytime you do recursive functions on a list, the Haskell compiler will always complain if you also don't give it a base statement on an empty list. So let's go ahead and take care of that. So we also need take. And then I'm going to do a wild card character for the number. I don't care what the number is because this is about the empty list. When you get to an empty list, just return an empty list. So there is the take function, one of the base functions for Haskell. And you have two base statements. So what happens when n reaches zero or less, we want an empty list. And if you happen to have an empty list, just return an empty list. And otherwise you're going to take in x cons x's and do the recursion, do the loop, until you finally reach one of these statements and then the loop stops. Now the way Haskell works is these are actually three different functions. They're not all one function. Take with these arguments is a function. Take with these arguments is a totally separate function. And then take with this argument is a totally separate function. I mentioned that because if you're learning some of the basics of Haskell, you probably know that Haskell is in a mutable language, wants you to find functions. They can never change and you're probably wondering, well, you did three different takes. No, no, no. We actually did not because take with different arguments technically is a different function. And now that we know how to work a little bit with recursion using list, why don't we do the length command? So the length command is another function. It's a base function in Haskell. So if you do something like the length of a list and I'll do once again, one through five, it will actually return the number of items in the list. It will just return five because that's how many integers are in a list of one through five consecutive numbers that's going to return five. Now how would you define this using recursion? Well, knowing what we know now about lists, I would define it this way. I would do length and then x cons x's and then it's going to equal one plus length on x's. Right? That's really all we need to do. And if I did this out in long form, right, let's go ahead and prove our homework here and then what we're going to do length on one through five is going to equal one plus length of x's, which is now one plus one plus length of x's. So this x's here would have been two through five because we drop the x the head immediately, right? And this length on x's would be three to five and then the recursion keeps on. Now it would be one plus plus one plus one plus length of x's which now is just going to be four and five and then finally one plus one plus one plus one plus and then finally we get to length on just five and then we get of course one plus one plus one plus one plus one plus length on the empty list. Now length on the empty list that is where we should stop the loop and what we're going to do is we're going to say length of the empty list should return a value of zero. So finally one plus one plus one plus one plus zero and then how we get five, right? So that is how that would work so that is exactly how I would define the length function and typically in Haskell you also want to type meaning give a type for your function. So just for clarification a type is basically saying what kind of values the function takes and what kind of values it should return. So in this case length it's going to take a list of a values. Now that I could label that anything but everything in a list are all the same values that's just the way lists work. If you have a list of numbers everything's a number. If you have a list of strings they all have to be strings, right? So I don't really care if they're numbers or if they're strings or if their character is yada yada yada or bull values or whatever it happens to be that they all have to be the same kind of value. So we're going to take a list of a values and what does it return? It returns an integer. So that's a little bit of how recursion works in Haskell. So how are we going to employ this in our program in the bye bye program that we were working with earlier? Let me get back to the source code for bye bye. So looking at the source code here most of the source code involves creating the seven buttons for bye bye. Let me run the program so you guys can see the seven buttons I'm talking about here, right? So that is these blocks here. That's button one, that's button two, button three, four, five, six, seven. And for the most part the code is a lot of duplication. We're creating a button, we're setting the style, all of this stuff is the same. And the only thing that we really, that's different for each button is the action that it does when it's clicked. On button one clicked do this, on button two clicked do this, right? So obviously the actions are different for the cancel button, for the shutdown button, for the reboot button, but I could create a list of actions to work with. And then through recursion have this recursive function create the buttons for us, give it the appropriate label and give it the appropriate action based on a list. And I think that's what I'm going to do. So the way I would attack this I would probably go ahead and just start by creating a function, I'll give it a name, let me create some space here. So I'm going to call this button with image and label because I'm going to have this function create the button, assign it the appropriate label and also assign it the appropriate image for the icon. And what is the type? I'm going to go ahead and specify the type. What I'm going to do is I'm going to take a list of tuples, the tuples are going to involve a string and the other part of the tuple is going to involve a IO action. So what I'm thinking is I'm going to create choices here and it'll be a list of tuples, these comma separated tuples here and what these will be the first part will be a string for example, cancel, right? The cancel button and then the next part will be the command that the cancel button does which in this case the command is GTK widget destroy, let me actually just copy that and then paste that right there and that is the first tuple in the list. The next one would be button two which of course would be logout if I spell it correctly, comma and then the command for logout which in this case I'm going to use call command and then kill all and then the location of the Xmonet x86 underscore 64 dash Linux binary we're just going to kill that program and then I'm just going to keep on with this list of tuples plugging in the appropriate values. So I went ahead and created the rest of this choices list of tuples. So the function that we were creating button with image and label is going to take this list of tuples and where's the first part of the tuple is the string second part is the IO action you know the command that it's going to run and then what is it going to do with that? Well it's going to take all of that and return an IO action because that is what these GTK functions do they create these widgets that do stuff IO stuff so the last part of the type typically on these is almost always going to be IO and the reason I began defining both the list here and the function with let is because I'm actually inside a do block at the very top of this where main equals do and then everything in this program is actually inside this do block. So I did have to define the function using let I had to also define the choices list here also with let now using recursion even though we're only dealing with seven items I don't have to define this as a recursive function but because that was the point of today's video let's actually do this recursively. So since we created that list I'm going to do button with image and label and they take an argument x kinds x's right it takes a list as an argument and then it's going to equal it's going to equal this do block a do block is important anytime you're dealing with IO IO actions within IO have to happen in a certain order in sequential order. Now typically Haskell functions when you define a function in Haskell the order that it does the steps it doesn't matter what it actually does to achieve the desired result typically doesn't matter to you the way Haskell typically works you define a function and you basically tell it the end result hey this is what I want at the end you really don't care how you get there but with IO it's different it's very important that these steps are in the correct order because if you create a widget before another widget and then try to add that widget inside the other widget for example right it's it's not going to work right you have to do things in a specific order and then really at this point some of the steps that I want to take you know I'm just going to copy this I'm going to grab that because for the most part that is actually what we want let me correct the spacing here because if this is not indented right the Haskell compiler is going to complain a lot and then this variable button one I'm just going to name it button I'm not going to label it button one because it doesn't make sense because it's going to label all of these as button one so I'd rather just give it a more generic term button then on button clicked do this will be the action we will eventually specify exactly what that action is going to be in just a second for recursion to take place you know ultimately somewhere along the way we have to restart the loop so we have to rerun button with image and label right because recursion means it's defined using itself so at the very end rerun the function on X's the rest of the list so it's going to take X so X is going to be the first item in the list which is this very first tuple which has two values how do we separate the first value and the second value of a tuple well in Haskell there are two built-in functions you have FST for first and SND for second and when you use those functions on tuples it will give you the first value and SND of course will give you the second value so the first value of the tuple is actually the label so if I rerun the app we actually have two things going on we have seven buttons and then below the seven buttons we have seven labels so this gets us started on creating the buttons let's go ahead and also have this loop take care of creating our labels so label is going to equal GTK dot label new and we have to give it a value I'm gonna give it a value of nothing we are going to set something to that in just a second but for now we're just gonna create label new nothing and then we're gonna set label and then I'm gonna hit enter I'm gonna indent over two spaces and I'm gonna start this block here really this is just a list here of properties I'm gonna set first we're gonna set label then I'm gonna give it colon equals and then what we want the label to equal is the very first part of the tuple which would be FST X I also want to add some markup to it because I would like this to be bolded so really what I want to do is I want to add this bold tag and then I want to concatenate FST X the first part of the tuple so in this case it'll be cancel on the very first one and then of course at the end I need to append a closing bold tag as well one thing I know I'm probably gonna have an issue here is setting the label this needs to be text rather than a string and by default I think the compiler is going to take all of these the first part of the tuple as strings so it's gonna make this whole thing a string but it needs to be converted to data text so I'm gonna do a pack here and I do t dot pack because I imported data dot text in my imports as t actually I haven't even added a data dot text here I probably need to do that so let me go ahead and add that library because there's gonna be several things we're about to define that are gonna need to be defined as text rather than strings and I'm gonna go ahead and import two functions pack and unpack pack will take a string and make it text unpack will do the reverse it will take text and make it a string and of course because I'm doing this with stack what I would need to do is I would need to go into the package dot yaml and in the dependencies I would need to make sure that I had the text package as part of the dependencies for this project so I've already added that one thing I notice is I'm setting an image for the button and I'm setting it to this variable image one because I have seven images created somewhere in this file I think yeah the image are these locations of these ping files I created now the ping files are labeled cancel reboot shut down you know dot ping which is also the word that I was going to actually use as the label themselves so actually what I could do is I could set the image to again the first part of the tuple dot ping right FST X and of course I would have to concatenate dot ping at the end and actually I also have to get my home directory which I already have a variable defined for home I would have to propane the location of my home directory plus the location of this directory we're working in which I should probably just create like a prefix which is the path to this directory our working directory then plus first on X and then plus dot ping which I could actually just create a variable for it and I'll call it suffix so I know this video is already going on rather long what I'm going to do I'm going to speed ahead I'm just actually going to show you the finished product because I got a good enough start here that I think I can show you the finished loop here and you'll be able to understand exactly what I did all right now I worked on this for a little bit and I went ahead and created the prefix and suffix variables so and I already had the home variable I think toward the top the home variable just gets the location of my home directory which is slash home slash dt and what this allows me to do is now setting the image for the icons is the home variable slash home slash dt plus the prefix variable which is the location to this project file plus first of X which is the first part of the X tuple which is going to be this column here right the actual names plus suffix which that variable is dot ping so that takes care of the images and then the tuples themselves we just plug in the various first values so FST X for labels and you notice I have this function here capitalized first X because I noticed that the names of my image files are lowercase which means if I'm using these also as the label names they would also be lowercase but I really wanted these to be capitalized so actually I just created a little function for that if I go toward the top so I created this very simple function this is not a recursive function all it does is capitalized on X cons X is what it does is there's two Haskell functions two upper and two lower two upper capitalizes a character to lower lower cases a character and what it does we're going to take a list because technically a word a string is a list it's a list of characters right and I specified a list of characters and it returns a list of characters so it's taking the very first character X and that's doing a two upper on it and then it's consign that to the rest of the list which we're mapping to lower over the rest of the list map means do this function on every single member of a list so we're lower casing the rest of the list so that was just a quick and easy function that solved my problem for capitalizing the labels and you can see I left some comments here so first we create the GTK image and then the next section here is creating the GTK label and I set the appropriate values for it and then create the GTK button and I set the appropriate values for it and then what happens when the button is clicked so on button clicked do and this should be our action what is the action well this should be sndx right the second part of the tuple were the actions for each button so this column here and then finally we need to place those buttons on the grid because if I rerun this you know these are all part of a grid you can think of it this grid as seven columns by two rows is essentially kind of what this grid is where does everything get placed well what I did to have each button have its own XY coordinates was I created these variables here A and B where A is the length of our choices list minus one and B is going to be the length of the rest of the list so if we start with seven items in the choices list seven minus one is six and then the length of the rest of the list when we start is also six so I take those values and do a minus B I get zero which is the very first coordinate I want is zero but then I want the next coordinate for the column to be one so it runs the loop again a the value of a never changes the length of choices was seven it will always be seven minus one still six right except now the length of X's is five instead of six so now a minus B is one and then the next time it runs through the loop it will be two three so it increments the numbers to where when I do the attach grid for button call for column is going to be a minus B right so the first time it runs the coordinates will be zero zero one one the next time it runs it will be one zero one one the next time it runs to zero one one so this is the zero column this is the one column this two column yada yada yada so that is how I achieve this and finally of course we just need to run this recursive function so button with image and label and then you have to feed it a list because that was part of the definition remember but with image and label takes a list as an argument the list of course is the choices list which was our list of tuples so doing that if I zoom back out how much did I save as far as typing you know the duplicated lines how many did I eliminate by doing that well I still have the old version up let me actually show you the old version as well I still have yet here and let's see some of what we got rid of right so I defined all of these images before I no longer have to do that I define it one time the loop takes care of creating the seven images all these labels this block here no longer have to do that the loop takes care of the seven labels for me I have to do it one time in the loop the loop takes care of the rest creating all of these buttons each one of these blocks for each one of the seven buttons I only have to do this block one time now because the loop takes care of everything else and then attaching the buttons and labels to the grid I no longer have to do that because I created that variable the called variable that gets incremented right so instead of having you know 12 for 14 different things to attach to the grid I'm only attaching the one button and one label but the the values change as the loop runs so that makes this thing I mean a lot shorter right and when I say I cut it by about 75% yeah we did probably remove 75% of the code from the first version to the second one and it still runs exactly the same thing it's actually looks functions and feels exactly the same way is there any real difference other than it's a little bit less lines of code and probably a little easier to read as long as you understand you know how this function works right now that I've kind of run through some of the basics of creating a function in Haskell and how IO works and especially how recursion works you probably could actually figure that out you know now that I've kind of talked you through it and we threw a lot at you today with this right this was much more advanced stuff than that very first video on bye-bye where you know I explain what lists were and what a tuple was how you get the first and second values from a tuple and of course from here you've got the foundation to eventually you know do more substantial apps obviously something like this is very simple but I mean you could get into more complicated stuff I've been working on this welcome application for DTOS which has an auto start tab where I can add or remove auto start programs of color schemes I can do a one-click change of color scheme a one-click change of various default user shells yada yada yada video tutorials this is a grid of various YouTube playlists which is good for documentation for those that are using DTOS I've been working on that I just got kind of a rough outline it's still not nearly ready for prime time I started working on this just a few days ago I was trying to create a little application menu so listing all the applications obviously the icon sizes are all messed up but you get the idea you know I was able to pull out all of your desktop applications and create a little GTK menu kind of like you would see a system menu as part of a panel you know there's a lot you can do pretty much the sky's the limit with what you can do with GTK once you kind of know how all the widgets and all work and from here I think I'm gonna continue exploring this rabbit hole if you want to continue with me stay tuned to the channel and of course once I get into more complicated stuff I'm gonna rely heavily on you guys especially you Haskell pros out there I'm gonna need your help now before I go I need to thank a few special people I need to thank the producers of this episode Dustin Gabe James Matt Maxim Michael Mitchell Paul West why you bald homie Alan Armored Dragon Chuck commander angry die okay Dylan Marsh drummer young Alexander B's origin or polytech realities for less red profit Steven tools Devler and Willie these are my highest tier patrons over on patreon without these guys this episode would not have been possible the show is also brought to you by each and every one of these fine ladies and gentlemen as well all these names you're seeing on the screen right now these are all my supporters over on patreon because I don't have any corporate sponsors I'm just sponsored by you guys the community if you like my work want to see more videos about Linux free and open source software Haskell GTK subscribe to distro tube over on patreon alright guys peace we could create games with Haskell and GTK such as Minesweeper