 So for our first practical example of go pigeon code, we'll take the hangman program we wrote in regular pigeon and rewrite it into go pigeon. And there's not much different, except we basically just have to specify our types. For example, this is the one we wrote in regular pigeon and here's the one in go pigeon. And we say that our parameters start and end, we have to specify that they're integers. And we say that the function itself returns an integer and then its local n is a float. And inside here, another thing we have to do is that our arithmetic operations, any one operation has to have operands of the same type. You can't mix integers and floats. So here, if we want to multiply n by the result of this operation, well, this is returning an int because both ended starting integers. So the subtraction returns an int. But we want to multiply by this float n. So we need to convert, we have to get the float equivalent of this integer value. And we do so with using f as an operation. And then same deal here, we want to take this integer start and add it to n, but then it's a float. So we have to convert our integer into a float. And down here, the floor operation takes a float and returns another float, but our function needs to return an integer. So we use i as an operator to get the integer equivalent of what floor here returns. And the other functions is nothing much different except when we deal with lists, we have to specify what kind of list and we're dealing with lists of strings. And down here in the original code, we just used the list operator to create an empty list, but you have to specify what kind of list. So this is creating an empty list of strings. It's a specific kind of list. Also notice that in our foreach loops and our foreing loops, we have to specify the types of the variables. So here we have to specify that the counter variable i has a type integer. And it's always going to be an integer, but for consistency, we make you write the i there. Same deal in the foreach, the counter, the index that we iterate over is always going to be an integer, but you have to specify it explicitly. But then in a foreach, depending on what kind of thing we're iterating over, in this case, a list of strings, that determines what type this thing has to be. In this case, it has to be a string because we're iterating over a list of strings. Now, understand that in real go, as we'll see later, they don't have a built-in list type. They only have slices and arrays. So the way we would write the equivalent code in actual go is we'd use slices. So here, let me just demonstrate this main function where here we have it written using lists of strings. But here's the code if we had slices of strings, and here I'll quickly comment this. There's a couple differences then. The first is that in this version with lists, the found variable is a list of strings, but it starts out nil because list variables hold references to lists. And until we assign it this new empty list of strings, found is referencing nothing. And you can't do anything with the nil reference. You need to have some actual list referenced first. And then what we do in this loop is we're pushing values onto the list. We're mutating the list referenced by found. We're tacking the thing onto it every time through this loop. With a slice, here the found variable, we don't have to initialize it because its default value is already an empty slice. It's something we can append to. And so down here in the loop, instead of pushing, we use append on a slice. And the critical difference with slices is that when you append, the append operation is not really modifying the found value itself. What found store is still a slice pointing to the same array with the same length and capacity. But append will return a new slice that may point to the same array or may point to a new array if there was no more space in the original array and we needed to allocate a new one and copy everything over. But whether or not it points to a new array, the new slice here will have a length that is greater by one. And that's what we need. So every time you append in this loop, we need to modify found itself. We need to assign the new slice value to found so that each time through, the length of found is increasing by one. So just watch out for this when working with slices. It's a very common mistake to forget to modify your slice variable after doing the append because append is not mutating the original slice itself. It's mutating potentially the array which the slice points to but it's not changing your slice itself. So that's what the code will look like with slices. I'll leave it to you as an exercise if you want to go through and change the rest of this code to also use slices instead of lists. Now let's look at something slightly more interesting and something new. We're gonna read and write files because in GoPidgin we have operators for doing so which we didn't have in regular Pidgin and regular Pidgin, all we could do for input and outputs is print out to the console and read from the console. But now we have a few more operators including open file and right file and also close file. Ignore the fact this isn't highlighted. That's a bug in my syntax highlighter which I'll fix. Anyway, so this program here is going to write some bytes to this new file called myfile.text and let's walk through it. So first off the open file operator. You specify as a string a so-called file path which is specifying the name of a file and also where it's located. There are two kinds of paths there are absolute paths and relative paths and absolute path specifies exactly where on your system the file is supposed to be located whereas a relative path is relative from the directory in which your program is running. So this is a relative path so it matters where we run our GoPidgin program from. So it'll be relative from the directory of your command prompt when you run this program. So this will look for a file called myfile.text. If it doesn't exist it will create it. As you'll see in a real environment in actual Go you'll have more control about determining whether well if they want open file it doesn't exist should it create it or not you have control over those details but for simplicity the operator's in GoPidgin just don't give you these options. Anyway so assuming my file.text doesn't exist this will create the file and what it returns what open file returns is an integer which it'll be assigned to the file here and a string which will be assigned to error. The integer represents a so-called open file handle. When your program runs every file it opens there's a unique integer which is associated with each open file and we need that integer because subsequently when we do stuff with the open file like write to it or close it we need to specify that number, that integer to specify which open file we're talking about. We don't really care what the actual integer is. Generally the operating system when we open files it assigns file handles that start from zero or one or two or three or some low number like that and just increments that's usually what it does but there's no guarantee and we don't really care. All that really matters is that the number should be unique for every open file. Anyway so the error here, the string that gets returned that is an indication of whether or not the operation succeeded because every file operation we do may fail because the nature of input output devices for reasons beyond control of our code it might not work and so the operating system when we do these operations we're making requests to the operating system asking it to like read these files for us and write data to these files for us and it may give us back an error saying that for whatever reason it couldn't. There are many different reasons why it can't do these things. Maybe the hardware failed, maybe when dealing with files maybe you don't have permission to do that operation on that file. Various reasons like that. We won't go into any details here but what we do need to know is that we need to get back some value indicating whether or not the operation succeeded and if it did fail we need to hear about it. We need to know what kind of error it was and so the way the operators here in GoPitch and work is they simply just return a string and if the string is empty then that means the operation succeeded. If it's not empty then there was some kind of error and the string describes what went wrong. So what we're gonna do is after each one of our file operations here we're gonna check if error, if it's not equal to an empty string then something went wrong and we're just gonna print out hey something went wrong and abort the program. Because then this is the kind of error where for the purposes of this program if we can't open this file then our program can't really do anything so we might as well quit. Normally we would expect it to work but even if these currencies are rare like the hardware fails on a rare basis or something else goes wrong on a rare basis you don't want your code to just blithely continue on when something goes wrong, right? You want to try and cope with the problem if you can maybe do something else, maybe try again, sometimes that is appropriate in some circumstances but in this case for our purposes we're just gonna quit. Sometimes you can't do anything so you just have to quit and that's what we're gonna do. Anyway so assuming that the file opened successfully error will be an empty string and it will come down here and it will create this slice of bytes and assign it to bytes. The slice of bytes has three bytes and it's a byte with value 100, two and 255 those are just random values I chose. And then we do a write file operation on the file. This is the file handle integer that we're specifying which open file we're talking about the one open file we have and we pass in bytes, the slice of bytes and it's gonna write out all of the bytes to the file. Well this is gonna try. Again for complicated reasons having to do with details of the operating system and the hardware, a write operation might not successfully write all the bytes you want. It might do a partial write, it might, if you request to write 1,000 bytes it might write only half or 600 or something less than 1,000, right? And so we get back a number, an integer which we're gonna store in n here which tells us how many bytes were actually written and we also get back a string which we store in error indicating whether or not something went wrong. And when a partial write does occur we get an error, we get a non-empty string for error. And so we're not actually gonna really use n here we actually don't really care what it is because a partial write is a kind of error in a sense it's not normally expected it's a relatively rare occurrence and I won't even go to the details. Circumstances under which you get partial writes that they're fairly exotic but they can happen and when exactly that is depends on your operating system depends on various circumstances that we will discuss here. But anyway, so then we're gonna check after the write we check error to see if it's not an empty string if so something went wrong and we print out the message of what went wrong and we bail out. Anyway, assuming that the bytes were written out in full and everything was fine then we're gonna continue on and we wanna write out the string to the file and the way we do that is we first have to get a slice of byte equivalent of the string that's what byte slice does it gets us a slice of bytes representing the data making up this string and we write that out and same deal we have to account for the possibility of an error but assuming there is no error we continue on and now we're done we've written everything that we want out to the file and at this point we could just abort the program we could just quit we don't need these remaining lines of code but we're gonna explicitly close the file and what that means is that every time you open the file there's the file handle associated with the file and internally in the operating system it keeps potentially a bunch of information about that file it might allocate some memory to store buffers for it uses when it reads and writes from the files and so it's good practice to explicitly close your files cause in a well in a short program like this when we just abort the program when we just close the program it automatically closes any open files anyway so we don't really need to do this we don't need to explicitly close the file but imagine a longer running program where say like a program that runs indefinitely as like servers do and over the course of their running they can open many files and you don't wanna leave open a bunch of files you're no longer using you wanna make sure to close them and so it's generally just good practice it's a good habit to form to just always explicitly close your files to make sure they're not accidentally leaving them open so just for the sake of imbuing a good habit here we're gonna explicitly close this file and again for fairly odd reasons files that you try to close it might actually fail so we actually should check for an error message saying we couldn't explicitly close the file there's again not anything we could do about it in this case and again the circumstances under which a file might fail to close are quite exotic and we'll defer those questions until later but for now we'll just check for the error and if there was an error we'll acknowledge it we'll print it out to the user and return so that's a program that writes a file now let's look at a program that reads a file which is quite similar the opening moves are the same we just open the same file in the exact same way check for the error returning if there was an error and then to read data from the file we need a so-called buffer we need a slice of bytes to read the data into from the file and just for good measure we'll make it much larger than we actually need we'll just make it a thousand bytes so this is creating a slice of bytes for the thousand bytes and then the read operation like write gives us back an integer we store an n here indicating how many bytes were actually read because reads can be partial like writes but in the case of a read that's not necessarily considered an error because it's actually very common for reads to not read all the bytes you requested and in this case because our bytes here has a length of a thousand the read file operation will attempt to read up to a thousand bytes in this case assuming that we didn't modify my file the text we just wrote it out with this program and now we're gonna read it well we know that this we know it's like 20 bytes at most it's not a large file so a thousand bytes is actually way larger than the file we're reading so this is definitely going to be a partial read but it may even be less than the full file let's say it's 20 so it could be like 10 or five or something less than the file anyway so the first time we read what we're going to do is we need to check if there was some kind of error because maybe something else went wrong maybe the file we were reading was on a flash drive but the user as we were trying to read the file yanked it out with the drive something like that could happen other things could go wrong so in the event of error not being an empty string something went wrong but there's a special kind of error we're gonna check for which is an error message denoted EOF capital EOF standing for end of file and this is the error we get that's not really an error it's just indicating that hey you reached the end of the file and so you're done reading the whole thing and in that case we're gonna break out of our loop because we're reading in a loop that just keeps reading because we know the reads are gonna be partial potentially and so we need to potentially read more than once so in the event though that we reached in the file that's when we break out in the event it's some other kind of error then we print out what the error message was and we just abort the whole program but in the event that there wasn't any error at all and we're not an end of file then we're gonna take from the byte the data was copied into the byte slice and we wanna read those bytes it was only end bytes though so we slice bytes from zero up to n because those are only the rest of the buffer the rest of bytes is not anything it's not useful data it's just whatever it's just as much as zero is actually the first time through so we just want the first end bytes of the slice we then take that slice of bytes, convert it to a string that's what this operation does and we're gonna concatenate it to message and assign the result to message so each time through this loop message just starts out as an empty string but the first time through this loop we read some number of bytes, convert it to string concat that to message and so each time through this loop we're reading more data, converting it to a string and then tacking that string onto our existing string message we're just building up the string each time through the loop do you understand this is really not an efficient way to build up a string that we read from a file because what we're doing is like I imagine it was a very large file and so the string would get very large and then each time through this loop we're taking the existing string that might be very large tacking on a little bit more to it and then creating from that a third new string which is the size of those two combined and imagine doing that many many times and you're creating all these large strings in a very wasteful unnecessary way when we get to real go we'll have a more efficient alternative to this solution so but for now this works for our purposes and we're dealing with a small file anyway so we're not gonna notice anyway that's how we build up our string and actually something I haven't said explicitly is that when we read and write from an open file with an open file is associated a so-called marker that as you read and write from the file it advances so say we open the file that's a thousand bytes in size and I read 10 bytes from it well the marker starts out when you open the file at the beginning of the file you read 10 bytes the marker advances by 10 bytes so the next time you read we're not starting at the beginning of the file we're starting at the 11th byte and reading from there so every time you read the marker is advancing and eventually the marker is gonna advance to the end of the file and when we try and do a read operation there's gonna be zero bytes left to read and that's when we get the end of file error only when the marker is at the very end of the file when we do a read so that's what's eventually gonna happen here most likely this will just take two iterations first read will read the whole file and the next read will give us back into the end of file that's the most likely thing to happen because we're talking about a very small file here and a small amount of data but not guaranteed so it could be more than two iterations through this loop anyway when we break out of the loop because we reached end of file we have our contents as a string message we can print that out and then we should close the file just like we did in write file and we're done okay so now let's look at these two programs in action first we're gonna run the write file program here we go and let's see if it created a file by file.txt yes I see it we'll type out its contents and there we go and what it's printing out is well the first three bytes we wrote were the value 102 and the 101 in Unicode 100 is the value of lowercase d 2 is a non-printing character so that's why we see this funny question mark box um that's what the command prompt displays when it doesn't know how to print a character and then E is 101 that's lowercase yeah so that's right and then hello file world right here that's the other stuff we print it out okay so so far so good and now let's do the read file read file program and yeah it's reading in the file and then printing out the console what it read in and it looks the same okay so that's all good now let's do something a little bit more interesting we want to write out what's called CSV data CSV stands for comma separated values not commas comma separated values it's a informal format there's no strict uh specification of what this data format looks like exactly but there's just a set of conventions and common practices the idea is that it's a text file where each line of of the text represents an individual piece of data or a composite piece of data made up of some number of elements those elements are separated by commas on that line so say we have our struct cat we're made up of a name, weight, and age and we want to write a bunch of cats out to a file well it would CSV would make sense it's a super format for that purpose and so each line would be a name then a comma a weight value a comma and then an age value and then a new line at the end to go down to the next line understand CSV is not really appropriate for all kinds of data it's a very simplistic kind of data representation it works out fine for a lot of things but for data that involves a lot of thing that stuff that's more binary like say a large array of a bunch of numbers you it would be an inefficient way of storing that data and generally not a good idea but for some simple stuff like say these cats it makes sense so what a program here does is looking down at main we create this slice of cats with three cats first is cat Oscar and Mittens and Fluffy and we're then going to iterate over all the cats and for each cat we're calling this method CSV which we defined right here CSV method takes your cat and returns a string representation of that cat it's returning the individual line for that cat so we first get the name and we decided that we were going to go in the same order here so name weight and age that's important to keep the order in mind so we get the name first then the weight and the age and name is already a string but weight and age weight as a float age as an integer so we have to convert those to strings so we do so like this and we're going to concatenate it all together putting commas in between them and at the end we have a new line what this is we haven't discussed this before I don't think inside strings backslash has special meaning the character backslash doesn't represent a backslash it represents the start of a so-called escape sequence which is two characters first a backslash and then some other character together forming an escape sequence representing some kind of character which we otherwise couldn't put in our strings we couldn't type it and show it in our strings so new line for example our strings in GoPidgin can't span across multiple lines so you can't just put a new line in the middle of your string if you want to write a new line in your string then you have to write backslash and those two characters together are an escape sequence representing a new line character there are a few other escape sequences there's backslash t for a tab character backslash r as a carriage return character and then backslash backslash is how you write an actual backslash so because backslash itself is used to denote the start of an escape sequence if you wanted to put an actual backslash you know to write two of them together and so now this string if we wrote this these two together represent a single backslash and then this would be a lowercase and not a new line character so this would actually be a two character string whereas this here is a one character string just a new line which is what we want here so that's our csv function it spits out an individual line representing that cat as a string but it's not actually writing it to the file yet anyway so in our loop we're for each cat we're getting the csv equivalent concatenating it to s so each time through we're building up the value of s s starts out empty each time through we tack on another line representing the cat we're then just going to print that out so we can see what we have just for demo purposes and then we want to write it out to a file so we want to take this string s write it all out to a file I've created this write function which takes a file name some file path and a slice of bytes and then it returns a string which is an error value so upon success we want to write function to return an empty string but then on failure we return something else some message explaining what went wrong so in write file so we're passing in test.txt the name of our file and we're passing in the string the byte slice from our string so the byte representation of our string data and inside write we open a file open the file we specified test if there was an error if there was some kind of error well we can't proceed so we should just return return the error message and then we try and write the file and if that succeeds then if that fails then we return early otherwise we go on and we close the file and if that fails then we return early otherwise if all these operations succeed then we're going to return an empty string indicating that the write completed successfully so down here after calling write sign the result to error and then we check if error is not equal to empty string and if it is and something went wrong we'll print out what the error was and return because in this program we can't really do anything in that eventuality so we might as well just abort but assuming it complete successfully the write happened successfully then we can go on and and then actually we don't even need this line that's not even correct and then the next line let's see we print out the well this is not even correct if you want to get the byte slice from a string you do that but that was just printing out the length of how many bytes were written but we don't even need that so I'm just going to delete that anyway so it'll print out successfully wrote file cats.csv and that's also wrong because I didn't call it that let's call it that cats.csv okay now it's consistent so now let's look at the readcsv program which is going to read in a text file we just wrote and for each line it's going to interpret each line as a cat as for each each line we get back a cat struct and so here in main we have this function read we've defined which we pass in a path and we get back the full text of the file and an error message something went wrong so we'll look at that function in a second but after we call this function we check if there was an error if so we print out what the error was in return because we can't proceed really our program has to just abort and then we're going to split the lines of the text we define the split function which you specify a string it'll go through the string and for each occurrence of that string it'll split the string into two into pieces so if I have a string with a bunch of new lines in it then I get back a slice of strings which is all the stuff around the new lines not the new lines themselves so we'll look at that in a second so effectively we're getting all the individual lines as individual strings in a slice without the new line at the end and then for each line we call the read cat function and that returns a cat struct assigned here to C and if there was some issue with the format of the text if it wasn't a properly formed cat like maybe it was missing commas or something was wrong about the data well then we get an error so our read cat function will return an error message if there was something wrong so we check after calling read cat we check if the if error was not equal to empty string and if so we just abort because something's wrong with the data and we'll just quit otherwise we proceed and we take that cat C and append it to the cat slice so once we go through this loop for each line we'll have a cat struct in the cats slice and so at the end of this well we'll just print out the length of cats which in this case should be three because that's how many we wrote out to the file so let's look at the read function the read function takes in the file name returns first to string which is the text of the file read and then the string the second string is the error message if any and so first we open the file check if check if there was an error then we have our buffer this code looks really exactly the same in that previous read file program we go through a loop where we read from the file we get some number of bytes back and if there was an error and it was end of file that means that we're reading from the end of the file and so we break out of the loop because we have everything we need otherwise we append to text we tack on to the text string everything we read from the file and that's what this line does it's exactly the same as what we did in the read file program anyway so we get through the loop and once we're out of the loop we can close the file because we're done with it we test if there was an error in closing the file and in the end we return the text and oops there should be an empty string here so every time we return we need to return two values in the case there's an error the first thing we return is just an it doesn't matter we return in that case really so we'll just run an empty string because all we care about and if there's an error all we care about is that what the error message says but upon success the error message is the empty string and then text is whatever we read from the file so that's the read function and the split function is kind of involved I'm I would actually go through it as an exercise you can see if you can figure it out there's probably a simpler more elegant way to write this but this works at least in my testing so given some string we want to split up into multiple strings and some splitter string in this case it was the new line string we get back a slice of strings and I'll leave the details for you to look at anyway so the read cat function that's more significant here that we pass in the individual line without the new line at the end and what we want to get back is a cat and then a string if there's some error this will be empty string in the event that we could properly parse a cat otherwise we get back some message indicating when we're wrong and so we have our individual line but the three elements that make up each cat are separated by comma so we're going to split the line splitting it on comma and we should get back a slice of strings with three elements the first should be the name the second thing should be the weight and the third thing should be the age and first we'll check off if the length is correct because if elements are something other than three then there's something wrong with the line it's a misinformed cat it's not properly a cat so we're going to return in that case well we don't I actually just created this variable cat here to make it easier to return a cat we don't care about it's just defaulting to an empty cat that has no values like it is an empty string name as a weight and age of zero but in the event that there's an error we don't really care what we're returning for the cat we just want the error message here so just a convenience thing that's why I created this variable C here so we can make it easier to return a cat we don't care about anyway so in the event there aren't three elements separated by commas on the line we that's what we return as our error message and in a way we proceed on to the next line what we want to do is we want to take the second element the element and index one and it's a string but we want to read it as a float it's a sequence of text characters but it should be interpretable it should look like a float value and so to get a float representation of that string we have the parse float operation be clear in the previous program in right csv over here we just took our float value and convert it to a string we can do that easy enough but this is a separate operation because we're going the other way around we're taking our our string and getting a float and so for that we have parse float and the thing about parsing a float or and also parse int as we use here is it maybe the string is malformed maybe it's a string that doesn't look like a float so maybe like what we want is something that looks like this and that's something you know string that looks like this and that's something we can use parse float on but if it looks like this well that's not a float and if it's just has characters that aren't valid number characters well that's not something we can parse either so in the event that it's a string that isn't properly formed as a float it doesn't look like a float then we get back a non-empty string error message it's actually an operation that returns two values it returns the value which we signed the weight here assuming it's successful but then upon error returns a non-empty empty string here so in the event we can't it's improperly formed if it's not a valid float string then we return and then for the age is the same deal except we're dealing with the integer so it's parse int instead of parse float but same deal we have to account for the possibility that it's malformed it doesn't it's not properly interpretable readable as an integer and in the case assuming it's properly formed everything's properly formed we get down here and we have our name weight and age and actually I made a mistake here let me correct this we didn't actually use any name variable we just need to get the first element from the elements slice there we go that's our name which is a string and weight which is now float and age is an integer and this is a success case so for the error string we return an empty string so that's the read cat function works on each individual line and that's the whole program so down here when we use read cat we can take a line and get back a cat or we get back a non-empty string error message if the line can't be interpreted as a cat so let's see if we can get these programs to run first off write csv okay that seems good so far let me look you know as cats.csv is there let's type out its contents oh I need to work on this the way it's converting floats into string values is in this format we probably don't want we want it to look something more sensible cleaner we don't need this engineering notation style but let's just go with it for now that that's valid okay and now let's look at the read csv program in action after I've made some fixes just now and it prints out three which is correct because we have three lines each one represent a cat and that's how it should be so it seems to work we could delve in more and see if it is actually creating the cats correctly but I think we're good well let's try it let's probably printing out the actual cats see how it prints it yeah there we go that all looks good