 Everyone, all of my lovely 340 students, this is the recorded lecture for March 28th. Unfortunately, I will be out of town, so what we're going to do is we're going to keep the momentum of the class going, and we're going to continue right where we left off. So if you recall on the Monday before the midterm, we were talking about type equivalents. So specifically here we're talking about in a type system, how can the type system tell if two types are the same? So we've looked at various types of type systems. We looked at type name equivalents, which specifies that for two types to be equivalent, they must have exactly the same name. So if we define a new type, even if that type is the same as a basic type, that because that new type no longer has the same name as the basic type, that that is not name equivalent. And we saw an interesting case of what happens when you have anonymous types. So anonymous type is a type with no name. So in that case, even two variables with the same anonymous type will not be name equivalent because there is no name. They cannot be name equivalent because anonymous types do not have a name. So we said, okay, this is probably a little bit too restrictive. So what can we do? So how can we improve this? So in the next step of the evolution, we then looked at internal name equivalents. So we looked at two things can be equivalent if the compiler would give them the same internal name, right? And this comes up specifically with anonymous types and specifically with multiple variables that are declared to have the same anonymous type. So those types are type equivalent. And then on Monday, we continued looking forward and we said, well, but you know, these rules are very restrictive, right? So what if we try to give the programmer more expressivity, right? Then how can they how can the type system tell if two types are equivalent based on really the underlying structure of the data type? So for instance, if one type is an int and the other type is also an int, but different names, we maybe still want that to be the same. But we have to look at what happens? How do we deal with structures and pointers and structures and pointers and functions, right? How do we determine the type equivalents of them? So this brings us to today. So we've been talking about on Monday, we've talked about structural equivalents, right? So we've looked at we want to look at these two structures A and B and determine that A and B are both the same, right? So we're defining a new structure A and a new structure B. We define two new variables foo and bar that have the type A and B. And we want to answer the question, is foo and bar the same thing, right? And so this is where we get into the structural equivalents rules of determining that, right? So we went through the rules for the basic types. So basic types are structurally equivalent if they're the same. Pointers to basic pointers are the same if the types that they're pointing to are the same. Structures as we saw, remember we talked on Monday that structures can be structurally equivalent if and only if the types in the order are the same. So the key here is that as we can see here, we have the structure field names. Here are x1, x2, all the way to xk. Here in structure two is y1, y2 all the way to yk. But we're ignoring to tell if two structures are structurally equivalent. We ignore the name of the field and we just want to look at the types. So we say, are the types of the first field structurally equivalent? W1 structurally equivalent to WQ1. And we do this for W2 structurally equivalent to Q2. And we keep going through all of them until all the way to the end. So this condition must hold. For two structures to be structurally equivalent, this must hold. So we saw that. And so we saw that here we have a structure A with field A int followed by field B float. Here structure B followed by B, a field B which is an int, and field A which is a float. And so if we have two structures, foo which is type A, bar which is type B, and we want to ask the question is foo equal to bar, under structural equivalents these are structurally equivalent because the first two types, the first two fields are structurally equivalent and the second two fields are also structurally equivalent. So the important thing here is that the name doesn't matter. And so similarly if we have the same thing, struct A and struct B where A has a field int, B has a field float, and struct B where B has a field float, where the first one, the field name is B which is a float, and the second structure name is an A which type is an int. And here even though we can see A int, A int, B float, B float, the order in structural equivalents is what matters. So foo and bar here would not be structurally equivalent. And we can just quickly update this, see these are how problems get fixed. Awesome. So we also saw that two arrays, if we want to determine if two arrays can be structurally equivalent. So if you have two arrays, remember and in this kind of fake type system that we're defining, right, we say that arrays have a certain range. So T1 has an array of range one of some type T1, T2 are arrays of some range two of type T2. And so we say that T1 and T2 are structurally equivalent if and only if the ranges are the same number of dimensions and the same number of entries. So essentially the ranges are identical. And T1 and T2 are structurally equivalent. Right, seems pretty straightforward. And we saw that two functions are structurally equivalent if they have the same number of parameters. And all of those parameters are structurally equivalent, right. So in order, so the first parameters are structurally equivalent, the types of the first parameters, the types of the second parameters are structurally equivalent, the types of the third parameters are structurally equivalent, all the way to the end of the very last type are structurally equivalent. And finally also the return values are structurally equivalent. So now we get to today's topic is how do we actually determine what's the algorithm for determining if types are structurally equivalent. And so the goal is to determine we want to be able to try to understand for every pair of types in the program are they structurally equivalent. And we want to be able to calculate this because we want to be able to answer the question, are these two types structurally equivalent? Does the types system allow this assignment operator because these two types are structurally equivalent? So the outcome of our algorithm we want to know for every type in the program with every other type if they're structurally equivalent. So when you see something like this we want to see for every pair of types we want to think a table right we want to make a two-dimensional table to try to understand the structural equivalents of the types. And so in a naive way how you may do this is fairly simple. So we saw for all the rules they are all recursive rules right. So in functions as we just saw functions are structurally equivalent if the types of the function parameters are structurally equivalent. Arrays or pointers are structurally equivalent if they point if what they point to are structurally equivalent. And so it seems really straightforward right we should be able to just keep applying these five rules over and over until we get to our base case of one and two right. So the base case of types being equal and the other base case. So let's look at one and two here right. So one are same built-in types and actually so one is actually the base case right pointers are structurally equivalent types. So I will update this right one is our base case great. But we run into a problem so let's let's look at what would happen. So right so let's say we have over here so from this example we have some type t1 and t1 is we're defining as a struct right where the first field we'll call it a is an int right and the second field p is a pointer to t2 and here we have t2 or this is a struct where we have a is an int and p is a pointer to t1 right okay. So once we get here right so the question is how do we determine so let's apply our rules to determine if these are structurally equivalent right. So we want to ask we want to ask the question are t1 and t2 structurally equivalent right. So we know from rule three right that t1 and t2 are so just keep applying these rules right. So t1 and t2 are structurally equivalent if the first fields are structurally equivalent. So to satisfy this first one w1 is structurally equivalent to q1 so the first field type is in t1 is the same as the first field type in t2 so the first field type in t1 is an int the first field type in t2 is also an int so we say our two int structurally equivalent yes so this is structurally equivalent so this condition passes and then here we have are the second parameter structurally equivalent pointer to t2 pointer to t1. So now we want to ask the question right so applying these rules here forces us to ask the question are these two types this equivalent right are structurally equivalent so is pointer to t2 is that equivalent to pointer to t1 right so that's the question that we want to answer. So okay let's look at our pointer rule right we have our pointer rule is number two so pointer to t2 pointer to t1 pointers are pointers are structurally equivalent if what they point to is structurally equivalent oops I just hit some button here that I don't know what it was that's fine right so these are structurally equivalent pointer to t2 to pointer to t1 if what they point to are structurally equivalent right so we say is t2 structurally equivalent to t1 right well how do we do that well let's go back here so we say okay they're structurally equivalent if they're structures so we know the rules for structural equivalents are there we go found the button okay right so let me go back to our rule for structural equivalents and we say okay how do we determine these two structures are equivalent well if the first two fields are equivalent int and int then they're structurally equivalent great those are good let me check pointer to t2 pointer to t1 our pointer to t2 pointer to t1 right pointer to t2 is that structurally equivalent to right pointer to t1 well these are going to be structurally equivalent if and only if these are structurally equivalent so we go is t2 structurally equivalent to t1 and hopefully at this point you're screaming at your computer please Adam stop doing this you're gonna keep going forever right we can see we've already reached this point and so we're gonna keep looping here trying to determine if these structures are structurally equivalent right so how can we fix this right so when we apply these rules we're saying that well t2 is structurally equivalent to t1 is structurally equivalent to t2 if and only if pointer to t1 is structurally equivalent to pointer to t2 which is true if and only if t1 is structurally equivalent to t2 dot dot dot on and on again forever right so now we kind of reach a conundrum so are these two structures structurally equivalent and this is actually something I want you to kind of maybe you can pause the video for a little bit and think hmm intuitively should these two structures be structurally equivalent right could you use in place of t1 right where your program is expecting a t1 could you give it a t2 and have that execute the same right so think about it like this so I have those two types and now here in my program I'm declaring a variable of type t1 called foo right I do stuff with foo then later on I'm declaring a variable of type t2 called bar right and now I want to say well I want to use right I want to copy foo into bar so can I do that or to put another way let's say I have some function right Baz and let's say Baz takes in an argument of type t2 right it's written to take in and I'm gonna call it a because I'm running out of variable names it takes in something of type t2 so it knows it can do t2 dot a dot a to get an int right so let's set this to be 42 and we'll have a dot p right which is a pointer to a t1 and let's say I want to get the in it after that so I know it's a pointer so I'm gonna need to if I'm writing kind of see pseudo code right I'm gonna need the arrow operator here so a dot p arrow a right so I said that equal to a hundred so the question is so clearly I can call Baz with bar right we can see bar is of type t2 the function Baz accepts parameters of type t2 clearly I should be able to do this right but the question is can I call Baz with foo right is this a valid call can I every place in this function right that I use t2 could I use a t1 interchangeably well it should be clear from where we're trying to calculate this that yes we have an a structure the a field is the first field here so clearly I can use that here that's gonna be an integer that's not gonna be a problem right and the other thing is so it has a p field right which is gonna be a pointer to here t1 to t2 so I can dereference that pointer and then access the a field set that to be equal to a hundred so really I can use a t1 here when I was expecting a t2 right because the pointers here are pointing to types so it's kind of almost as you can see we get into this infinite recursive thing well t1 and t2 are structured equivalent right if the pointers to t2 and the pointers to t1 are structured equivalent and those are only gonna be structured equivalent if and only if t2 and t1 are structured equivalent which is the whole question that we're trying to resolve in the first place so what we're going to do is we're gonna what if we assume that t1 and t2 are structurally equivalent right then pointers to t1 and pointers to t2 is fine because t1 and t2 are structurally equivalent right so that's how we're gonna do this and that's how we're actually gonna break this logjam that we get in is we're going to assume from the start that t1 and t2 are structurally equivalent right because when we start we know nothing about the types so we just assume that all the types are structurally equivalent right and then what we're gonna do is we're gonna go through and try to prove that types are not structured equivalent because they don't follow the rules right so you can see how the alternative our previous way that we were thinking about this is well we first assume that you know this doesn't say that there's an assumption necessarily in here but there is right there's an assumption here that the types are all types are not structurally equivalent and we need to apply our rules to prove that they are structurally equivalent well an alternative way of thinking about that right is well why don't we do this from the other way assume that all the types are not structurally equivalent or sorry assume that all the types are structurally equivalent and if any type violates one of those rules then we say that it is not structurally equivalent right so you can see that we're trying to get to these get to the actual rules the actual structural equivalent from two different directions either assuming that they're all the same or assuming that none of them are the same and we're gonna use those rules to work towards the actual type system sorry the actual structural equivalents so like I mentioned right our goal is to create an end-by-end table where n is the number of types in the program and we want each entry in the table is going to be true if the types are structurally equivalent or false if the types are not structurally equivalent and so in order to support this cyclical definitions right this is why we're gonna do it this way is we're gonna assume that all everything is structurally equivalent so we're gonna create our table and we're gonna set every element in that table to be true and so the way to think about this is right where we know nothing before we approach this types we know nothing we have to assume that they're all the same or they're all different right so we're gonna assume that they're all the same until we have proof otherwise because they contradict one of the rules and so once we have this once we start here so this is this key idea is starting here then the algorithm itself is fairly simple so we first set the end-by-end table to have each entry is true and while the table has not changed so this should be very similar sorry this should be very familiar to you right what think about what other algorithms we've talked about that we keep executing and going until nothing has changed right so hopefully right in your head popped in oh yes first and follow sets right when we calculated first and follow sets we did those until the table until our first and follow sets had not changed right and I think at the time I mentioned that this is a technique called a fixed point so we're continually applying an algorithm until nothing changes of the mathematical term for that is a fixed point so this structural equivalence algorithm is the same so we're gonna keep applying these rules until we haven't changed anything so you can see this kind of pseudo code right so while the table is not changed check each entry i and j in the table so each pair of types right and if ti and tj are not structural equivalent so you check using the rules the structural equivalent of the two types then you set the entry in the table to be false right and so ti and tj are the ith and jth types in the programs right it doesn't really matter what you name them as so let's go through an example using that logjam example that we had right so here we have a structure t1 is a structure it has a field a as an int b a field p which is a pointer to t2 t2 is a structure which has a field c which is an int and a field q which is a pointer to t3 and t3 is a structure that has a the first field is a float and the second field p is a pointer to t1 right and so the question is are these structurally equivalent and so hopefully by looking at this you can see it's actually not clear right away what the answer is going to be right and you can imagine if there's more types it's gonna be even more confusing so this is why we have this algorithm to calculate this so we're first going to create our table right and what's and in this case and is the number of types in the program so we have three types so we're gonna have a three by three table and we're gonna write each of the types right t1 t2 t3 along the top here and t1 t2 t3 along the side here and remember the first step of the structure equivalence algorithm we're going to assume that everything is true that every type is true is structure equivalent and then what we want to do right we're gonna go through each of these types t1 t1 t1 t2 t1 t3 and see are they actually structurally equivalent right and so this is what we're gonna do we're gonna say our t1 and t1 structure equivalent well yes they're the exact same type they are definitely structure equivalent right that's one way to do it we can actually go through this right and say well an int is structure equivalent to an int a pointer to t2 is that structure equivalent to pointer to t2 well pointed to t2 is structure equivalent to point to t2 if t2 and t2 are structure equivalent so how do we determine if t2 and t2 are structure equivalent we look at our table and we see t2 t2 yes that's true yes at this point they're structurally equivalent so we say yes t1 is structured equivalent to t1 then we go on to something more interesting we say our t1 and t2 structure equivalent right so we look here and we say okay well they're both structures so we know that two structures are structured equivalent if and only if the first field types are structured equivalent so is an int structure equivalent to an int yes that's rule one those are structured equivalent then we say let's look at the next field are they equivalent we have pointer to t2 and pointer to t3 so we know from rule two that pointers are structured equivalent if and only if the types that they point to our structure equivalent so we say is t2 structure equivalent to t3 now this is the very important thing right just like when we're calculating first and follow we always refer to the previously calculated value of t2 and t3 right we don't go through and try to recalculate new values for t2 and t3 or try to determine if t2 and t3 are structured equivalent we have this beautiful table that we're building about the structural equivalent of all the types in the program so we look up in the table the types so we say is t2 structure equivalent to t3 and we say yes right we say the the entry here is true which means at this point t2 and t3 are structurally t2 and t3 are structurally equivalent and then we say okay great that means t1 and t2 are structurally equivalent right so we check these fields check these fields check those and say great they are structurally equivalent so now we move on to t1 and t3 so we first look right at the first fields int and float are ints and floats structurally equivalent they are not structurally equivalent so then we change this entry to be false so you can think of it as we've essentially at this point proved to ourselves that t1 and t3 are not possibly structurally equivalent right because that first structure field is not structurally equivalent now the other thing that we have to keep in mind right is we really only care about half of this table right because structural equivalents is I cannot remember the term for it right now if we're in class one of you could definitely help me out I want to say communicative or associative so if t1 is not structurally equivalent to t3 then t3 is not going to be structurally equivalent to t1 so we need to make sure to update in our table the reflective one right just to make sure that we have that great so we did all t1 against all the other types so now we're going to do t2 against all the other types right once again we're not going to start with t1 t2 t2 t1 because we've already calculated that so and you can kind of see hopefully you can convince yourself that along the diagonal here right there's always going to be true because a type is always going to be structurally equivalent to itself so the only other one to check here is is t2 structure equivalent to t3 so then we look is the first field their structures is the first field in t2 equivalent to the t3 so I have an infinite float so it tells me that nope they are not structurally equivalent and I have to update in my table t3 t2 so now I've gone through all the pairs I've gone through t1 against t2 t3 t2 against t3 right so I've compared all the pairs and now I say well am I done have I I've gone through all of them have I calculated have I properly calculated all the structural equivalents here remember and just like first and follow sets the answer is no because we we went through applied all the rules and we made changes to this table so we have to make sure we go through again and calculate apply the rules again so we do exactly what we did so let's check t1 and t2 and say are these structure equivalent so let's look at our types t1 and t2 so we have t1 which is an int t2's first field type is an int as well so we say those two are structurally equivalent great so now we check the other one pointer to t2 pointer to t3 well we know okay good they're both pointers and pointers are going to be structure equivalent if and only if t2 is structurally equivalent to t3 now we look up in our field is t2 structurally equivalent to t3 false it is not which means t1 is not structurally equivalent to t2 so that's going to be false so we update that in our table and then we'd go and check t1 t3 and we'd see t1 t3 those are definitely still not structure equivalent we check t2 t3 and say yes these are not structure equivalent we'd say I made changes I have to do it again so we go through it one more time and if you go through it you will get this exact same table right and so we can see here now that this example shows how we can calculate that so let's do a few more examples of this let's say we have types let's say t1 is a string t2 is going to be a pointer to t1 and t3 is going to be a pointer to a string right so just like before so and then let's clear some variables so here I have my types and then I have my variables so let's say I have x and y are both of type x let's see x is type t2 right y and z are both types of pointers oops pointers is not a type it's pointer to string right okay so now I want to say okay so what are what types are structure equivalent right so that's one way to think about this what types are structure equivalent the other way to think about it is can I do this oh let's I'm going to learn other variable w that's type t3 right so can I do x equals w under let's say structure uh name equivalents right can I do this under name equivalents under internal name internal name equivalents structural right this is something that definitely you could get asked on a midterm right so this demonstrates that you understand how to apply these type equivalence rules so you think about what actually is this question asking right so this question is asking is the type of x is t2 the type of w is t3 so it's asking all of these questions assuming name equivalents is t2 name equivalent to t3 right so it should be very clear is t2 the same name as t3 no no not equivalent are they internal name equivalent right no they're declared as different types they are not internal name equivalent either are they structurally equivalent this is when we need to create our table but before we do we have let's look at maybe another question that they could ask us right or I guess when I say they I kind of mean me so let's have y equal w right can I do that so name internal structural right so y and z name equivalent so the name of type so right when we think about y is w remember we're talking about the types so w's type is t3 but what's y's type so hopefully you can realize at this point that the type of y right here right this has no name there is no pointer to a string type uh this is not a name so it's an anonymous type so when we say are they name equivalent no they have no name they are not name equivalent are they internal name equivalent no right this is declared here this is declared here so internally they're gonna have different names so no they're not an internal name equivalent finally are they structurally equivalent so what this means is looking at the types here right let's see can I move this to get more no it's just gonna zoom okay that's fine well now I have a mouse so now I can do stuff like move things down okay so fancy right so now I can move this down sweet but now I want to remember I want to be able to answer this question so how do I determine if these are structurally equivalent well remember as we said to determine structural equivalents you have to put all of the types in your type system in your program you have to put them in this table right so the types in the program are t1 t2 t3 and this anonymous type here so let's give it a name we're gonna call it let's call it alpha because it's one of the greek letters that I can draw so we'll call this alpha and alpha is going to be this type here so we have four types in this program that we want to determine the type the structural equivalents right so let's build the table we'll do t1 t2 t3 and alpha along the top and then we will tax my straight line drawing abilities not so bad if I do say so myself all right and then we'll need another line here and then we also put these same types along the line here t1 right t2 t3 and alpha all right great here's our table now remember what are we assuming about all the types in the program when we compute structural equivalents we are assuming that everything in this is structured equivalent everything is structured equivalent to everything else right so you can see here I'm I'm only drawing the upper half here so that should be very clear about why I'm doing that now then we want to go through and say and I'm going to write alpha up here right so alpha doesn't have an explicit name but the usage of the program is as if alpha is pointer to string right okay so I'm going to look at t1 and t2 so I go through so I've set them all to be true they're all structurally equivalent and I'm going to go through for t1 through all the other ones and say are they structure equivalent and prove it from the rule or ask if any of the rules contradict this statement so I have t1 and I'm going to go to t2 and I'm going to say is a string structure equivalent to a pointer to t1 no that is false right strings are not the same thing as pointers and I just got rid of my beautiful line there we go awesome even better line right so this is false they are not structure equivalent then I go t1 t3 is t1 structure equivalent to t2 string and a pointer to a string no pointers and strings cannot be structure equivalent so that's false t1 and alpha alpha is a pointer to a string t1 is a string nope cannot be structure equivalent right great t2 t3 t2 pointer to t1 t3 pointer to string okay they're both pointers so they're structure equivalent if and only if the types that they're pointing to are structure equivalent so is t1 structure equivalent to a string so we see t1 and we see t1 and string so t1 and string so t string is a basic type and t1 is a basic type string so we use rule one to say yes those are structure equivalent so we say then t2 and t3 are structure equivalent based on what we know let me say t2 and alpha t2 is pointer to t1 alpha is pointer to string we pointers are the same string and t1 so string is a string t1 is a string we compare string and string those are structure equivalent so we say yes t2 and alpha are structure equivalent we look at t3 we say t3 and alpha are they structure equivalent well pointer to string they're they're both pointers they're going to be the same if what they point to is the same so string structure equivalent to string means that yes, these are structurally equivalent. Then I change something in this table, so I have to go through it again. So T1, T2, still not structurally equivalent. T1, T3, still not structurally equivalent. And this should be very clear why, right? This is a string, and these are all pointers. There's no way these can be structurally equivalent. So it's going to be false all here. T2, T3, T1 and string are still the same. So T2 and T3 are still structurally equivalent. T2 and alpha are both point. T2 is a pointer to T1, alpha is a pointer to a string. These are still structurally equivalent, so these are structurally equivalent. T3, alpha, pointer to string, pointer to string, both structurally equivalent, bang. So here I have all of the structural equivalents in the program, right? So now when I say our T2 and T3 structurally equivalent, I don't even need to think about it, right? I've already calculated it. So T2 and T3 are in this table. We look T2, T3, yes, structurally equivalent. We would write yes, and we would get all the points in the world here, and we would be very happy students. And I would be a happy instructor. Okay. Now in this other problem, remember, we're asking, can y equal to w? So the type of y, remember, is alpha and the type which we gave the internal name alpha. The type of w is T3. So we ask, are they structurally equivalent? Alpha, well, T3, alpha, true. So yes, they're structurally equivalent. Also, very good, right, correct answer. Awesome. So just for, so let's ask one more question here. So now that you've computed this, now you can answer pretty much any question about assignment operators or an equivalence, right? So is y equals z allowed under name equivalence? I want you to think about that. So what's the type of z, right? Well, remember, we gave it the name alpha, but it is an anonymous type. z has no name. So z has no name, and y has no name. And if you have two types that have no name, they are not name equivalent. So they are not gonna be name equivalent. Are they internal name equivalent? So we look at y and z. You see, y and z are both pointers of string, but they're the same anonymous type pointer to string. So we know that internally the compiler is gonna have to give them the same type name, alpha. And so we'll say that yes, they are internal name equivalent. Then structural equivalence, well, we can look at our table and we know alpha and alpha is always gonna be structural equivalent. So we say yes, and then we are happy, happy students. Cool, all right, so that is it. We've reached the end of the type system for right now. There'll be obviously homeworks on this to help reinforce the concepts. And on Wednesday, we're gonna talk about something very, very interesting. We're gonna go over Hindley-Milner type inference. So this is a really important, very cool topic that I'm very excited to start talking about on Wednesday. So thank you, and I will see you then.