 Welcome everyone to the Idris workshop. Hopefully you'll be able to learn a little bit about Idris. If at some point Idris isn't working for you for some reason, I've got this service called Try Idris, which you can go on. If you go to slash compile, there's like just a big editor that you can just paste code into. So just take one of the files and paste it in there. You won't be able to do the last exercise because it's too big. Oh no, you might be able to do it. You might be able to do it. It's just a web server so maybe it will time out. Idris can take a while to compile some things. I'm Brian McKenna and I work at Slamdata. I write pre-script so I've been doing functional programming for a couple years now and I think dependent types are the way forward. It's going to take a while to get there, but I think we'll be doing dependent types in the future sometime. And so dependent types are more expressive than what everyone's probably used to. What you can do is have types depend upon values and so that just allows you to do more things. Why would we want to do more things? One of them is correctness. We have a lot of problems at the moment where if we want to specify a program, it's hard for us to actually put that into types. There's a lot of cases where we either put it into some sort of error type and say it can either be a value that we expect or it can have some error inside of it. Other times we just do awful stuff like just trust me or do casting and things like that. If we have more expressive types, we're able to encode more things about our programs and be able to prove that they're correct. Another one is metaprogramming because it's more expressive. We can do things like and you can have values depending upon types. We can have values that create types and we can do some sort of metaprogramming. I've got an example at the end, hopefully we'll get to it, but we'll be able to have a string literal that is only a valid type if it parses correctly. So we can actually have a string little in our program and our program will only compile if that string contains the right characters. Plus they're a bit more simple. Haskell has a lot of extensions to try and do, things like dependent types. If you have a program language with actual dependent types, it's a lot more simple. Things such as type constructors, we say that type constructors are just functions from types. In Idris, that is literally how they're implemented. They're just functions over types. So there's a lot of things that get unified because we've got dependent types and that makes it a little bit more simple. Now writing dependent type programs can be a little bit more complicated because we have to prove a lot more, but the fundamental theory behind dependent types is usually more simple. So Idris is the tool we'll be using for this workshop. There's a lot of other ones. There's Koch, Agda and a couple others. What makes Idris different is that it's good for writing executables. It's built for writing executables. Things like Koch and Agda have this idea of proof extraction. So you write a proof and then you extract it to a program. Idris is built for writing executables, not second. It's written first for writing executables. And along with that is a lot of tooling. There's a backend for JavaScript so we can compile Idris programs to JavaScript to C code to LLVM. There's also a Java one which compiles to a Maven project and then compiles using Maven. If you know anything about Java's ecosystem, you're probably horrified by that. And there's a lot of tooling around. There's a really good REPL. Agda has a pretty good tooling ecosystem as well, but Idris is getting along as well. Idris is starting to compete. And there's lots of syntactic sugar. Agda, for example, doesn't have do notation. Idris does. And it's got a lot of other things. It's got these things called Indian brackets which Haskell doesn't have. And it's got a couple more extensions that Haskell doesn't have. So it's for making dependent types a little bit more easy to write. We're going to start with talking about equality. With dependent types, we can easily talk about equality, which is really cool. And you can do something like that in Haskell, but we'll see what dependent types gives us. So if everyone can load up the equal Idris file. How many people have got like an editor that's got good Idris support? Only a couple of people? Yeah, that's good. Emacs has got a special mode if you're using the Emacs mode. For the others, if you just load up the Idris program, I'm running this on a Chromebook, so it's not the fastest. But we can load. If we do that, we should see this meta variable stuff. Is that working for everyone? If you're in Emacs, you press Ctrl C, Ctrl L, and they'll load it up and give you something very similar. I don't know what you do for them, I'm sorry. Here we're defining a type called equal. Given an x and y, we can only get an equal if a and a. So we're saying that if the x and y are the same thing, then we can instantiate it and say this is a reflexive. So there's only one constructor for this type, and we can only construct it if the left-hand side and the right-hand side are the same thing. Does it make sense to everyone? Anyone confused by this syntax? We can write this in Haskell. It's a GATT. It's a little bit different because we don't have dependent types, and so you'll see the difference in a minute. But we're just defining a GATT where the only way to create an equal type is if the two things are actually equal. What do you mean? It's a constructor. So defining a constructor that can only, yeah, the type of that constructor is when two things are literally equal. So this will come up when, so Idris has to prove that these two things, when Idris can see that these two things are exactly syntactically equal, we can create this, we can construct this. Does it make sense? So here we're going to create an equal. We're saying that when Boolean and Boolean are equal, we're going to instantiate that. We're going to create an equal. How would we do that? Anyone want to take us? Of Booleans? What we're actually saying is that the type Boolean, we're not saying that the values of Booleans are equal, we're saying that the types, that the type of Boolean is equal to the type of Boolean. Yeah. So we just put in reflexive here, and what we've done is proven that Boolean equal to Boolean. That's all we've done. And we can do this exact thing in Haskell. So Haskell can prove that two types are equal. You just have to enable the GADT extension and you can do exactly that. What gets interesting though is that in Idris we can actually put values in the types. So here we can actually prove that here we're actually proving that true is equal to true. Does it make sense to everyone? This is something that you'd have to do a promotion in Haskell. So you have to promote a Boolean into a type. You have to promote a Boolean value into a type. So we can promote true into a type. We just kind Boolean, but in Idris we don't have to do that. We just take the value and put it up there. Idris allows you to put values into a type. Does it make sense to everyone? Anyone lost already? Okay. So this type equal takes an x and a y. So it's a type constructor, right? Takes an x and a y. Anyone lost so far? So to create a value of that type we use reflexive. Reflexive can only be created if the two types, if x and y, are the same. Because we're using a in the same places. Idris figures out that these two things have to be exactly the same. Does it make sense to everyone now? What's that? Reflexive is a value. Yes, reflexive is a value equal as a type. Equal is a type constructor. It takes two arguments, two other things. Reflexive is a value? Reflexive is a value, exactly. Does it make any more sense to everyone? Do we have the quality and quality notion? Yes. So Haskell has the same thing when you enable GEDTs it has to know a type of quality. So it has this built in thing and it can compare two types as equal. And that's the first example. You can compare Boolean to Boolean and you can do that in Haskell. But it starts getting interesting. You don't have to do promotion here. Idris, we don't say that equal takes two types. We say it takes two things. And then we can compare any two things together. So any two terms we can lift up and compare them as equal. In Idris. Does it make sense? Reflexive is... What do you mean by that? It's the name of... So reflexive is a type constructor and it's type is when two things are equal. It captures a proof that two things are equal. Yes, so my next example. So... How would we do this one then, Daniel? Okay, so this one is actually exactly the same. So what Idris does is it computes the... It actually tries to reduce as much as possible in the types. So it'll actually reduce one plus one to two and then see that two and two are equal. So we're just proving that one plus one is two. As far as possible. Partial? Yeah, it won't reduce at all. So Idris has to know that things are total, meaning that they will reduce to a value before it will even reduce at all in the type. If it knows that it's partial... Idris has a totality checker. And so if you haven't proven that it's total, then it won't reduce anything in the type. It'll just leave that as a whole. I don't know anything about this. You can't prove it. Yep. So Idris... It has to be total, like I said, so it has to reduce to a value. It has to be productive. And of course, like... I don't think you can do unsafe before my own because that's partial. So you can't do any effects or anything like that. It'll just reduce any computation over pure values. Part of that notation is two equal to a reflective. So we're saying that... So two is the proof, like the equals on the right-hand side of the equals is the proof. Oh, okay. And the top is the proposition. So we're proposing that two equals one plus one, and here's the proof. I keep trying to read it like Haskell originally. Well, yes, it is... Yeah. We go a little bit further than Haskell, but yeah. Right. Yes, exactly. We'll just one step. I think Idris will just refuse. I don't know of any other way, yeah. I know Idris just refused for me before. And that's because I've made a mistake and not made things total. Sure. It's a proposition. It's a proposition and the proof, yeah. Proposition and the proof? Well... We're proposing something and we're giving the proof. The proof is reflexive. We're just saying two equals two because it's reflexively equal. They're both the same on both sides. Does that make some sense? Maybe it's still a question, but... So if we do two and one plus two and then we do two equals reflexive, it will fail? Yeah. Let's do that. Why not? So... So it cannot prove that... We can't unify two is three. Yeah. So this is actually a proof. If it compiles, we know that we actually implemented. We actually can prove that two equals two. So anyone that's following along with the wrapper, you can just type in the changes and then reload it. So you can see that before we had four others, but we've done three of those. I should explain how holes work. So see where we've got a question mark? That is what Idris calls a meta variable. I like to call it a hole because it's like a hole in the program. Idris will load it. It just won't have any computation content to it. So it will say you have to eventually give me some sort of proof here. Or you have to fill in this hole. So we've removed three of those holes so far. Or meta variables. And so if we load that up into the into the REPL, we'll see that before we had four other meta variables to solve and now we've only got one other. So we're going through it. So if you're implementing this and you load it up into the REPL, if you get an error, then we've done something wrong. If you see the meta variable count go down, then you've done something good. So there's a couple of things we can do with this. So if we know that A equals B and B equals C, we should know that A equals C, right? Does that make sense to everyone? We should be able to know that. So how would we implement this one? Anyone take a guess? So the answer is that what will happen if we put reflexive in here? So it doesn't know that C and A are the same thing. Why doesn't it know that? So I won't do that. So what we need to do is actually pattern match on the A and the A, B and the B, C it was. That's the only possible way to have something that's equal, right? That's the only way to construct something that's equal. Do we agree on that? So reflexive is the only way and reflexive's type is when A and A so when we pattern match on it it'll unify the A and B and the B and C so it'll know that those are all the same thing. Does it make sense? This is pattern matching that'll refine what we know about the type variables that we put into the proposition. Does that make any sense? Like if we pattern match on that equality and that equality it'll know that A and B must be the same thing, that must be the A and A. Right? So if we do that, then we can say that A, B and yeah, A, B and C are all the same thing so we can just instantiate it with reflexive and we know. Does that make sense? Anyone lost? Someone's lost. Come on. Yes, we'll get there. Bear with me for this and we'll get there. We'll do something a little bit more interesting. We'll prove something cool in a minute. So if A and B are the same if we know A and B are the same then we should know that B and A are the same. Right? Yes. That's a pattern match, yes. The only way to construct an equality though is if it is, if they do match. Does that make sense? There's no way to construct an equality where they don't match because that would not be equal. There's no other constructer so we can only pattern match on one case total. Does that make sense? Yes. We added some other constructer that was focused on everything with fall apart, right? Oh, yeah, yeah, yeah. Like, if I put in like, yeah, then like we can prove anything so you could do anything. So with the following things and fail to compile? Yeah, because they'll be non-total. There's one thing I always forget to put but by default address is partial, allows you to write any functions that are partial. I hate that but if you change the default to total then these should be able to compile. But we could also say like we could do that. You know, spoolings and strings, same thing. It's JavaScript now. It takes to prove that A is equal to B that B is equal to C and produces the proof. Exactly, that's exactly it. I can't remember how to do this one. Anyone want to take a guess? Oh, this should be easy, right? So Idris basically did all the work there. You just like, if you have the, this is why I really recommend having an editor installed. I just pressed a couple hotkeys and I just figured out the proof for me. I pressed CTRL C, CTRL S on the congruent which split it out and gave me a function body. I'll do it in steps. So here, I've just written, okay, I've written this type out. I don't know anything, I don't know how to prove this. I want this. I want Idris to help me create this. So I pressed CTRL C, CTRL S and then still just create a function body and put a hole in because it doesn't know how to prove it yet. So just put in the arguments for me. I pressed CTRL C, CTRL C and figured out that the only thing is reflexive. If there's more than one constructor, it'll break it down. But there's only one constructor and we can use that as reflexive. And then we can do a proof search. So we can say Idris, try and create a value of this type. Which is pretty cool. So I just pressed CTRL C, CTRL A. It says reflexive, so now we've got a proof. So just in a couple hotkeys, we can prove something. We'll get there. We'll get there. There's eMax and Vim. Someone's working on Adam at the moment. I suppose there's a sublime one, but it's not very good. There's a couple as well, but it's pretty tricky. OK. Everyone follow along so far like that. If we have a function from t to t and we know that a and b are the same, then we should be able to run that function with a or run that function with b and get the same thing. It doesn't matter. They're equal, so we should always get the same thing. Right? We should get the same value. Yeah, because it's a pure function and we know that a is equal to b. If we run the function with e to value because it's the same one. It just says the types are the same, right? No, no. Equal, you can say that anything is the same. So up here we're showing that we're saying that these values are the same. We're saying that true is true or that two is two. So we can actually like, yeah. So we're running a function. But yeah, we could also put a type up there and we can say that given type, we can say that we can say for type constructors and we can prove type constructors having the same type and having a type constructor of that type is the same thing. Idris doesn't make a distinction here. What's that? Team means a value. Team means, yeah, anything. It could be a type. Either way. It's polymorphic over both of those. Anyone want to take a guess how we can prove the next one? It's getting a little bit tricky now. What's that? Hotkeys. Anyone want to take a guess before I do that? People might not know this syntax. Z here or Z is 0 and what we're proving is that NAT is a natural number. So numbers from 0 upwards. There's no negatives or anything like that. It's an inductive definition. So we have either 0 or we've got a successor of 0. So we can just keep adding 1. Yes, Z is built in. It's a built-in 0 value for NATs. So we're saying that getting a NAT and then adding 0 to it should get the same natural number. Right? Is that right? So we can write our own. We can do the same thing. We just replace Z with Z. And what we're trying to prove is that adding Z onto something doesn't actually change that number. So yes, we've got to get to a reflexive eventually. How can we get there? Yeah. So there's two cases. So we've got so we'll see that Z, we've got Z here. So N is Z. What do we get? Z. So we can fill in that with reflexive because we know that Z is Z. This one can reduce now because we know what N is. So we can reduce that type. So we know Z is Z. And we can prove that it's reflexive because Z is Z. When we add them together, we get Z back out. That's all the same. So I think I can write this out so we can say that yeah. We can write it like ascribe a type. Which is cool because in Idris, we don't have to have special syntax for that. In Haskell, we have to have special syntax. We can just write a function that takes a type and then a value and then prove that the value has that type. So what we're doing is proving that reflexive here has Z and Z on both sides. So that's when we've instantiated it. Yeah. Yes. There's also Doc is very good. It shows you the thing. Yes. It's Ctrl C, Ctrl D, D. Something like that. How can we do the successor case? How can we prove that successor of K plus 0 equals successor of K? Someone said recurse before. Anyone done any proof theory or anything? Anyone done any proofs before today? What's recursion known in proof theory? Induction. So we're going to use some induction, right? So we're going to make a recursive call. So something's going to have to be a recursive call. How are we going to do that? What's the call going to look like? So we're going to recurse and prove that K is the same as K plus 0 is K. Does it make sense? Oh yeah, that's a pattern matching we're saying that given the successor of a number, so K is another number. K is one less than N, exactly. Yeah. Because we write it out so much. Does it make sense now? Awesome. Yeah, so we know that K plus 0 is K. How can we transform that proof successor of K plus 0 equals the successor of K? The definition's at the top. There's a hint right at the top. So we've got a proof that K plus 0 equals K. So we can do a congruent, say the successor of that. Exactly. Hopefully that works. Yes. There we go. So we're just saying that prove the previous number and then we're just saying 1 to that number. Does it make sense? Anyone lost? Let's see what? That is, we'll look at the monotype to an expression. It's like, have you used Haskell before? So it's like saying 1 has a number of int. In interest, we'd say that the int 1. You don't need it. You could just write it. I just put it in there to show that the type of this reflexive is actually 0 and 0. It's not the type that we see above. We've instantiated the int to be 0 so we can be more specific in this case and it will show. Does it make sense to everyone? So the inductive case makes sense? Congruent over the inductive case? No. It doesn't make sense, does it? Congruent will say that you have to have a function over a t given... Given... I'll make it a little bit more clearer. So a and b are actually t's. So we're saying that given that a and b are equal, we've got a function from t to x, then we can run a and b through that function and they'll be the same thing because they're equal. They're equal to begin with so it's like saying 0 and 0 equal and over that doesn't matter. They're always going to be equal. The x is like the output. So we're saying that the output of that function is the same. So given given a and b which are t's give it to that function, we'll get an x. Those x's are the same. May sense? a and b, yes, exactly. They're equal. So what we're saying is that... So here we're saying f is s a successor function. So we're saying that we've got a proof from the previous number all the way down to 0. And we're just adding 1 each time. We're saying that yes, we've proven that 0 and 0 are equal. We can just add 1 to both sides and we've got proof that they're equal. Congruent does that. Congruent adds 1 and then gives it into the type. We are subtracting 1 by doing the plus 0. So we've got the k we say like, prove it for the previous number and we say add 1 to that proof. So the base case is 0 and we just keep recursing down to 0 and just keep adding 1 back on. Exactly. Add 1 each time. Back up to the number. Yes, that's exactly it. Anyone confused by that? I don't know what you mean by that. I have no idea what you mean by that. Oh yeah, yeah. So I'm going to show a different way to do this. We're going to do a line now instead of the equal word, the equal type constructor, equal, this one is actually built into Idris and Idris lets us do some cool things with it. Some people hate it, some people like it, but I'll show you how to use it anyway. So we'll do the same thing, we'll split and here this one will be called refl instead of reflexive. They just use a short term refl. It's the same thing, same idea that you can only prove that two things are equal but just that syntactic shortcut for the equal. Now what we see, if we load this up, we'll see this meta variable. If we expand that, because we've got the equal sign, Idris is able to do a little bit more and able to say that we need to prove that these two things are equal. That k, successor of k equals successor of k. So we've got a thing that's telling us what we need to prove before we had to just keep it around in our head. That Idris has given us a little bit more information than what we had before. You should load it up and you meta vars. Oh, there we go. You just ask for the type, so call on t and it'll give you that type and say that you must prove that this is the same. Yeah. Then define your own. And we'll show that right now. So we'll do the same thing. Induction equals plus zero, but we've got a prime now, k and keep a hole in there though. If we expand this, what we have is Idris telling us exactly what proof we have by doing the inductive case and what we need to prove. See how this is a little bit more clearer than what we had before? We've got more information. We've got what's in scope and we also have what we need to prove. The underscore two. You can name it anything. Everyone understand this so far? Yeah. Idris has got sugar. It's just sugar. It is a z, it's just Idris will render it as zero sometimes. What's that? You should be able to use zero. It does a weird thing. You should be able to use zero up there I think. No, it's polymorphic, so it's overloaded. It does that from integer thing. That's why I avoid it, because it's a bit hard to explain. Okay. Everyone understand this so far? We know what's in scope. We know what we need to prove. Idris is showing us that. Reflexive is something that I created. Refle is the built-in thing, so we need to say that Refle is the one that is the constructor for that type of equality. The built-in one is a little bit more magic than how in this mode. So if you are using... I'll make this a bit smaller. So if you type in p, you're going to the special mode, and you should be able to just type in everything I type in from now on. So we're going to intros. So intros will give us what's in scope and tell us what we need to prove again, like we had before. Intros will just bring everything into scope for us. What we can say is rewrite. So we've got this whole. We want to rewrite this whole with our proof. Everyone see that? See what's happening now? So previously we had this inductive case, and we know that plus k in zero equals k. So what we're going to do is replace the plus k in zero. Sorry, we're going to replace the k on our right-hand side of the equality with what we have on the left-hand side of the induction. Does that make sense? Everyone see that happening? The k gets replaced with what's on the left-hand side of our induction variable. No, you can write sim and sim is symmetric, so we'll flip it over. Now we know that s of k equals k. Does that make sense to everyone? We've got the inductive case. We rewrite what we're trying to prove with our inductive case and we've got something that's equal. No, you can so if you go p and then the hole that you put in the question marketing, you can go intros rewrite induction oh what did that not work? Proof. Yes, I didn't load. So intros rewrite induction and see how it's the same on both sides now. So we're done with our proof. What we can go is we can write trivial says that there's no more goals we've proved what we needed to prove. Meta n meta n, yeah and meta p to go backwards. So we can write trivial says that no more goals we've done with the proof. We've proven what we wanted to prove. Now we can write qed keep this proof script if we say yes to that emax will add it in so here's the proof for that thing and we've done. We've finished this file. Yeah, the only the hardest thing is knowing when you have to do induction. That's that's literally the hardest part. Yeah, so if you do qed you'll get that thing that you can copy and paste back into your file if you're using the repple. Everyone satisfied with that? Proving that n plus 0 equals n. What's that? Oh yeah, if you type in qed did you type in that? You should see this you can copy and paste this those four lines put that on the end of your file and you're done. Everyone followed along so far? Yeah, so this is this is known as the tactic form of Idris so you can use tactics to prove things. So the tactics that we used were let's go through it again. Yes, we're not using Idris itself we're using like a language that it uses for proof sometimes that you don't have to use you can still use the things that I was showing before like kungaroo and stuff. Exactly Exactly So this is the hole that we need to prove this is what we're trying to prove that that's the type that we're trying to prove. If we try if we type in intros that's the function that we need to prove if we type in intros it'll take the arguments from that function and put it in to scope and say that that the end goal is the thing on the right hand side of the function and these are the things you have in scope. I like it, other people don't Why don't they like it? Why don't they like it? It's kind of magic yeah Plus you're using a second language like put back in You can do that you can't just prove anything here it has to be it has to make sense but there's a lot of things that you can put in here and just say proof searching and try and figure it out for you Spit out the address from the tactic No I don't think so You just do So intros brings everything into scope if we do a rewrite what we do is we've got this proof see how we've got an equal sign already in the scope induction is like saying that k in 0 equals k so we've got that we can rewrite our goal type with that proof intros brings things into scope if you see here how we've got this function we're trying to prove this function if we type in intros it just brings those variables from that function into scope and changes our goal to be yeah what's in the so see here we've got our goal is the function right now our goal is a function it's got a k and it's got this thing does it make sense? that is our goal and if we type in intros it changes our goal to be the right hand side instead of being the whole function that we have to prove we now just have to prove what was on the right hand side of that function and it brings the two arguments into scope so that we can use them does it make sense? so we have this thing we're saying we have this inductive case and we've got this question mark so we've got a hole if we do it up Idris will say you've got a hole do you want to prove it? and then you say yes I want to prove it introduce the variables that are in scope in this where I am in that hole and then you can rewrite the goal with what you have in scope and then you can say trivial QED and save it into the file right yeah type different development so you can see here same goal so we can say trivial QED save it in we're done that's it that's the tactic stuff at the bottom exactly it's formally proven though so why would you read it back into it maybe I don't know everything from that that would be cool though there's things like proof search and so how do you put that into there you can't like if you have a tactic it will do proof search and if you upgrade Idris maybe it will work and maybe it won't it's actually kind of dynamic in that way like yeah do proof search and then once you got something right chuck it in and forget that I did a proof search yeah that's what emacs does as soon as your code generation sees delete the code generation yeah okay let's load up the algebraic file just load that one okay we've already spent an hour so I'm going to try and do this one half an hour and then we'll try and get to something really cool in the last half an hour so we've got these two functions ignore why I did I wrote things like that I just did I do so idempotent is a we'll go down here so idempotent takes a function a to a and then an a and then proves that it creates a type that proves that f of f of a equals f of a so applying the function once is the same as applying it twice definition of idempotent does it make sense to everyone so yes it returns a type and the type is the equality well so yeah no it's the same for the a yes you can see that f of a so we're not putting in there for all a we're just saying for this a we can see here that a not and not equals not true is that right so we're asking so with the first one not not true oh hang on we're not I've skipped the idempotent part here we go ok ignore that so ignore the idempotent for now we'll get back to that in a minute so we've got a function that says not and not of true equals true so doing not twice is the same thing right does it make sense to everyone so doing the function twice is the same as not doing it at all so if we can prove that one using refl because we were asked to prove that true because true now we've got false equals false that one's an easy one to fill in right refl again so we've got the true case we should be able to say for all right we should be able to say for all booleans not and not of that boolean equals the boolean so doing doing the function twice is the same as not doing it at all for both booleans exactly so if we just split this boolean into two into the pattern match so both cases we see that if we were to load this up without the pattern match we were asked to prove that be it as boolean like that not and not of that boolean is the same as the boolean we don't know anything about the boolean so what we need to do is pattern match on it we need more information than just that we have a boolean so if we load that up after we change it to pattern match with two holes we have to prove that false equals false and true equals true which we can do that's pretty easy right well reflexive is the one that we created for our own data type called equal this one is the built-in type we're using the equal sign which is the built-in thing built-in one oh yeah sure the thing is we're just proving that false equals false so anyway that can prove that we're good so yeah we could do that not false but you can see that the type signature of not not false if we reduce the type signature that we put up there then we get false equals false but if we just leave it like refl is probably a better way to do that because it makes more sense that false equals false it's more specific than then saying something like that even though it does reduce down to the same thing it's just for demonstration oh no you don't have to get the type signature not for the false case so the fact that this function twice is the same as not applying at all is known as an involution that's an algebraic property that some functions have so XOR if you do an XOR with anything and you do that twice is the same as not doing it at all so for both Booleans so for true and false doing XOR with true and false and then true and false exactly does it make sense to everyone that XOR is an involution and we can prove that you don't have to trust me we can prove that so we just do the same thing we just split and so now we have to prove that that's not good yeah we do have to split it twice I just thought that type signature would be more useful than what it is basically saying that B and true and true equals B right the delay is for laziness the end is lazy so delay gets put in in the value to like make it lazy lazy true yeah yep so it has to put this delay constructor in to delay things to make it lazy okay so how can we prove that B and true equals B pattern match this is the answer for everything we just pattern match on everything and then we just put in raffle we're good does it make sense that we've just proven that XOR is false and then you give it the argument true it's the same thing as true doing that twice is the same thing as true and the same thing for false and we can just do it for all cases XOR is defined actually up the top yep yep that doing XOR twice is the same as not doing it at all doing XOR twice for the same argument is the same as not doing it at all it's the truth table in every case it reflects it but that's like two semantic steps and I had to get there by parsing your thing is all of this indirection come on that would be awesome you should work on that yeah yeah yeah this is this is not like the most common case usually you've got induction or something more interesting almost as in there yeah yeah confusing they do yep so this is a function that creates a type and the type will be if we just manually do this so we should say that not XOR A of XOR X that is that right so that's all we're really doing before we're just using a function to create that type so that type really is that and so what we're saying is that false if the first case is false so A is false and then XOR false with XOR false and then false equals false right we're just going for every single every everything in the domain we just compute just do every possible way to get there make sense what it's doing is just plugging these values into the type creating the type and then we're just saying we're a refl which is creating a value of that type so every possible way to get to that type we'll get to the right-hand side we're creating a value for that make sense now we're going to prove that something is not true anyone want to guess how we would do that no no that's non-constructive right yeah there's something like absurd exactly so what we say is that there's a void type void has no inhabitants there's no way to create a void does it make sense so if we were able to take something a proof and prove void basically prove anything from from that from that proof then we've proven that something is false it's a pinch let's do that so anyone want to take a guess how to do this pattern match just say every time I ask a question just say pattern match or refl so now what we have to prove is that not not false equals not false so we have to prove that what will this result in false right true so we have to prove that false equals true so how can we create a proof how can we create a proof of equality refl right there is no way to do it so how would we prove that there is no there's no inhabitants of of equal of that type two terms true and false that's what it's reducing to pattern match pattern match come on so if we have refl right Idris will say you're trying to prove that false equals true what are you trying to do so we go to something almost like absurd we tell Idris Idris knows, Idris is like hang on you're trying to prove that true because false right Idris knows that that's impossible so we can say impossible and then it will prove anything it's not possible if Idris tries to unify things that can't unify you can put impossible in there so you say this case is impossible exactly so we do the same thing so now so what we've proven is that if we have an equality if we somehow get an equality that true equals false then we can prove anything we can prove void even by just saying impossible it's a total because we've gotten through all the cases these are the one constructed for equality right refl so we can only say pattern match on refl if it's not refl then it can't be anything else so we can there's no cases for this that actually make sense exactly yeah that sounds oh let's do it so if we don't pattern match we just say proof right and we say getting a proof of this is impossible actually it's a valid case because we're saying like given some proof that we haven't deconstructed it and said the only possible implementation is refl so therefore it's impossible we're saying you've got a proof so it's yeah I have no idea what that means now if you have if you have a negative proof then if you have void you can do anything the other way around but if you have a void yes um adding one to a number is not adept right like adding it twice is not the same as adding ones because it's plus two versus plus one so how will we do this one pattern match and so now we have to do the same thing we have to say that refl is impossible right because zero plus two which is two is not equal to one right actually we'll get we'll get interest to tell us that one is not zero that's impossible right okay how we do this one so we can't do that we can't just do refl because it's trying to create some sort of infinite it's trying to unify with itself because it's got a k in there it can't do that exactly so this is what we're trying to prove which obviously doesn't make sense right three plus n three plus k is not equal to two plus k like it's obviously weird so if we do the same thing that we did before the induction stuff so we say let induction equals suck there you go given hang on do we need to press in that prove what do we pass there tell me it yeah is that right can we just put in proof here that probably won't be the right type right yeah yeah that's a great question I can't remember how to do this one yeah hotkeys aren't helping me today yeah exactly oh let's do that let's just search for type so we've got an s of k equals no what do we have s of k k equals k there we go yeah oh there you go we found the function I needed so what this is so what we just did so if we just if you look down the bottom so we've got this proof that so we have we have this proof prf right that s of s of s of k equals s of s of k so k plus two equals k plus three which is obviously wrong but we need to basically unwrap at one level because we have to prove like because we're doing induction we need to go up one level right so we need to although this is this looks strange but what we're trying to prove is that s of s of k k plus two equals k plus one so we need to go down one level we need to unwrap the s's one level so we can pass it to the inductive one right yeah so we need to like we need to keep going up right so we need to do the k we need to we've got s of k we're trying to prove we need to prove k so we need to unwrap our proof one level and then do the inductive case doesn't make sense exactly yeah so we've got we've got an n we've unwrapped it so we've got successor of k so that's the the inner one here the s of s so this s of k is the n that we got and on that side s of s of k that's the the last the one in brackets is the one that we just is the n that we're trying to prove at the moment that we've split up and then got an s of k so we know that is an s of k we don't we know it's not a zero we know it's an s of k so that's why it's in there and so what we're trying to prove is that we're not trying to prove that there's three we're trying to prove for the k's below it we're trying to prove for k so we've got an unwrapped one level and so what we can do is Idris has got a type search built in it's kind of like who but built into into the rappel if you look over here I think it's just search so we can say that given given that we know that s of n equals s of n then we should be able to prove that n equals n right makes sense n plus 1 equals n plus 1 obviously n must be the same thing so then that'll give us a function it's built into the standard library it's called suckinjective and so you give it you give it the left hand side and the right hand side and you get a proof oh sorry and then you also give it the proof that those are actually equal and you get another proof saying that the numbers below it are really cool as well makes sense so if we just so I've already got a proof that are I've already got PRF which is a proof that the current one the current number is equal so if we do suckinjective k in k was it s of s of k equals s of k that we're trying to prove there we go doesn't make sense to everyone that we've unwrapped it one little no so so do you get that we're doing induction right and we need to prove like so the inductive case means that we have to provide a proof that that's something it's a weird proof like it's sudden that's not true but we already have a proof that something's not true we just need to unwrapper one level prove that the number below it is also not true and we'll just keep going up until we get to the base case and we'll prove obviously it's completely ridiculous doesn't make sense and so suckinjective is a way of unwrapping that proof that we already have one level down and then using the induction to prove that something's wrong so suckinjective is just able to unwrapper one level unwrapper proof that we already have one level you mean the s and s of k we've got too many or we don't have enough oh right right right if we actually had like a specific number yeah we could do suckinjective that four equals four like four and four and we give it raffle and then we get a proof that three equals three doesn't make sense it should be able to figure it out yeah I don't think those are required but yeah well so they are required if we change the definition of suckinjective I think we can make those implicit parameters and therefore we wouldn't have to pass them in explicitly it would be able to figure it out because we already have a proof maybe yeah there we go that's cool the underscore just means if interest can infer the values and interest can infer the values because if you look at the thing that we have to pass we have to pass it in equality and so see how left and right are in that equality yeah so we've got left and right and so interest can figure out what these are interest can figure out what these are because we already passed it in here in the type it unifies left and right with what it already has and then just tracks it in anyone still lost by this we're good yes you can now if you've got a later version you've got let's use tactics ok so we need to so we have a proof that hang on is this loaded oh yeah there you go we don't even need that inductive case do we we've already proven that something is void we've already got a value of void so we don't even need to do induction well I mean we are doing induction but we don't even have to we don't have to rewrite what we're trying to prove we already are trying to prove void so we've got something that's void we've gone all the way down the base case and we're like here's something that's void this is impossible and so then we just return that up so we don't even have to use I mean we are using induction because we've got the suck not identical to using k so we're going one less but we're just proving void we just pass that up exactly we're not rewriting any equality and saying that this thing is void like we just have to return a void we just have exactly so this is where we do the rewrite yeah absolutely I like that I don't know if the I don't I would like that I think so sometimes you can use proof search to get to it and then what Idris will do is say whenever you go to compile this do a proof search so if you compile a different version of Idris you actually get different answers it doesn't actually just it doesn't actually just give you a value to put in there like so you can do things that are more and you can also do things like modify the data type and have the same tactic just work you wouldn't be able to do that with it's doing it at compilation time you don't just have a term that you can just tuck in there all the time there are a little more events than that yeah in the bytecode IBC it must be in there I don't know for sure but I mean that's the only place that makes sense to me have it redo it you have to just do some sort of metaprogramming thing not a moment no it works out more likely, yeah no it's much more likely to break things I'm telling you like the rare case like a the tactic language is more expressive than what an Idris term would be right yeah yeah yeah exactly that's a good example so renaming the constructor for example is a good one I mean they run they run over code so they're not like code itself I guess it's a meta language exactly that's exactly you can so we've got one more last example before I want to get onto something a little bit cooler let's say we've got a function that's both idempitant and an involution can anyone think of an example function that satisfies that property what else no not even that what's that involution is doing something twice is the same as doing it not at all and idempitant is doing it twice the same as doing it once identity right like it's a trick question yes identity is the only function that can do this and like we can all just say that but like let's we should actually prove that so yeah this is what we're going to prove that if we have an idempitant function f of a so we're given like a we were saying for all a we're saying like for all f like if we know that this function is idempitant and we know it's an involution then f of a is a like that's the only way to that's the only function that can be make sense anyone lost by that definition anyone want to guess how we can get there wrong there's no raffle we're going to use tactics for this because it's a bit easier to see how we can get there like tactics here is just going to be the like if we just jump in and just start proving it it'll jump out at us what the solution is yeah yeah yeah so no oh yeah you're getting there so we know that f of f of a equals f of a right so and we're trying to prove that f of a equals a so what we can oh wait it did it gave us f of f of a right equals a so now we know that now what can we do right so now we know f of f of a equals f of a or we could have written that we could have written cm which is a bit nicer because we could just say a equals a it's a bit more clear now what do we do trivial first then what chuck it in something that's idempotent and an involution is identity that's pretty cool right right you don't have exactly that's why people don't like tactics exactly it's just running what we just ran over like it's doing exactly what we just did I mean when you did it interactively it's like oh look back into that exactly and so it just can redo it but yeah that would be cool yeah alright we've got about 23 minutes to do the trickiest one load up bf.idris load up bf.idris I'm going to call this language bf you might know what by another name so what we're actually doing is creating a data type that's indexed by a character so to construct this we have to give it a character and there's only a couple of characters that we can construct it with right makes sense for everyone you can't just do any character it has to be this limited subset of characters that we've defined right here so we can only create a value of that of the bf type if we know that the character is in there doesn't make sense now you can ignore all of this stuff it's awful stuff as well don't read that okay yeah below literals is everyone there everyone following so we're going to create a data type where we've got a list of a's like the data type is going to be indexed by a list of a's and what we're going to do is make a proof that this data type we're going to go through everything in this list of a's and then prove that prove that we have is that right yeah that's right there should probably be a list of char right anyway so we're going to make a witness that everything in our list of I think that should be a list of char everything in our list of char you won't be able to load this but we're going to prove that everything in our list of char has the has the bf type exactly so if we get an empty list of char right then we can prove that every the right I'm just going to comment that out for a second so I can know when I'm going the right direction so Neil is the case where like we don't have any characters so we're just going to we know that we can construct this so we can say that yes the empty list of char is valid yes so if we get a bf of character and we get a list of more characters then we can prove that every it can be for we could generalize this a lot more and there is actually something built into the standard library that does generalize this but I just want to show people how to do this so what we're saying is that given that we have a bf of c so we've got some character that's that's in our algebra of things we can show that actually we don't even need to index that I don't think I think we can just do we don't even need this in the type exactly oh wait no we do need it in the type because we need to prove it for yeah we need to there we go because we can only construct this so what we're doing is we're putting this string into a type right we're splitting the string into a list of characters we're putting it into a type so we've got an every of a list of characters so we've got a value at the type we've got a list of characters at the type so we can only construct this if we can show that there's a bf for everything in the in that character list so for every character we have to be able to show that that character has a bf it makes sense if it doesn't it won't compile so what Idris has is a special keyword called auto which will just say try and figure out a way to construct a value of this type so given a string s try and construct one of these every things for that s so unpack just generate just take the string and generate the list of characters so we need to create an every for every we need to show that there's a bf for everything in that list of characters so given a string split it up into list of characters go through and find a bf for every one of them auto will just whenever you apply that function will just try and find a solution to try and create a bf for every one of those characters if it can't it will just not compile that's just to make it auto we're not going to use it we might be yeah we would use it it's just the proof that everything in the all the list of every character in that list has a bf exactly it's just a name yes so auto p p will be a proof that everything in that string can create a bf makes sense the signature just here's the thing I'm giving a take to the curly braces means that it's implicit so Idris will just combine with auto it will just figure it out it will just look up trying so you don't have to give it anything it will just look it up yeah you know how I was pressing CTRL A before to do a proof search that's exactly what it's going to do and put the result into a value no that io type is just going to we're not going we can't evaluate this you will be able to in a minute but you can't evaluate this in like emacs or anything like that it's like it especially since it's partial if you see partial anywhere you can't Idris won't be able to reduce that you won't be able to use that in a type ok so if we try to implement this function so what we want to do is take a string and we're going to get a bf for everything in that string and we need to do some io so we're going to actually execute a program based on a string literal assuming that we have a proof that everything in it is a bf so we've got an s which is a string which we don't need to care about what we need to care about is actually the proof that everything in that string is a bf so what do we have up here I'm just trying to figure out what we're meant to call so we need to make a we need to create a tape of integers which we can just do as empty type empty ok so now we've got one last thing so we've got this proof that everything in a string has a bf so now we need to create some instructions from that tape so that should be pretty easy I think so we're going to take a everything and create a tape of instructions from our algebra yes so this will be repeat nothing so a tape is an infinite string and if we have an empty if we have if we have an empty if our program is just empty then we just create an infinite type of nothing and so our program will just finish and here we can just use recursion to say that so the tape has so that's the left hand side centre and the right hand side and we can say we can make a tape where we just shift it we're just putting the current we just keep shifting the tape left sorry, the tape to the right because the evaluation doesn't actually care about what type of character the bf algebra is indexed by we just need to put we need to just do something like an existential, a sigma type in dependent types and say that put a hole in there and say that, figure it out so given given that we have a bf of a character then we need to say we need to put the character on the left hand side of our sigma type and the bf on the right hand side so we just need to put an underscore to say figure out what that should be and we'll get an actual instruction so what we're doing is just wrapping up the fact that, no because C is the character it's the current position of the next tape so we're shifting it right does this make sense to everyone that we're like we've got a bf of some character and we're just doing underscore like we don't care about what character that is it's just an existential it makes sense anyway, we should be able to do there we go so with that two instructions thing just takes evidence that everything that we have has a bf every character has a bf yeah, okay so every carries evidence that everything you know a list of characters has a bf and so then we go through and we create instructions instructions are just existentially wrapped it's just this but existentially wrapped and we just go through and we make a tape out of that so it's an infinite tape but we just load up that list of characters into that tape and then we can execute it and we execute it with the bf prime function which does some io stuff and now what we can do is write bf of something and that'll compile to an io thing and so you don't need to do and that's a program as well because that converts into this case the empty case so we've got an empty program and an empty tape what we can do what's going to happen if we do this we can't create a bf Idris won't be able to find a bf for a and so it can't solve that goal so the auto keyword was not able to resolve to something and find a proof that we have a bf of that but if we do that that should hopefully print something so what we've done is created a string literal that only accepts a subset of characters and then first into an algebra we wrap the algebra up a little bit and then pass it off to something that can actually execute that algebra so what we've done is made like a string literal that can go into io actions so we just made a bf interpreter does anyone you should run it because I can't run it I'm on a chromebook that doesn't have f of i or anything like that so I can't run it but I like to see someone else run it because I haven't done it I have no idea if it's going to work so to run it from the rep we can type in yes I will, I'll do that so you should be able to run mine's not supported but if you can do that that would be awesome was anyone able to follow along enough to do that or you could actually get an executable by doing idris that should work as well it'll just generate an executable that can run well let me know if anyone actually gets that to compile I'll push up the code I'd like to see if anyone programs actually work I've proven it correct but I haven't run it so yeah that's a really good question so there's a type called deck and we can give it a proposition yeah so then we can like if we have like p of something if we can show that p exactly exactly so basically just go through the string try and create a bf for everything in that string try and create an every for that type and you've got a decidable instance for everything in that type so either it's true or it's false so you've got to prove that something's true or something is false dispatch on that yes that's a problem do you see all the partial keywords thank you