 But we could, if I loose everybody, we could just stop at the end of Hindu Miller and just skip the higher end extension part. And if you have been familiar with this topic, please be patient that just think it like us away if it could be a good tutorial for newcomer because I know this topic would be an interesting one for those people interested into the type system. Okay, so some of you might have aware that this t-shirt that with the Hindu Miller type system printed down the t-shirt I call it HMM t-shirt. Maybe you go to visit the HMM store then you will find it and you just can't get at what does it say. And if you zoom in a little bit you will notice that there are a lot of mathematical logic symbol and it's to you it's kind of elvish you can't understand any single bit of it. So I sum up a few of reason that why it is so hard for people that is not from a similar background. That because the language used for describe this whole system is the language used in academy and its purpose is for being sat and no mistake but not easy for comprehension. And for people inside academy they use it frequently so you have to pick up the jargon and intuition to know that. And sometimes you bump into the form that is not the form that you could read out an algorithm. That's not the syntax the regular form that driven by syntax. And even you look at the language that allow tutorial just for a simplified formal language but doesn't look very similar to Hasco and you don't understand why I have to learn this simplified language but not Hasco. And the answer is that the Hasco type system is much much more complicated. But as a foundation that you learn this simplified formal language that you could get the basic and you can eventually get the idea of what the Hasco is doing. And the next question is clear why we need hingling meter types. And it just bummed out at some previous time and you don't understand why we use it now. But you could trace it back to the early 20th century that the whole type theory is developed around that time. And at a certain point that PL community are aware that it could be used for programming language theory and it's quite useful for prevent programmer to get some mistake in programming. So in this talk I'm going to derive trying to start from an intuition example and training of examines for easier comprehension and that starts from a simplified one. So if you're going to a McDonald's supposing that burners make up breath and beef then how could you express a make burner function that we so to define such a function that we have to define a simplified formal language which is like this is I would like to call it because the example is McDonald's I would like to call it system MC which is McDonald's but actually it's lambda calculus then the make function could be expressed like this with a little icon like you could put two things like breath and meat then the output would be a burger then in tax form would be the following. But it's unsafe you could put anything to make a burger. So therefore we have to extend the language a little bit to annotate with type that like with double column burger so that with annotation we could just think it like a label you could tag a label on it. So with the building rules that we the programmer know that claim that burger making is like breath and beef then making a burger is that because the carry heart for us fathers that would call it hamburger making theory then we could type check this program with the rule of the theory that we have two things and return a burger then give it its input. But if you think it backward that it's not only for type checking because the above formula is that all things are given if we miss something that we could like infer back to those missing part like is guessing look at what the value is but then guess is type what is type is. So if we know the rule that makes breath and getting a breath and beef and getting a burger then if we have missing two things then what are these question mark would be. So let's start with a fuzzy idea that if we want to have an inference algorithm what the function would be like it's like it's a function with infer row that row is the type name and because the inference involves some states and we have to put it into a model called PC's type check for means type checking. And so with this function that we know that because we have to push eventually we need to get some type variable it's still empty but we need to do some magic and put the final result into that. So we allocate a reference and calling something else and eventually read that reference back which should be the answer for the inference result then what would be the next. So if you debug some intubation that we first want to reformulate our problem is if you put it like this and you think like it should be two formula come by and solve it like a high school algebra so is this observation correct? The answer is yes. The essence of the typing for this is to walk the whole abstract syntax tree and collect the constraint look like this and solve the missing part as you would do for high school algebra and this problem is called unification problem. So with a tiny example is like the unification problem is an equation left hand side should equal to the right hand side then we fill out t1 with int and t2 with int then both sides should be equivalent. So then with the example previously mentioned that with these two equations given then solving the equation should give us b equal to b and a equal to graph. So with this set of solution we call it unifier in academic terminology so with the problem reformulated then but one thing still missing is that we want the eventual type result with a lot of error probably with some error embedded in science and those things still missing and so it's not in the syntax or the part abstract syntax tree. So we still have to figure out that how to generate the type level grammar syntax thing. So I just name it like a type expression with a grammar generation like a type level generation like this. So with a row which is the function I name it infer row right. If I want to infer it eventually int to int to int then the process will be like row be rewrite by grammar to expand it to tau to tau then another time tau to tau to tau then and substitute by some constant type which could be int then all of them could be int. So with this grammar I call it the row and tau as meta variable for expanding the type expression. The row is the what is the row we name it infer row. So with this grammar then transform it into the Haskell type then would be like this. The type is quite obvious like function is function type is one of type to another type which is algebraic and could be itself or is constant or is type variable which should be the variable we should fill and another is meta variable is merely for expansion that the meta type variable is the row thing. So and we use a trick for type variable it's using a reference. It could be reference to some actual realized tau which could be some tree but we use reference to point to that place so it could be also be empty still not expanded or it could be expanded to something that is point to. Then with all the basic setting done we could start walking the actual AST. So with the example expression then the tree would look like this which is top level application for the beef and another application for the bread and inside is an annotation and underline is lambda calculus I use lambda symbol for that which first layer because only one single argument right so first layer is X then second layer is Y and I use beef as a short term short hand for burger. So with this thing then the inferred row we call another type checking row thing for that the expression is this expression and we run it with inferred interest mode the reference should eventually get the answer. So the first step is application. Then the next step in the TC row for application case is called a unified function with supposing that we have inferred the function body and get its result we wanted to get to something like argument and result but what is that? Actually it's the unification algorithm we mentioned before that this algorithm is for solving unification problem that depends on the problem you solve could be a little modification for management so this function does two things one is for type spreading tension for like extend one single meta variable into some kind of long tree and with arrow embedded and unified trying to make both side of equation at least structurally be equivalent so the unified tau and the function type which is a1 to a2 then this constant would be like this the tau be equal to this function a1 to a2 so with if we write it into program it would be like if it's already a function type then okay then it's just returned it otherwise then we want to first allocate a type variable for the argument which is the right hand side a1 and result type allocate another one for a2 so then with this thing assuming that unified is doing the type checking thing to check both sides of the equation be equivalent then we call this thing to check if it results any error if both sides doesn't unify then it should get some error and abort the program if it's fine then we get the structurally expanded type into rty error of rty and result dy so inside the unified we would do even further for a function that we want both sides we know both sides to look like this but we still don't know what's inside the a and what's inside the b so we have to recursively call itself to unify the argument with the right hand side argument left hand side result to right hand side result which sounds reasonable so that's for the function case and the following another following is that for the meta variable as I said this could be reference to other things so if we're hitting to a case that the unified function that both of them are meta variable then we have to check if the thing they point to are equal or not if they aren't equal then it's not okay if they are equal then it's fine if only one of them are meta variable then we have another case to handle because it's a reference so the reference we just take out the thing inside the reference if it's a thing then okay then we unify the thing inside the reference and the other thing is normal thing which is TY if another meta is happening and handled by this case so if there's something already bound for the meta variable then it's okay if it's still unbound then what's happening we still have to cover that case that means like a row still unbind to something then we have to handle this case is like we already know that TV1 is nothing it's meta variable with nothing inside so we know that TV1 is unbound then we have to check with TV2 which is inside the second meta variable if there's a thing inside then it could still be another meta variable inside so we unify the first thing meta variable to the thing inside the second meta variable and if the second thing is nothing then okay then we just save what's the thing it points to into the meta variable TV1 to point TV1 to what TV1 is located at so here the unbound variable means the meta variable is unbound then another case is that TV2 is like either function or type variable type constant then we have to check if it doesn't actually capture any variable if it doesn't actually capture any of we use the same name variable then if that's okay then we just simply save it to TV1 then the rest of them is trivial case like for constant and constant then we just compare them to see if they are equal right if we are unifying burger and burger then result should be fine and one thing to be extremely careful about is that the type variable here is not the variable we usually refer to when we are developing if their names are different then they are not the same so A1 is A1 but A1 is not A2 because their names are not the same so it's quite confusing that if you are new to this field and the rest of the case is like TV1 to TV1 and both of the arguments are TV1 then we just simply compare it as we do for the type constant and that's above all of the case we have to handle if we fall through them just are able to unify them which means the type checking failed okay so the unification algorithm is the essence of the hindrometer type system and although we are just in this example we are doing for simply type number calculus but in general the whole algorithm the layout is pretty similar just with a few little modifications so getting back to walking the abstract syntax tree we have done the application with the unify function right so with argument it's expected to by the function definition this is the type we inferred and we split it to expected argument and type and result type then at the application case we have to check if the inputs and argument are they the same that's for simply type number calculus case so we just call the check row for checking which is doing the TC row call itself in the type checking mode and the last one statement is just simply save it back to the return value which is a reference we pass in to the TC row we just put the result into the stack type and I call it for each row in this case in the single type number calculus it's trivial but for the later extension for hindrometer type system it's just we just needed this abstract layer for easy comprehension so walking down a level is another application which they do the same thing and the next level is our annotation that we know the annotation is this thing then we still do the checking as we have done for application right we check the body if the body type if it match the annotation and save the annotation back to the thing we returned then the next level is lambda lambda have the input and body and so we first allocate a type variable for the input then with the given symbol is variable right that we put it into an environment that's like this we just save it like thinking like a hash table and we know that this variable name corresponds to the type which is bar TY we allocate then for the later inference in the body then we could use this for inference and after that we write the function which because this is a lambda line it's a function type to the reference where it returned then if it runs in type in the check the above is for the inference mode and if it's in the type checking mode which could be called by the check flow function then we still call unified font to expand the static type and do the further checking for the body in the lambda to see if they are structurally equivalent then for the argument because we have allocated the variable right saving somewhere in the environment could be a hash table then we just look up the environment then save it into the return value which is at a TY this is simple and understandable then another level of lambda which doing the exact thing again but now we have two things in the variable environment which X should be the type of bar TY1 and Y to be the bar TY2 then doing the same thing again that we know that Y type should be in the environment then we get the right hand side's burger type which is trivial it's just saving the burger type into the return value then going back to the annotation because of the calling we know that we get the constraint that the body should be equal to the right hand side looking like this and so we have an annotation here should be looked like this then unify algorithm like unify the left hand side to the right hand side rather to beef and burger then should be split out to some like only the right hand side left with bar TY2 to burger and beef to burger then eventually only three unify union okay then after calling TY it's another constant type which is trivial then going back to the application so we check the check row arc making sure that it's okay for the brand as input then save beef burger to its return value with its row beef burger to burger key Y then another is constant then we basically finish all of that thing this is the tree walking is that okay? okay how can you be so sure that the type of the previous ones like the variables how can you be sure that beef and burger like I wonder if you have something like instant burger which you could put in water to make burger then you basically have two ways to make burger and so how do you know it's bread and beef and not instant burger plus water does that make any sense? you are referring to the rule itself because we have write it down the rule for bread and burger so we are oracle to claim that rule to be true all the thing is based on that that's only one way to make a burger right? yeah yeah that's the when you're programming in class or you write a type that's the thing you claim that should be the theory should be the truth and what you program is proof to prove that theory and the thing you claim okay so we finish all of the algorithm which although I think there should be still a lot of things to figure out if you are new to this but in general you get some general idea for how the thing works right but the problem is the type system is too simple the simply type lambda calculus no generalization in how the burger is made so that's the thing you refer to that we want to generalize the thing to incorporate something else that the burger should not be just meat it could be brown like you go to a mosque burger it could be rice burger so if you're calling this thing then it should work right so the trick of discovered by him and Miller independently is to add a last syntax into the grammar to add these things so that from the syntax you could tell where should the type should be generalized so the first thing is that for the language simplify language itself we have to extend above the thing we defined we have to add some polytypes like the for all in Costco so as a matter of thinking it's no longer just a label for burger it's a blank label that you could put on anything or draw anything on the label and the type would look like this it's a for all AB and become a burger and the syntax with the lab added would look like this we add a lab name turn turn case here which is correspond the name and turn and turn so but notice that for the him and Miller type this isn't the only half allowed to happen at the outer most it's not in the arrow but at the outer most should always have the left side then we could see that we just need a few extend to the previous structure defined then we could get the him and Miller so the grammar to expand the type level expression we add a for all called Sigma Sigma type is the polytype we called and bound is for the for all bound variable is for the for inside this so Sigma would be a lot of form level and a simple row of thing here could be a lot of thing inside so the only thing we added is like for type row here other thing should be the same so with the polytype introduced a concept, a corresponding concept is also introduced called instantiation it's replacing the type variable captured by for all with another fresh type variable or type concept so if you take a look at the example the left hand side instantiates the right hand side so for all A instantiate burger and this A to A instantiate burger to burger and B1 without for should be something although I call it type variable but it's not that variable it's just something empty that we should feel but it's not exactly as the variable as you saw in C or C++ then you could instantiate the blank label with a pen draw it to a burger king label so it's work like this then with the instantiate function we just replace every type variable in the topmost form with a newly instantiate type variable with a unique name so that with that unique name you could apply the same trick as we define a similar type lambda calculus so with this thing if the sigma type is for all case then we just knew a lot of meta variable for various actions and then substitute the body in the type with this newly allocated meta variable with the example like this like for all A1 A2 then we just allocate some tau 1 tau 2 which is also type variable but not for all type variable which still you have to fill in something into tau 1 and tau 2 but they are not the same as for all A1 and A2 then I said previously instantiate row if we just save it right then now we call the instantiation then save the result of what happened instantiated so that should be the instantiating or the burger being right for the various simple case then with another case introduce this generalization which is also a key part for him it's replacing a simple type variable as you saw in the tau 1 tau 2 with for all variable so with a I write poly type here but thinking like replace a normal C plus function and add a template declaration above it so it may get to a C plus a template function it works quite similar to that so thinking like that way then we could extend the infer row further to the version of infer sigma so what infer sigma do is that with knowing that we just first do the infer row for the expression passing then we just add a for all at the head of the type level expression but we have to exclude the single that only happened in the right hand side of type variable but not at the left hand side otherwise we just make the for all capture extra variable because what's happening outside should not to the outside what's happening inside should not for inside so quantify this for all statement just close like a gate just close it for the inside of the body okay so with a few of example with like this suppose that we have variable type variable B1 to A1 then we use a for all to close it like we close it for B and we just assume that because just for demonstration that A1 is done by outer scope so we just skip it and then we just kind of for generalization is wipe out the use and use ratio to wipe out the Burger King label into a black label sticker okay for the generalization case one case to handle is that it's tricky to compare two poly types for the single type lambda calculates is obvious right because it's just only what could happen is only Burger or meat but that like for all A not like that so for comparing Burger to Burger it's simple but for comparing poly types it needs some special trick so saying that the right hand side is the annotated type and the left hand side is the inferred type then this operator means more polymorphic so the left hand side is more polymorphic than the right hand side which is obvious and so a trick for comparing two poly types is that just drop the fall for the right hand side and replace it with unique thing which is previous unseen and see if the left hand side could instantiate the right hand side so then right into the program then the previous check row would become check sigma the check sigma first split the sigma into the fall variable and the variable for the type expression I wrote it for a special name because it's for the essential rank in the latter half of this talk but the thing it does is just like instantiation we write all those fall things into some unique name type variable name so allocate a lot of unique type variable and substitute for the fall so with this thing done then we guess for all type variable and row then we can just the trick we previously demonstrated we could just use the previous defined check row for the simple row head because it's no longer fall it's a little bit difficult to understand it but it's how it works so the rest is to do some checking so it doesn't accidentally capture extra variable but it's just for checking but the essence is for the above with another tree walking then with this type expression I just skip the left hand side because we have already made it we just at the top level it's a lat so when we bump into the lat we use infer sigma but not infer row and the infer sigma would generalize the type expression with and for so this lat we would put it into the environment so in our environment the type is no longer simple case like burger or meat but with something with fall it's poly type so then inside the right hand side the lat body we could use this fall to freely instantiate the new type variable for inside the lat body so when we walk into the right bump into an app we no longer use check row or instantiate row then we use the sigma version so we check if the argument type could subsume the function argument type then using the check sigma which is defined before we check it to compare two poly types to see if one of them could it's more polymorphic than the other so comparing to the simply type is just simply comparing the check simple equal or not then in station sigma instantiate the result infer back to the thing we should return okay so another app which is the same an app is also the same just looking up an app when we looking up the app what storing size is poly type then we instantiate this poly type into something actualize that could be like a bread, burger and back to the thing to return then the constant type which in this case is not that different because it's a constant but in general we just call the instant sigma version to back to the thing we return and for annotation we check if the which is also the same thing we should do for the application checking for the function argument it's the same first do checking then instantiate okay but for the unification argument just nothing needed to be done so the only thing we have all of the things we do is just make extend the role to make it incorporate the sigma the poly type for the form that for a single type comparison that we we can't just naively do it we have to do some trick to make two poly type compare that's the thing you have to think it through so that you could get a handle of how definitely the type works so what's the defect of the millimeter type system which is going I'm going to talk about higher rank so everyone follows that I should keep going or we should stop here that is I lose everybody okay so even with the addition of leds it only tells with the same text we only tell the system to generalize at the place where the lab should happen so but for the case that we have a generalized function passing in which like callback function that we want to call ID to passing in then we can handle that because the lab polymorphism doesn't generalize the argument place but in this case it's happening everywhere so we should make type system to incorporate this case so the forward should pass through the type chapter like make burgers we just want to passing the main function into another function accepting make which is polymorphic function and make two burgers which is red burger and rice burger which is sound reasonable right so then it's so here comes the higher end type system it's more the following is more mathematical simple so it could be hard to understand but trying hard so to solve the problem of lab polymorphism is it's not generalized at the argument so a trade-off is that we don't want to make the type system another big change like we don't want to overhaul the whole type system we just want to make a slight modification and maybe asking programmer to add a little annotation for that it wouldn't cause a lot of burden but in general could make the case pass the type checking so for the simplified language we define is like we add an annotation at the argument we add a firewall for the passing function so this is the previous research to write what has been implemented in Costco the answer we should do okay so we have to do another modification for the type expression which adding annotated lambda okay so which is making the argument with an annotated like burger okay so another thing keep the same and and make the row could derive sigma to sigma which is poly type to poly type that previously is only row to sigma right now it could be tau or sigma to sigma so now the type level expression could be anything that forward could happen not only at the left at the most left place but could be the like after the first arrow or the argument, the dysfunction in the argument okay so for the type level expression it's okay should be the first one of these two examples now are you okay I know I know it's I just make the grammar as an example that's okay that's the same because in fact if you want for all a two to eight ways yeah okay probably I can still get something wrong but okay just keep going and then what's the most difficult thing extending to higher range is like when comparing to two poly types since we allow for to be put at anywhere that but it's the thing it's the case we have to handle but apart from that few things we have to share with the previous layout we defined so the question in the paper the question should be asked is whether the two type expression are equivalent but they have annotated logic derivation in the paper but I didn't write it because it's hard to understand but in general with I call it intuitive but it's not that intuitive but I want to prove it that the second one could be rewrote rewritten to the first one but but not vice versa so for the second one could be first be by remove the four things into some actualized type variable and add the four as the last mode so could be rewrote into the first one which is an issue is that we could the programmer could annotate the type into in the second case but but the system algorithm inferred to be the first one then they are not the same by the mechanics so our goal is that we would like them to be asomorphic okay so then the following should be type chat that is the following two should hold okay which is that left hand side should be more polymorphic than the right hand side right hand side should be also polymorphic than the left hand side then in the paper it define a form called prenext form that is using I think it like a hack you way that to lift the four into the top level that just flows out the four be into the into the latter form so that they could be compared so that then we could see that if one is more generalized than the other okay so the scholar mice we've seen before which should have been extended from only the case one which is doing the flowing out thing to making sure that the type system sorry type expression to make it to the second one okay then it just do all the flowing out making the ball in the right hand side to to be at the most left okay then we have another subject for compared to polytypes then the trick is that we just scholar mice one and do the subs subsumption checking for the subschecking then the annotation six mark two become is supposing that is four to four and the third type is four AB and A to B then the scholar type variable is is is AB and row two is A to B then then we could see that for AB A to B could instantiate to AB then we also need some checking for not accidentally capture free variable okay then so with the subsumption checking we have done the sigma part because we have do the floating out to make the second sigma into the row the normal just with normal type variable then with another function to handle that case with second as row then we do apply the same trick which is we try to see if the first sigma one could instantiate the could be instantiate then do the two row checking then with both of them are row then we could do the unified if the second is function then we have to first made them structurally equivalent then do the function checking and vice versa if the first one is function then the second one and do it in reverse otherwise both of them are row then for the function case then we recursively call itself with the argument of sigma we have to call the subsumption checking in normal and for row result for simple row checking okay and that's that's for further extension the above all is for the extension of the instantiation to make it compare at the argument annotation to make argument annotation at what in first could be compared at the application case but now for the walking through the tree I skipped for the graphing but with those things subsumption row added we just need to add two cases which is make it structurally equivalent then do the checking which which is floating out things and checking if the argument at what passing into the as the lambda could be equivalent in the in the poly type case yeah okay yeah middle type is the foundation of Haskell 98 standard with an introduction of it that you could understand the basic of Haskell or early version of Haskell and higher rank corresponds to Haskell's extension which is rank n types and with this add on then we could handle the case which the previous example we take care and the slides keep a lot of mathematical logical deduction for better combination but if you want to look at anything beyond then you still have to pick up the jargon yeah and thank you and probably get something from this general comments and if you think it's a this implementation right circumstances over the place which makes sense for performance considerations yeah however you can implement the unilaterary type chapter yeah using pure yeah and I am aware of that just but I'm getting somewhere with this but if you do it in pure way you can also find out that it's not necessarily monadic I it's enough if you have an applicative which is a source of fresh hyper-equals because if you have that rest you can build a code that and then you can get an applicative type chapter and it should generalize with the higher higher ranking files so everything will monadic approaches build a code from a theoretical point of view yeah yeah I noticed that there are things discussed about this it's basically like basically it's basically like like it's basically like it's basically like it's basically like it's basically like put into attention yeah and the other thing is that one property the theme is that it is not positional meaning if you have two ! and it can't take the type of first especially second and then just to which means you can get type errors where we have two well-tyed sum expressions. I mean, you have the programs that replace your program with just that sum expression in the type check, and then the other one also in the type check. But if you practice them together, you get a type error from one of them. It doesn't make much sense if you go and set it bigger than your height. Why isn't this not well-tyed? I mean, it's perfectly well-tyed to expression. So there's a whole area of compositional types, which is something where this property that we can just negate the types of sum expression and compile them into the type of expression at least for the task log, we have this property that we get back to the error recipes because the errors will pinpoint that it's the composition of these two, or the combination of these two expressions where they are the sum, from or not from any of the two type sum. Yeah. I would love to hear about that. Well, I need to master some of these, but... I'm not pressing now. It's possible something goes wrong. It's possible something goes wrong is your finger. Is it? I think so. That's a bit sad because I didn't do that machine stuff. I mean, if you don't repeat the error before, I think that the chances that somebody will do new stuff on it are a population of people that will do new stuff on it. It's less. Jason, after that, I was actually with the right from casual conversation with somebody about union texts and intersection texts were supposed to be presented. I learned about union texts when they were in traffic.