 Okay, so now we're going to move on from dynamic pigeon to static pigeon. And what those names imply is that static pigeon is a statically typed language, whereas dynamic pigeon is a dynamically typed language. And this distinction is that in a statically typed language, as you'll see here, we have to specify the types of all of our variables. And also for our functions, we have to specify what the input and output types are. You can't just say that a function takes in say three parameters, you have to specify what types those inputs are supposed to be. And the point of all this is that at compile time when the compiler compiles their code, it can then look at all the operations and all the function calls and so forth, and all the assignments, and make sure that everything is being used appropriately. So if I create a variable and say that it's a string variable, something that can only hold strings, that is then enforced by the compiler, I can't accidentally assign it something else. If I do, the compiler will catch the error, and it will refuse to compile my code. This is different from a dynamically typed language like dynamic pigeon, where we don't specify any of these types. And so the compiler can't enforce any such restrictions. You create a variable in dynamic pigeon, and you can assign anything to it, you can assign it a string, and then later on, you can assign that same variable, a boolean or a list or whatever, the compiler has no such restrictions. And then what's going to happen at runtime is that if you call a function with say the wrong kind of input, you wrote a function, it's expecting to say a string is input, you pass in a number instead. What's going to happen is that inside the function, there's some operation where you use the thing that's passed in, and you try and use it like a string, but it turns out it's actually a number, that operation is then invalid, like if I try and perform a get char operation on a number, that's invalid, you can't do that on numbers. In dynamic pigeon, when you perform such an invalid operation, that triggers an error at runtime, the program compiles, and then it starts running. And when some operation like this is performed a bad operation with incorrect inputs, that triggers a error that's going to abort the program. The idea of statically typed languages like static pigeon is that by specifying these types in our code, the compiler can check for all these problems, and thereby prevent your code from compiling until you fix them. And so it's much easier to find these problems. The argument for statically typed languages is that you don't have these type errors lurking in your code, whereas that's perfectly possible in the language like dynamic pigeon, because you could have a bad operation somewhere where you call a function incorrectly, but maybe it's in a branch of code that only executes very rarely. And so what's going to happen is that the code will seem to work, but then some unusual circumstance arises where that branch of code executes, and then you get that error. Another reason for static typed languages is that they generally perform better. Because at compile time, the compiler can know exactly what kind of piece of data is stored in a variable, and what the inputs are supposed to be for a function of what it returns. And so when the compiler knows what the types of things are, in many cases that knows exactly how big it is in memory, but then at least in many cases knows exactly how big the thing is in memory. And so it can perform certain kinds of optimizations that just aren't possible in a dynamic language where the types of things aren't nailed down at compile time. So can't assume the type of anything. And so can't assume its size. And so if you look at the languages that are generally more performant that generally perform better, generally they're static the type languages, like C and C plus plus, for example, one reason they're more efficient generally than say Python code or JavaScript code is because they're statically typed languages. Anyway, looking at this brief artificial code example here, what's going on is we have a function called amber, just like you would expect coming from dynamic pigeon, it has a parameter called x. But then we specify the type of the parameter. And we say it's a string. And type names here in static pigeon are always distinguished from other names, by virtue that they begin with an uppercase letter. So why is the second parameter bool is its type. And then the colon here is separating the return type of the function we have to specify for this function amber, what type of thing is going to return. And in this case, we're saying that amber returns a boolean. And so having specified that amber returns a boolean everywhere in amber where we have a return statement, the value here has to be some expression something that's returning a boolean it can't be something returning a string, that would not be legal. And in fact, because we've specified return type, this is a function that is going to return something rather than nothing. We can't have anywhere in amber a return statement returns nothing. And there has to be a return statement at the end. If we didn't have this return statement at the end, the compiler would complain about that say, hey, this function is supposed to return a boolean, where's your return statement returning a boolean. So looking here in the body, first thing we're doing is we're printing out x and y. And this is okay, because print line is an operator that can take any kind of input x is a string wise boolean, the compiler knows that. But it okays this operation because print line is one of the operators that'll take anything it works on anything, no matter what the type, just like in dynamic pigeon. But then here, when we assign y the value of not x, that's a problem. Not as an operator that expects a boolean input, not a string, but we're passing a string as input here. And that's just invalid. So the compiler is going to give us an error because of this line, the code will not compile, and we'll have to remove or fix this line. And so it makes sense. So it's legal. And then on the next line, this is also a problem because we're taking the value of y, which the compiler up here, we've said it's a boolean. So the compiler knows it must be a boolean, why is always a boolean. If we assign to x value of y, this is also illegal. This is a compile error, because you can't assign a boolean to x. And then on the next line, we also have a compile error problem, because we're signing the value of y to x. And in dynamic pigeon, this would always be fine. But here, why we've declared to be a boolean and x is a string variable. And so the compiler knows for sure at compile time that why whatever it is, it's a boolean, and x is a variable to which we're only supposed to assign strings, the compiler's attempting to enforce that to make sure x is always a string. And so this is an invalid assignment, because a variable is declared to have a certain type, and so you can't assign anything of another type to that variable. Down here in the main function, main looks like it does in dynamic pigeon, where we don't give it any parameters. But also, we don't give it a return type, because main is a function that never returns anything. It returns nothing. This is different from dynamic pigeon, where a function that returns nothing, if you don't have a return statement at the end of your function, it implicitly returns nil. That's not going on here, main actually returns nothing. So if we have a return statement anywhere here in main, in fact, we'll add that, if we just have a return right there, this is legal, it's fine to have a return saying nothing, we don't need it here, it's implicitly there at the end of the function. But it would be totally improper if we then like gave it a value because main as as we've declared as a function returning nothing, so it can't return anything. And then inside main, we've created a local variable answer and given it a type boolean. So just like the parameters have their type listed after each parameter, same for when we create locals after each local variable, we specify its type, in this case, answer is a boolean. And then we're going to print out the value of answer. And the question is, well, what is the value of answer? Immediately after local statement, the value of every variable is the default for that particular type. So for answer, it's a boolean, and the default value for boolean is false. So that's why this line is going to print out false. The next line is an okay line of code, the compiler won't complain about anything here, because we're calling amber, passing in a string and a boolean. And that's all proper, because we've specified amber supposed to receive a string and then a boolean in that order, that's, that's okay. And then the return value, what's returned from amber is being assigned to answer, which is a boolean variable. And that's okay too, because we said that amber is a function returning a boolean, the compiler looks at all this and says, this all checks out, amber has the right inputs. And then the assignment to answer is also correct because this expression returns a boolean, and that's an acceptable kind of value to assign to answer. In the next line, this is also okay, we're printing out the value of answer, but now it's the value true because we passed in false to amber, what it returns in the end is it takes that input boolean and reverses it. So we get back true. So we should see print true from this line. This line, though, is a problem, this will trigger compilation error, because the first argument here, we have the right number of inputs, we have the right number of arguments to amber, there's two. But the first one is not a string, we set up here that amber is expecting a string as its first argument. And so if we try and call amber with anything else, the compiler knows what type of function amber is. And so it can look at this and say, well, this is invalid. I know what this is, this is a number that ain't a string. So it says we have a compilation error. So we have to fix this line or remove it before the compiler will accept this code. Another thing different in static pigeon is that whereas in dynamic pigeon, we just had numbers, and that was one kind of thing. In static pigeon, we have two kinds of numbers, we have integers, and we have floating point numbers, aka floats. Maybe you're not familiar with this distinction, I talk about it in another video about number representation as bits. In short, an integer is a whole number, either positive or negative. And a float is a number say like negative 2.7. It's a number value that can be fractional, it doesn't have to be a whole number an integer. In dynamic pigeon, like in other dynamic languages, for the sake of just simplicity and convenience, we just had the one number type, and you could you have integers or floating point values, and they were just all the same thing. And that may think simpler. But when you consider how numbers are represented as bits in the machine for efficiency reasons, statically type languages generally have a specify what kind of number. In static pigeon, we just have the two types, as you'll see in other languages, will have different sizes for integers, like how many bits are used to represent this value. We'll get into that in when we get to real language like go or see. But here we just have the two types. And so here we're creating a in main, we have a local variable I of type integer, I standing for integer, of course, and F, which is a float. And assuming this means something to you, they're both represented in 64 bits, and that implies a certain range of possible values. It's a very large range. So it's extremely likely you're going to need any value outside that range. So I wouldn't really worry about it. But in any case, so here, this assignment is okay, we're assigning an integer value five to I. And this line is also okay, we're signing a floating point value to F, so far so good. Here, though, the compiler looks at this and you know, 5.0 5.0 could be an integer value, but the way it's expressed with the point and you have the decimal point here, the compiler considers this to be a floating point value. So you can't actually assign this to I, that's illegal. Conversely, any number literal, any number written in code that doesn't have the dot in it doesn't have decimal point, that's considered to be an integer. So this is also an illegal assignment. Be clear, in terms of values, 5.0 and negative two, those could both be integers or floating point values in terms of just what the value is itself, like mathematically, 5.0 is equal to the integer five, they are really the same thing. But as far as our language is concerned, it considers them to be different types of things. Because internally, the way they're stored in memory is different the way they're represented in memory is different. So it wants to enforce this separation this distinction. And so when we do operations, like saying add operation or other arithmetic operations on numbers, mold, div, sub and mod, all of these operators work on both integers and floats. But for any one operation, the inputs have to be the same type. And then that particular operation returns that type. So here in this line, we're adding I and seven I has been declared to be an integer. So seven is a number literal, which is also an integer. So this is all valid. And this operation is returning an integer. So this assignment is valid to. And then this line down here is also good because we're adding a float to another float. And getting back a float, which can then be assigned to F, that's all valid. But the compiler is not going to like this line because we're attempting to add an integer float. So compile time is going to say, hey, you can't do that. The core issue here is like, okay, logically, it makes sense to add integers and floats together. But then what does the operation return that becomes ambiguous. So the compiler says, well, you just can't do it. I'm not going to let you add integers and floats together. And so this whole line is bad. Now, of course, sometimes in your code, you're going to want to take integer values and say add them to float values or subtract a float from an in or etc. You want to combine them into the same operation. But what you have to do is you have to convert either from integer to float or float integer so that for the sake of the operation, the compiler is happy that they're the same kind of thing. So here, for example, I'm going to take this line and proper version of this line, if we wanted to add an integer and a float together, and store the result as an integer, then this means this ad operation will we need it to return an integer. So we actually need both the inputs to be integers, but we can take the float value here f and convert it to an integer by using this expression. So this is using the type name itself like it's a function. And in this case, if you call I like a function, it's expecting some number as input. And what you go back is a number of type integer. So it's taking this float value and converting it in a sense to its equivalent as an integer. Now, depending upon what the value of f is, let's see f at this point would be, well, ignoring all these lines that won't compile, so we took that stuff out, then the value of f would be negative 2.7. Well, how do you convert negative 2.7 to an integer? Well, the answer is you really can't you can't have an exact equivalent of two negative 2.7 as an integer because it has a fractional component. What static pigeon will do in that case is it just strips off the fractional part that just truncates all that off. So we get negative two in this case. So be clear in some kinds of conversions between different data types, of course, you can't perfectly represent one thing in a different form. So there has to be some kind of distortion as happens here. So this will return negative 2, the integer value, which will be added to i and the result of this ad operation will then be assigned to i. Another change when we go from dynamic typing to static typing is that our collection types lists and maps, they can't just hold whatever. What we can do in dynamic pigeon is we can create a list and we can just stuff it with values of any type and it doesn't care what we put into a list. You can have a single list that has strings and then it has an integer and then another string and then a boolean whatever. It can just be a heterogeneous mix of whatever we want in a single list. Can't do that in a static type language because the compiler needs to know at compile time, hey, what's going to be in this list such that when I get a value out of the list, I need to know what it is. And the compiler can't know that if it's only determined at runtime where the code actually runs, if it's determined by the code execution, what's in the list. So our collections in a static type language have to be homogenous. When we have a list in static pigeon, it has to be a list of particular kind of thing. It has to be say a list of integers or a list of floats or a list of strings or a list of booleans. We can have lists of any kind of thing. It's just any particular list is a list of one type of thing. So the way we notate this in dynamic pigeon is that we say l for list, this is the type name, but then we have to specify what kind of list it is in the angle brackets here. So this is saying that this x, local x is something that stores a list of integers. And then when we create a list of integers, we use the type name for list, like it's an operator. And I wouldn't call this a conversion. This is the equivalent of the list operator we had in dynamic pigeon, we're just creating a new list of integers. And all these values are the initial values that make up that list. So x now will be a list of integers with a value six, negative 32 and 14, it's going to have a length of three. And then on the next line here, when we do a get operation on x, we can use get on any type of list, whether it's a list of integers or strings, but the compiler knows what kind of thing x is it says Oh, yeah, it's a list of integers, you set that up here. And so it knows when you do a get operation on x, that what we get back is going to be an integer. And so this line is okay, because the local variable a is known to be an integer, that's how we declared it. This is fine. But then on the next line, we're trying to sign the results of get x zero to be, well, B is a string variable, and we can only assign strings to it. So there's a problem on this line, because the compiler knows this operation here returns an integer, it gets a value of the list of integers, so it's going to be an integer. And so it knows this is an invalid assignment. And so this line means you can't compile the code. Another thing that changes is that when we use a forage loop on a list, here, we're specifying the list that we're iterating through. And here is the index variable, and the value variable. Again, just by convention, we call them I and V, but they could be anything. Well, here we have to specify the types that I totally made a mistake. I should just be I. So if we're going to loop through x, which is a list of integers, well, the indexes for any kind of list are going to be integers. So this is always can be I here, basically, when we're doing a forage over a list. But then the values that depends on what kind of list x is an x in this case is a list of integers. So the values we have to say they're also integers. Arguably, I could have allowed for each loops to just infer what these types are supposed to be, like it knows what x is, so then it can just infer that I and V have to be integers too. But I thought it better for the sake of clarity if you have to make it explicit. So that's why you put the types there. Anyway, so this is a forage loop that is going to print out the values of the index and then the value of each successive elements of list x. Down here in this other example, virtually the same code small problem here, I'll fix up. Yeah, so let's see, is this exactly the same is exactly the same except instead of list of integers, it's a list of strings. And so we have our initial list of strings with three strings cat dog and moose. And this is the line that's going to give us a problem because A is still here an integer and you can't this is going to give us back a string as far as the compiler knows. So it knows this is an invalid assignment. That's a problem. This line knows now okay because B is a string. And so what we get out of this x here, which is a list of strings, that's going to be a string. In the forage loop, we're iterating over a list of strings. And so the index, as usual, going to be an integer, but then the value is going to be a string. So you have to specify string here, str for string. When we work with maps and static pigeon, again, their collections where we have to specify what kinds of things they contain. With lists, we specify just one type because list contains just one kind of thing, but maps can contain multiple things, you have both the type of the keys, and then the type of the values. So the way this is notated is m for map, followed by two types in the angle brackets, first the type of the keys, and then the type of the values. So x here is a variable which can hold a reference to a map. But that map has to be a map of string keys to integer values. So here on this line, this is valid because we're creating a map of strings to integers. Here's the first key with its value, the second key with its value. Though maybe I shouldn't really say first and second, right, because there's no sense of order of the key value pairs in any map. So it would be exactly the same if I move this around, still the same map, right? Exactly the same. Anyway, so then when we loop through map x with a for each loop, for the variable receiving the key each time through, we have to specify it's a string because it's a map with string keys, and then the value I got this wrong again, that should be I because it's a map of strings to integers, the values are integers. Do I get this one's correct down here? This other example down here, virtually the same except now it's a map of floats to strings, that's what that's saying. The keys are floats, the values are strings. And so here's one key value pair, here's another. And then when we use for each to loop to this map, we have to say that the keys are floats, and the values are strings. Again, these types, I could have made it the rule that the language just infers their type because it knows what x is going to be. But I decided that I prefer the language require you to make it explicit. So you have to put the types here.