 Welcome everyone to the session now, we're really pleased to have the modern Jayatandra with us. He's going to be speaking to us on typescripts type system from solving puzzles to proving facts of program. So we'll be interested to see what you've got to share with us there. We're really thrilled that you could join us today and share your knowledge and experience with us. Yeah, hello everyone. So this is Damodaran. You can call me Damo. So I'm from India and I've been working in so for the past eight years. And so today I'll be talking on typescripts type system. So let's get into the presentation. So the things that we'll be dealing with this, we'll be using typescripts type system to solve puzzles. And this is for the fun part. And for the part of the profit, we'll be looking at how to prove properties of programs, like what all the facts of programs that can be proved using typescripts type system. And before getting into the actual presentation, like this presentation is live. And you can you can also follow through me on the link is given below this one. It's damo.js.org slash fncon 2022. So you can go to this slide and you can use the slide. So without much further ado, let's just dive in. So JavaScript is this omnipresent language that is being used by almost almost like every place every device just runs JavaScript, right. So the problem with JavaScript is that it doesn't have strong types. So in order to solve this program typescript came along and it just gives us types. But the thing about typescript is that the types are present only only in the compile time and they are not present in the runtime. And what happens is once we compile it down to JavaScript, the types gets gets erased completely. So let's just look at a small demo. So what I have over here is the typescript playground and I have two types over here. The first type is the person second type is I'm calling it as alien and both have names. So if I just want to create an instance for this, what I'll have to do is I'll just have to create the object alone. Let's say I gave name and I just type a generic name and this gets accepted. But let me change this to alien again and this also gets accepted. So the thing about typescript type is that these are actually followed by structural subtyping. So once it satisfies the condition that it has a key name and the value is going to be just string, typescript just finds it totally cool and it just moves on. So moving on top of that, what others we have is we have like higher order types like array, promise and these guys are actually generics. So let me just show one more demo on this. If you look at this, I have four types over here and the first thing is going to be an array of string and this is going to be a promise of string. And since array itself is a containment, you just say like string alone. If you look at this thing, this is where I write my typescript code where this is where is the compile.js. And so none of the types get carried along and there is no JavaScript being generated at all. And the moment I add a declare before some cons, I don't even have to initialize because these are also just considered like they are present and you don't have to do anything else. So I can still move on. If you look at this, type x dot, I get all the functions that are present within the array of JavaScript. So it just considers like this x is an array of string. And then similarly, if I give y, you have the dot then method within the promise and if I get it as the value, if I go and check the type of this value, it says string because the type automatically gets inferenced and everything, all the inference and stuff happens within the browser because this particular editor, Monaco editor has this typescripts compiler running within itself. And this gives us this very good intelligence. Let me just show this because since this value is going to be string, if I give a dot again, I just get all the assistance for string types, which is like nearly, it is nearly impossible to do it in plain JavaScript at all. So moving on, let's look at one more tool that is being given by typescript and these are called as assertions. So the type gets erased from the type system once it is compiled to JavaScript. But the problem is that there are certain times where you need to have those conditions to be validated at the runtime. So with the Bayes rule set, assertions can take care of runtime validations too. So let's now look at what happens. We actually do this right often. We write code like document.getElementById given by a id string. So if we go and inspect the type of this particular variable within the editor, it says HTML element or null because this id may be present, may not be present. If it is present, we will get the HTML element or not. But it's like most of the cases, we'll be getting it right. So what actually happens? Like let's say, let's take the condition that it is definitely present. So what you have to do is you just say like as HTML canvas element, that's it. So immediately what this now says is, I'm just telling the compiler that this id is definitely present and it is going to remove the R type of null and I'm going to get it. So this is one type of assertion that you can guide the compiler through without making a mistake or something like that. And followed by this look at this. This is a very interesting example. So the method is below four and I haven't actually implemented the method and the only thing that I've told is it gets a param. I don't care about the param, but it makes an assertion that the param is going to be either one, two, three or four and that's it. So this is what is the function is all about. And then and I also declare another constant, which is going to be a number. Now I call is below four of X one. So since this is this gets called it immediately understands that X one is either going to be one, two, three or four and nothing else. Now this is where it gets even more interesting. The moment I say the context whose type is just literal five, the only value X can take is five alone and nothing else. And this one is now throwing me an error. The moment I hover on top of this, it says the type of one, two, three and four is not assignable to five. So even though the code is actually not run, the type systems inference takes care of these kind of things because we are making sure that this assertions just get taken over through the compiler itself. So this is what is the sum of the key features of type script that really helps in real time. So moving on. So this is what happens in type script because like type script is coming from JavaScript and basically like more than 90% of the code that you write is going to be just pure imperative code. So let's say suppose you want to go full-blown functional program. I mean with type script, it is still possible that with very strong types and there is a library called as FPDS and then there is a very small example program within itself. I'll just show it to you. So what actually happens is the higher order types are being implemented on type on top of this type script type system and you get all the good flavors of Haskell like EQ or semi-group monoid category and etc. So like fungther applicative monoid and these guys are like implemented on top of this FPDS library and there is one example that we can go through. So what actually happens is like since we don't have imperative code, since we don't have like statements of sorts, let's look at the small code over here. So he just writes pipe and then he just does this what is the secret and what is the guess and then this guess it just asks for a particular name. It just pops up an alert or something and then once these values are injected, you can write functional code because right now this these two values get injected in this arguments. Say suppose you want to go full blown pure functional programming using TypeScript, along with the goodness of great abstractions like monoid, monoid, fungther and applicative, which is still possible on top of TypeScript type system thanks to this great FPDS library and if you can look at it, it's just going to be like it's sort of a lispy code on top of this very good abstraction also. So moving on like this is where the fun part actually comes in. So like you can now see how rich the TypeScript type system is and right now when it comes to this type system, we are actually going to use it to solve some small puzzles of sorts. So whenever it comes to solving puzzles, like we just give partial clues, we keep getting the partial clues and sometimes when the clue list is complete, we immediately get the answer out of the clues that are given. So in order to model this, we also have like logical programs like prologue and there is a very small interpreter that is being written on this blog post and you can look at the sample code in prologue. So it says Father, Child, Massimo and Rich. So it says the father is Massimo and the child is going to be Rich and then followed by Eric and Thon and then Thon and Alexandria and this is just kind of a family's complete hierarchy. So given this much of data and if I just ask for father, child of Thon, prologue says yes, Eric is the father and Thon is the child. So let's say you ask something like this. I know the mother but I don't know the child. I don't know who X is. So the moment I click it says there are actually not one but three children for Stephanie. So this is what happens in prologue actually. So you just give prologue the facts and after that you just ask for some generic question and it will be able to answer. And also you can actually write rules like this. It says a parent of X is the parent of Y only if X is the father of Y and X is also the mother of Y. Only if these two conditions get satisfied you can say that X is the parent of Y. Now look at this particular query it says who is the parent of Thon and if you click on ask it says Eric and Stephanie. It just gives you both the father as well as the mother. So this is the structure of prologue and the person who wrote this blog post has implemented this prologue on top of JavaScript and it runs using JavaScript. It just takes the string parses and so on. The usual compiler's lifecycle goes on but we are going to do something different. So what we are trying to do is we are going to encode those facts using structural types in TypeScript followed by we are going to populate the base data and let's look at what the type system can do for us. So let's look at this puzzle solver. As you can see the same three rules I have just modeled them using plain type using TypeScript ridges actually it goes in the opposite direction because the direction I have taken is child to father. This is actually child to father and it goes in the opposite direction and whatever is not present we will have to mark them as unknown Felicia and Kristen and followed by these three rules that were written in prologue are also mapped on to this TypeScript type. And then everything else. This is kind of a boilerplate that I need to map these things within the type level and then followed by I just have some utilities and stuff and just and now let's look at the actual progress. I just ask for who are the parents of Thon and I don't have to run any code or anything. All I have to do is just hold on top of it. I'll be able to see the parents. It's going to be Eric and Stephanie. Followed by if I ask for siblings of Felicia. All I have to do is just hold on top of this and I'll get all the siblings and then for the grandfather it's going to be this one. So all I have so all we have to do is just map through this particular series of keys and we should be able to do we should be able to solve small persons like this but the even more interesting factors we still haven't written a single line of code because these are all living on type of the type level and there is no JavaScript that is being still generated at all and we are still able to run some kind of logic on top of the type level. So moving on top of this, I wrote an article on this speaking about how we can do these kind of small stuff and why it actually happens in this article where you can go and have a look. So what happens actually is we can write even more interesting stuff on top on using this type script type system and one of them is you can model numerals on top of these types and you can also do some very basic math on top of this and one of them is what I'm going to show you next. So before getting into how I have implemented this, I'll just show you a demo on top of this. I've just said this is going to be one, this is going to be two and three and four and so on. So why should I even call this as one? Let me just hold on top of this, it just says Len once and if I hold on top of two it says Len two times and so on. If I look at the six, it says Len six, Len six times within this depth and if you look at this function, look at this type, it says MUL of three and four. So it is just multiplying three and four. So if I hold on this, I should be getting Len up to 12 times. So it comes 12 times actually if you count it, it will come 12 times. So how did I implement these functions at the type level? This feels like we are teaching the compiler about what numbers are on top of types at type level. So we have this inductive definition on numbers where we call this zero as just zero. This is the usual literal type and we have a function called a successor where it is a generic type where it takes in the t and just encapsulates with Len and that's it. So this is all we do. So one is going to be Len of once and then two is going to be Len two times and three is going to be and three times and so on. This is all we need for this successor function. So continuing on top of this, we can even write an addition function like if you give two numbers on type level, it should be able to add them and give you a result. So how does this actually happen is TypeScript actually allows you to write recursive types on top of like type of add x, y you can add on top of this and it just goes on. So now let's try out some cool functions on top of this. Let's say we don't have seven, right? So let's just define one underscore three and underscore four. And if you just hover, you'll get this Len defined seven times. So it will get up to you'll get it up to seven times in this and using this model, I've just defined like addition, multiplication and then subtraction maybe like even division is possible but I just, we can just have it as an exercise or so, so that we can develop on top of this. So moving forward, like, this is like totally it seems like fun part or people sometimes call this as overengineering. So why do you even want to do this? Why do you even want to model numbers on top of types? But it actually seems like these are like very important, very important in the forthcoming slide. So if you look at this, there is something called as this indexed list. So what this indexed list is that we have a value, we have the usual list but this list on the type level carries the length also. So what if it carries the length? So what actually happens is if you look at this up and function carefully, it takes an element of type X and a list of type X followed by length n. Let's say this is a list of number. So if I say list of number and underscore three and so this X2 at compile time we know its length is going to be definitely three. So if I call this append and I get this X1 and compiler now knows like this, I call the append function with a list of length three and X1 is definitely going to be of length four because I've appended this one. So what is the implication on top of this? So I type X1 dot, it immediately gives me next and then I call for the next value. This is the second one and followed by the third one, fourth one. And when I after the fourth, it doesn't give me the next value because the list is now complete. The type system now knows that this list is just of length four and that's it. It doesn't have, so you don't have, you will never go wrong with this when you have something like that. So right now this list is very similar to the cons list that we define in functional languages where it carries a value and followed by the next one, very similar to an usual So also we have this another function that is being declared over here. It's called, it's the concat function where it takes two lists of length m and length n. And since we have this add over here, we can say the return type of this list is going to be of length m plus n since we have this add function. So when I look at this, we have the first list where the length is going to be two and the second list where the length is three and this one is going to be of length five because we have concat at the two list. And following the same exercise, you are still going to get this next five times and it stops over there. So these are like extremely, extremely strong type lists where you will where you can also do this type level gymnastics and you can never go wrong at all. So if you look at the bottom, whatever black box we had set for the previous time, it just gets carried over and then you will still be able to move on top of this. So let's just try moving still more further and right now we are going to look at indexed matrix. So how do we actually program matrix using this? I think it's going to be far, far simpler because since we have list, matrix is just going to be list of list and then the first thing is going to be the type, type of the matrix. Let's say a matrix of numbers and then the second argument is the number of rows and the third one is going to be number of columns. And we can try something like really interesting. Let's look at this function. Let me just zoom out a little bit. If you look at this function, we are just trying to multiply two matrices M1 and then M2 and the M1 matrix is of row R1 and C1 and this one is going to be C1 and R1. And the written type of the matrix should be R1 and C1 because when you multiply two matrices, let's say three cross two and then two cross three, the result is always going to be three cross three. So this is what is the actual matrix multiplications underlying operation. So we just declare again a matrix of three cross two and then two cross three and then we multiply. Now if you look at this, this constant, I ask for the value. I get the next three times and then this is like the iterating through the row. And for the third row, I am getting the third value. By continuing this, you can just get all the nine values. You will just get just the nine values, nothing more, nothing less and then like you won't go wrong with these pointer changes and else forth. So let's look at the opposite case now. Let's say we define matrix of some other row and column. Let's say suppose we have like underscore four or something underscore five. This is a four cross five matrix and immediately this says with a very cryptic error and it goes to a very long depth and says a very cryptic error but the actual error is this type doesn't match at all. Because I think there's a variable, something going wrong over here. Yeah, now we are through. So the actual thing is since this is going to be m one is going to be three cross two definitely the column of this one is should this one should always be ending with two. It shouldn't start with four at all. So this is what is the error that it's throwing. So let's move on. So these are for the some of the fun part maybe like this one actually. So like we have this might be useful this might not be like sometimes be useful in the real life and people again can call this as over engineering or something, but there is like this particular use case where it's really really useful. People always we always need this particular use case of some kind of an automata like let's look at a life cycle of a bug in a very typical software development project lifecycle. You have the bug it will be filed by someone and it will always be open and immediately the next stage of the bug is going to be like it is under fixing stage. And once it is the developer market does I am fixing it and after fixing it he is going to say that this is to be tested and this is the next step from fixing you go to to be tested. And from to be tested you then go to the bug is now closed because whoever filed that bug will test whether after this fix the bug is there or not and then it will be like this bug is now fixed. Now we will just move on but sometimes what happens is that if that bug is going to persist again you just go back to like this has to be opened and it has to go on. So this particular life cycle is yet again model us on top of the type like key value pairs on type level and we again declare a function we call it as move bug and then we write this is kind of a generic function we say x extends key of bug life cycle. So this we TypeScript has this particular operator called us key of which just which just gives you the keys keys alone open fixing to be tested and closed only you get these four values. So the argument is going to be this x only those four values of this particular key and this is and the return type gets again interesting. If you look at that look at the return type it says bug cycle of x. So if it is open the return type will always be fixing and so on. Now let's look at I say I call it as move bug and what is the return type of why it says fixing. So if I call it for again fixing the return type has to be to be tested that is the next step. It says to be tested and so on let's say I just give something else like remove or something definitely it is going to throw error because it is not assignable to this particular type. So like people actually use this kind of an automata kind of thing with client libraries like read eggs and stuff where you will have to move from one state to the another. You get the state it followed by the action and then you move over to the new state. And for these things you can truly do the strong typing on top of this automata or this bug or this life cycle kind of things where you can never go wrong that the compile time at all. So I didn't want to use this word but these are actually dependent types and disguise and types here we cannot like say whether we can prove facts or like prove the I mean like through theorems on top of type script but definitely these come in very handy. So why not stretch this even further why not go even further because in the latest releases of type script they introduced some something called as template literals. Which we are going to look at next. So template literals are very similar to template template literals present at JavaScript but these are present in types type level actually in typescript. So let's look at the demo first. I just say Zoho it's going to be the URL of this one. And I just say whether it is URL of Zoho and I'm just hovering on top of this it says yes it's an URL. Let's say I give something else. I remove this HTTPS or colon on something. It says false it is not an URL so this happens actually at the type level because typescript now has the ability to define something similar like this. Like you can say the string has to begin with HTTPS and these are like capturing parenthesis of your regular expression. This is the first capture and this is the second capture. If it is of this particular pattern you say through otherwise it falls and with this you can say this is an URL or not. And you can go even further let's say you can define a type something or let's get domain and it can say like since it is able to get this value of you can just return it. Now for the same value of this Zoho if I just hover over here you get Zoho instead of true or false. So these things are as powerful as regular expressions and you can still program using them and you can strong type your code. So since we have now come to the lexing part why not do try doing parses. So let's try to do something on top of the parser level. Look at this we are just trying to do the balancing parenthesis. Since balancing parenthesis involves pushdown automata you definitely need a stack. You cannot write a validation for balancing parenthesis using the regular expression because you need to keep hold of the stack and you definitely need either a stack or a variable or something. Or the combination with the regular expression or something so it's definitely not possible using regular expressions. So we write something like this balance and we say we write this particular rule. We ask for whether it starts with the opening parenthesis and ends with the closing parenthesis and I try to infer whatever is in the middle. And whatever is in the middle still has to balance and this is again yet another recursive type. So with this if you go and test for this we are going to get yes this is getting balanced and let me remove only one thing and it's going to give me false. I guess the parenthesis are not matching right now and we can just go even more deep because we can just define something like these are going to be my operators and you can write a balancing grammar also like complete full blown mathematical expression or something. I just have a small demo of this where you have balancing parenthesis followed by operator plus and then it says yes it's true again because this is still going to type check. So this is what is so cool about this we can still go further and people have gone even more further and they have defined things like JSON parser on top of the type level. So if you look at all the demo we don't have any meaningful code in the right side and this is just on top of the type level and this is just very easily possible right now because of this particular tooling. So moving on so why is this even possible the reason for this is generics. Generic types are actually type level functions and then functions that is generics they can compose easily because since they compose easily we can say like these type system of type script is a very small lambda calculus. And the implication is actually very huge because type scripts type system is actually during complete. Since this is going to be during complete and this has been marked as an open issue within the GitHub but we can use it to our advantage because since something is during complete the equivalent in the church language is going to be like this is actually lambda calculus and with lambda calculus we can do even more fun stuff right so we can use it to our advantage. One of the unfortunate thing about proving using typescript is typescript steam calls this to be a non goal actually like having a sound type system or probably correct type system is not a goal of typescript type system but the thing is we don't have to limit ourselves to just go even further and try to prove even more crazy stuff on top of this one maybe like we can have a parser on top of the type level which can do which can parse these types and prove even more proper facts of the program or something similar of sorts. So, and the next part is going to be since we already have these kind of things right so we saw a program something like get an append off a list where you get the element the list of length n and the written value is going to be n plus one. And these programs you don't have to test it at all because even before writing the code you're writing the conditions the preconditions the post condition and so on and these things are like probably correct code and the formal verification takes care of the list. And what this means is we have like a very huge open source code basis, where you can actually scan for back doors of sorts. Let's say we have like very robust software system, like let's say there is an offline tool and it guarantees that I don't give a center request to any, any server of sorts. And who actually gives you the who actually gives certificate that this particular piece of software doesn't give a request at all. Of course there are like programs that can browse through the source code and do, but we don't have the actual proof which can be like we can be very damn short, but with these which is like really, really possible. And the second part is we do this test driven development like test driven development in the sense we write tests first and then followed by we do the actual implementation, but since we, but what we can actually do is by during the development if we are sure of the types, we can just move over and then just write the implementation alone and we can be very damn short during the development time itself like we don't remove these constraints of sorts. And then following this, this is like very, very opinionated because like types seem to be the dualness of imperativeness like imperativeness I have for the deal of imperativeness I mean like you say in imperative language do this in first statement and do this in the next and so on. But when you look at types what we say is just give clues. This is my input and this is my input of some additional clue and this is my output with additional clue which is very similar to what we did in prologue we just give it facts and then we just let the system decide what to do with that. And if we keep giving these constraints, tighter constraints and tighter constraints on top of this at one point of time it is just going to be a puzzle solver. The types themselves act as a puzzle solver and just gives you the correct value. So as you can look at the first beginning of the slides you can look at the type system did not know about numbers. We dotted numbers and then we dotted an indexed list which may use up the numbers and then we designed the matrix which may use up the list and then we can go on which just feels like teaching a toddler about a particular domain or something. So you can look at the compiler as a small toddler or something and you just keep giving it some small clues and then it can figure out like higher level details and so on. And so specifications can just be encoded at the type level and like approvers can take in these specifications and generate and give us a program which we can be very well sure that this is going to be like very secure and stuff. And then moving further, correctness of the software is now completely under the testing team or like going through very strong reviews and so on. But since this is involving some human effort, like most of it can be averted because right now from hospitals to software that actually use, that are getting used to drive flights and so there can never be errors at all. So in places like these where correctness is the major setback, we can make use of these very strong types to avoid this. And moving on, the next bank or the country's constitution could only be programs. Now that we have smart contract systems built on top of blockchain where everything is decentralized, the constitution itself can be written as a piece of code. And someone has to come and verify whether something is written wrong or bad using a panel or something. But with some kind of system like this, a provable system like this, we can just give it to the formal verification and then we can be sure like very sleek errors are not given up of sorts. And another implication is that right now we have lots of bureaucratic machines moving over like someone has to come and verify something. And with these kind of things in place where the constitution or something like that is itself being written as a piece of code and all it needs is like approval from someone. Now these can be again replaced by piece of code and in these kind of cases taking chances is never an option at all because we just need software that we are very sure will work and would never go wrong. So with this I conclude my session. And then for this session the major inspiration was like Philip Waddler's proposition was types talk and then Nadia's refinement types and those two talks are like really, really very engaging and like really, really path breaking. And then of course it is dependent types where I just I was thinking like if I had a system like this where I can use it in my work and in daily life I'll be like very helpful. And then of course Haskell has always been my good language for functional programming and then even though I don't use Haskell in day to day programming but it may be better programmer by learning a lot of these stuff on top of TypeScript. And the usual Haskell's Reddit thread is a good place where like it accepts newbies like me and they just give me videos and books and so on. And then I just want to introduce two books where you can get started and the first one is going to be a type of programming written by Renzo and this program just introduces to functional programming in a very elegant manner and also to theorems and proofs followed by algebra driven design. This is a very new book that has been written and it just talks about this how you write code, how you write code which actually has provable capabilities followed by I just have I just want to thank my friends at Zoho. And then thanks to functional content team like for the opportunity and a very cool cover photo for the presentation because I was thinking like what should I have as a first slide and like functional content just solve the problem for me by giving a very cool photo. So with this I just conclude my presentation and go on for the questions. You think there's a few, few questions there that people have Rick, Rick's got one. Why, why the reverse relationship? Yes, because I'll just go back to the slide. He just thought he's his next comment I think relates to the question to so look at both of them. You C2F and then invert. Okay, yeah, I'll just go to the program. Yeah, okay. So the thing is, whatever direction I go, I need the reverse one also. So let's say I'll have to define this twice. So either C2F or F2C I'll definitely be needing both because we have siblings as well as grandparents, right? That is why I wanted both. So I just defined C2F for me, but you can do it in the other way also very similar to the prologue way, but I just wanted this to work this way, nothing else. I think it's similar in the example. So have I answered Rick's question. Rakesh has got a question as well. Yeah, I think both are same questions then you C2F. Yeah, I think the second question is also same. In the example, some of the strings used like it should be as defined as the type. Defined as the type. How do you make this URL type to work and say and user users and okay fine. Yeah, let me just show over here. Yeah, let's say you define a function and it takes an argument. Okay, this is just returning me the let's do for the get domain. Yeah, this just returns me the Boolean value. We cannot get the thing. So yeah, we cannot be sure. Yes. So you have to do something like this. Yes. So I'll just give you a very small demo over here. What we have is we get the length of the string where we have a generic function and we have written as is going to be a string and we call the get domain thing. So now we know that it is going to get two types actually the first one is going to be string and the second one is going to be Boolean. So we'll have to do an enumeration on top of this and that is why we need to write an if check if it is going to be Boolean then it's not a domain. So we are returning null value and else we are just now asserting that is definitely a string because in this particular place type script is not able to make sure that you is a string and so the moment we assert we start getting the length of the string. So this is how we use this generic types or these kind of template literals in on your functions. I think we just about out of time. So thank you the modern for that little demonstration of how this all works and what the benefits of it are.