 Anyway, so we are here in lecture six, and we'll talk about programming in prologue. It's an introduction to what we'll see later in the lecture, because why? For several reasons. First, you are now working on PA2, implementing coroutines, and there will be a little bit of a pain to implement, but then on top of them, you can build relatively easily an interpreter in prologue, and then using prologue, we can explain many things that come later in the semester, such as parsing, typing friends, type checking, and even AST rewrite and interpretation itself. So next week, when you work on PA3, you will be implementing prologue, and for that would be good to know how to program in this language. So what we'll do today is a gentle introduction into the language, and then you will get an assigned reading, which is a prologue tutorial with more problems than we can do here. You'll have fun to actually just working through these examples. So normally on Thursdays, we don't do laptops, but today you are welcome. Download SWI prologue, which is the prologue we'll be using, we're not using our own. And of course, we assume that since you are downloading and playing with it, I can ask you to help us with the solution in the exercises that we are working on here. So if you want to download it, just use Google. The usage is, at this query here, you enter the name of the file, brackets, and the dot, and the file loads in, and the content of the file may look like this. It gives the facts, the facts in this case, all named by the programmer, is that there is somebody called John, somebody called Mary, and they are in this likes relationship. It's our agreement here that what we mean by likes is that John likes Mary, rather than the other way around, even though the other way around might be true as well. But we don't see it stated here. So unless it's stated that Mary likes John, unless such a fact is here in our database, or we can deduce it with additional rules, we'll talk about rules today we should assume that Mary does not like John. And after the file is loaded, you can ask queries at this prompt with variables. This query asks, who likes Mary? The answer is John. Then we can type a semicolon, which means is there anybody else who likes Mary? The answer is false, which means we have enumerated all the answers. So this is just a really brief introduction of how you work with prologue programs. You load the files, essentially it initializes the database of rules and facts. Here are our facts here, and now you can ask queries. The queries try to deduce answers from those facts and additional rules. The rules we don't see here yet, but we'll see them here. So let's go slowly through what prologues allows us to do. So on the top you see two simple facts terminated with a dot. They do not need to be on the same line. Then you can ask Boolean queries, which do not contain variables, which means the answers to them are true or false. In this case, given those facts, we have to conclude that John does not like Jim, so the answer is false. You could ask queries with variables. They are called existential, because what we are really asking, does there exist some x such that x likes Jim? So essentially we're asking for a value of x such that that query when that value is substituted into x is true. And in our database, yes, Mary, if you substitute it for x is true, because likes Mary, Jim is in the database. So the terminology we have the so-called ground, yes. Can you speak louder? All right, so that's an excellent question, and I am going to cover it here. So variables always start with capital letters. And the names of objects are so-called atoms. They always start with small letters. So you cannot confuse the name of a person with a variable because atoms always start with lowercase letters. Therefore, we are writing Jim and Mary and John with lowercase letters. Excellent question, though. So to elaborate on this, in the language we can have so-called ground terms, which do not contain any variables. And both facts here and queries can be ground. They can be free of any variables. Then there are these non-ground terms which contain variables. So what are we really saying here? We are saying that everybody likes himself or herself. Because we have a fact in which we have a variable, but it's the same variable in both positions. So when that variable is instantiated for some atom, for some name of a person, presumably, it's the same in both positions. So it would be likes Mary, Mary, likes John, John. And queries, of course, can be non-ground, meaning they have free variables for which we are looking for a substitution with the value. So variables in facts are universally quantified, such as here. We are saying for any x that you can plug in, for any atom that exists in the database, this likes xx is true. And somehow, logically, the variables in the queries are existentially quantified, which means we are asking, does there exist some at least one x? That we can plug in here. So this is universally quantified, and this is existentially quantified. So it starts getting more interesting here soon. But let's ask a question, because this must be clear before we go on. Yes. That's right. Can you ask the question again? Aha. So if there is an exception, if there is an exception, meaning there is somebody who doesn't like himself, then we cannot state this rule. I know you would like to have the freedom to state this rule and say, this is true, except when something is, well, OK. We could do it. Simple answer is, you could not use this rule. Because for some x, it is not true that x likes himself. But we'll get to rules soon. And when we cover the rules, I can then show you how to actually do it, so that we can write it more concisely. So hold that thought for a few slides. So how do we do actually computation in this language? What we saw so far was probably intuitively clear, which is good. But what is really going on? There is two deduction principles going on. One is generalization, and one is instantiation. Generalization is this. You have this fact with specific names, specific atoms. And you ask a query, which is a generalization of this rule, because it has a variable. And in the process of answering the query, we are instantiating x for something by finding a substitution for x, a value that we plug in into x. Instantiation is the opposite, rather than writing that plus of 0 and 1 is 1. So this is another way of writing and writing this rule and so on. We could write these two simpler rules, which in mathematical algebra mean this. So this here is a generalization of these facts. And then the query here does what it takes. This rule here, and instantiates this rule to the query, to look like a query. So in this case, the substitution that we use is x equals 3. We found a rule that was more general. We made it specific to match the query. We could answer yes or true. So here are the rules. The rules are essentially implications, which tell you if x is the father of y, then y, sorry, x is the parent of y. Which rule would typically accompany this rule? If I press space again, what rule will show up? Well, it could. Essentially, you could say that if somebody is a child of somebody, then it is a parent. But you probably wouldn't want to have both of these. Well, it could lead to complications if you add both of these rules into the database, because you have a parent rule between these two and a child rule between the opposite. And you could get into troubles with program getting into cycles. So another attempt that both rules will follow when I press space. Mother should be the other rule, because we may have more specific rules about father and mother. And from those, we are trying to define what it means to be a parent. So when we write these two rules together, are we writing an and relationship or an or relationship between them? So it is clearly an or. So the fact that we have two definitions of the two rule means that one and the other as well is true in the sense that a parent is when it is a father or a mother. It is not the case that the second rule overrides the first one. Both of them are in the database, and they essentially mean or. We could define a grandfather rule. And the grandfather rule says what? Says that x is a grandfather of y. If there exists a z, some z such that x is a parent of z, and z is a parent of y. And it is the same z in both of these positions, so it must be the same person, and hence you get a transitive two step relationship. So we are asking here, so this is an or. This comma here, as you by now figured out, is an and. And here with z, we are asking, does there exist a z such that both parent xz and parent zy are true? Colon dash essentially means if. Yes, essentially it says if the things on the right hand side are true, then the left hand side is true. Essentially, you could say you are establishing new relationships from those that exist on the right hand side. So we can now load the program here. I'll load it using the consult statement, which I believe raises all the statements that are already in the database. But I'll also want to show you the program. Let's maybe not do that. If I ask a grandfather x, y, all right. So here is the file. Make the font a little bit bigger. Zoom all right. So we have just a mother rule and a father rule. Here is the grandfather rule, which you saw before. Now I can ask for all pairs of grandfather and presumably grandchild. And in this case, here is one. I do a semicolon. There is no more. So the enumeration of solution stops. This should be hopefully straightforward. So with that, you can do interesting database programming. So let's try to write a rule brother, which given some family relationship tells you who is the brother of who. Given a brother can find the brother of that person, all of them, or give you all pairs of siblings such that one of them is a brother. So I have two rules. Remember, these commas are ends. I have these two clauses on the right hand side. The right hand side is here. I have two of them so far. They make sense. The brother and the sibling need to share the same parent. There needs to exist a parent that the two share. But that's not enough to get a good brother rule. So what other conjuncts do we add on the right hand side? Well, that could be. You could say that what I have here is perhaps half brother, the other full brother, because they share only one parent. So excellent. But let's assume that half brother is enough to count as brother. They're not equal to themselves, so that you don't come out to be your own brother. Fantastic. And what else, perhaps? It's not a sister, so we want to say that it is a male, the brother, the sibling could be a sister. And the two are different. So exactly, as you said. So what I wrote here is an infix operator, not equal. But it's the same as calling it in this functional notation, just for convenience, the language provides the infix syntax as well. So now we can return to your question of how would I write with these rules that everybody likes everybody if I want to exclude someone. So can you suggest how to write these general rules like likes, x, x, with a list of exceptions? What you see here on the slide should be sufficient for that. So would you see how to do that? I want people to think a little bit, because that will really force you to understand the rules of the language, especially the rules of the rules. So when I have left-hand side and right-hand side, when does the left-hand side hold? I see your hands, but I want people to think about it. So we'll do like, x, x, when it is a fact. It's a fact with variable, so not ground fact, but it's a fact. It always holds for any x. Now, if it is a rule, now I turn it into a rule, this will hold if this right-hand side holds. So what do we put on the right-hand side so that it holds only for the right people? Right, so x is not equal, and maybe Jimmy doesn't like himself, so we'll put it here. So far, so good. Now, what if I want to exclude two people? Let's see, I'll do this. And Mary doesn't like herself either. So am I done? Somebody can see a problem with this. Right, so by adding this extra rule, I have now, again, got into a situation that everybody likes. Everybody why? Because there is an orb between these rules, which means that it is sufficient to find a valid right-hand side for one of these two rules. And if I do, then the left-hand side holds, and we have somebody like somebody. So if I put these rules into the database, and I ask the query, likes Mary, Mary. I ask the question. It's better handwriting, but I ask this question. How would the interpreter answer this question? It would look at the first rule, and it would say, OK, I can take Mary and plug it in for x. So it is able to match the x variable to Mary. So it will take this Mary, and it can plug it here, and it can plug this here. So far, so good. Now it checks the right-hand side and asks, is the right-hand side true? And the right-hand side is true. So Mary likes herself. So what do we do here? How would we fix the problem? All right, so that would be indeed the right solution that we would say likes x, x. And now we do x must be different than Mary, comma, which is n, and x must be different from Jimmy, and so on. And even better would be to say x is not in a list of people who are excluded, but I'll show you the list in a few slides. Right now, we don't know about lists. OK? Yes. Yes, that would be nice. So this is actually a nice solution. So you could say on the right-hand side, I like that. OK, so I'll write it here. You could say like x, x. And there is an operator, not, which would say not. What is the name for somebody who doesn't like himself? What was the name? I'll call it a dislike curve. And then you could have the set of dislike curves, or self-dislike curves. And there would be Mary here. And OK. And so here, what do we do? When we want to check whether somebody likes herself, it must be the case that person is not a dislike curve, is not in the set of facts. So that's a nice solution as well. In fact, this is a sort of pure solution without lists. Really nice. OK? So we will show you how to translate once we know about parsing and more about compilation, sort of simple SQL-like queries into prologue for illustration of how you can take such a database and translate SQL into it. But it's sort of a silly thing to do because prologue can answer queries that are much richer than SQL. In particular, they can ask recursive queries. So do you see the recursion in this prologue query? Which, by the way, you cannot easily express in SQL, not without an outer program, as far as I know. It could be that there is a typo, descendant, the father. Did I get it? It looks like I got it the other way around, right? Descendant, excellent. I think it's, isn't it correct if we are saying, yeah, OK, I should have, and I'll do it right away. Good. My intention was good, but yeah, I screwed it up. So usually, you want the agreement when naming these predicates be that if it's a binary predicate, then y is a descendant of x because y is in the left-hand side position. So this is almost correct. I think this should be, why is he, is this correct now? I could never get those right. x is the father of z, yes, it should be, OK. So x is the father of z, and now I don't know where I am. And I'll put it here. No, all right, this is rocket science. Perhaps this is what I wanted to achieve, to have you really think about it. OK. It has educational purpose, after all. All right, so now let's do something more interesting. And we'll start looking at applications of prologue to the class itself, to programming languages, to compilation and interpretation. So compound terms are terms like these, right? So far, we have seen only, so far, we have only seen something like a, b, and c, these simple facts, a name of a fact, and then some arguments. But we could nest them, OK? And so what you see here is an sd term. If you took 61a, you remember Scheme, and you know that lists are created by consign values into a chain. So this is a potential representation of a list. So what we'll do with it, we can define a rule like car, which given a list will output the head of the list. So I want you to look at that rule and think about what sort of query you would write. A query so that given a list, you extract from the list the head. OK, so the query could look like this. We'll do a car and x for a variable. And we put the list here. For example, this entire list could go in here, right? And the answer will be here, what? It will be x equals a. So let's look at the, so if you pay attention to the first rules, you will realize that I can write it much more compactly. These two are the same. Well, let's see what is going on. Does this one work? This one is OK. All right, so maybe there was a parenthesis missing somewhere. So these rules are equivalent, and now you can see how the variable list is only there to link the right-hand side and the left-hand side. It must have the same value. So you might as well put the entire cons head and tail into the left-hand side of the rule, and then the right-hand side disappears. And now we have something much more compact. Now what would happen if instead of asking this query, which extracts a head from the list, I will ask this query. Let me write it here. So I am now asking, what would you expect the system to return? So it could find all the lists that have this property. How will it instantiate them? In particular, what will it put for the tail? So the answer is correct, by the way, but there are some subtleties. And one of them is, what about the tail thing? So here is our rule. So if I raise a query, car A, X, then X is going to be so-called unified with cons head and tail. And we know that head equals A. Therefore, this is going to be an A. What about the tail? So it could start with an empty list, but look at the rules that we have here. We really have here no definition of lists. Really, the only rule I have here is what? I say that car extracts the left element of the cons. I have no definition of what the list means. In fact, the program here has no clue what is a list. This is the only rule I have here. This is indeed what I have. That's the only rule. Here is the query. So the only thing it can do is create an answer, a value for X, for which we get the correct answer. And what did it put for tail? This is a variable that is not bound to anything, because it has nothing to bound it to. Essentially, the underscore G333 is some internal representation for this tail variable here, for which we had nothing to substitute for. But notice this was the only answer. I did not get a choice to enter a semicolon enumerate more. It could start inventing various values for tail, but it doesn't have any other rules of what could go there. So this is, in a sense, the most general answer. It is telling us that no matter what you put here, this cons a is still a correct answer to this query. It could tell us something more specific, but it would not quite be a correct answer, because indeed anything can go for tail. And still, this is the right answer to this query. Well, which example do you have in mind where we had something different? So let's go to those. For example, the one here at the bottom, right? No, it wouldn't. So let's try it. Let's go here and go to the likes program. I will show you the likes program. So these are just these two rules. And now I can ask likes x, gives me John and nobody else. I can, in fact, ask likes x, y. And it will now enumerate the entire database. And why does it do that? Why doesn't it insert a free variable? Because we do have a set of facts for likes, and it assumes that we are in a closed world that these likes facts are the complete facts that we have. There is nothing more. Like number one, any number is that number. There's a different number of numbers, but not anything to do. Right, so in that case, it could start enumerating them. But if we look at our program here, the one where we do nothing but just ask for a list. So we are now asking the query, what does it have? It says, well, x can be equal to this. And then it tries to look for what it should instantiate for tail. And it has no constraints on tail. It can be anything. In other words, the program is looking for substitutions given the facts that it has that it can plug in for x. And what can it plug in for x to satisfy the query? The most general substitution is, so x can be, you could think of it as these three cons with head on the left, but this head equals a, and tail here. So it finds x to be equal to this tree, and it finds head to be equal to a. So it builds this so-called substitution. It says, head must be equal to a. X must be equal to these three cons with head on the left and tail on the right, both of them are variables. And as soon as they satisfy them, the query answers them. But it finds no constraints on tail. And therefore, the best it can answer is say, I have no constraints for tail. It's a free variable. We'll talk more about the substitution principle later. But if you are bothered why it cannot provide anything specific, look at the rules and try to find whether there are any rules that it could use to instantiate anything specific for tail. It just doesn't have any such rules in the database. OK, well, what answer would you expect? So let's ask this query. Clearly, Adam is not in the database, right? And so now we are asking a question, can we find some substitution, some value for x, such that when we plug it here, this will become a fact that is in the database. And you cannot invent such a value for x, right? Because none of the facts we have in the database have Adam in them. So the answer here will be false. OK, let's see. So let's try it. I have a syntax error somewhere. So it didn't. And I wish I had the better explanation that I do, but I don't. So I'll promise the explanation on Tuesday so that it is all clear. Tuesday is also when we'll talk about the algorithm that the interpreter uses. And that will be also a better context to explain this. So let's leave that till Tuesday. And let's look at what we can build now on top of the compound rules. So what I want to do is now build an interpreter, a very simple interpreter. We'll start by defining an abstract syntax tree. So this here is a tree corresponding to value 3. This one would be plus 3 and 2. And this one is plus 3 minus 2 and 3. So you can think of these functors, these compound statements as nothing but Python tuples that we used to construct our AST. There are three representations of the same representation of three different trees. Now, how do we evaluate it? Let's look at the first rule and see what it does. The first thing you want to try to understand when looking at the rule is how many parameters does it have? And what goes into one parameter, what goes into the next one? So this eval rule has two parameters, right? What would be the first parameter? It's the argument to eval. It's the tree that we want to evaluate. What would be the second parameter? It's the result of the evaluation. In Prolog, these rules are not functions where you put the input into one parameter and the result comes out. You have these variables. And one of them acts as input. One of them acts as output. And sometimes it's reversed in the sense that one of them could become all of a sudden input. We bind it to a value. And the other one is a variable. And we instantiate it to something. OK, so here is the first of three rules that we have here. What does this rule say? That when we reach a leaf that is an integer, the evaluation of it is the value of the leaf. So let's just see whether it works. OK, here is the same thing written slightly different notation. I think I'm doing something wrong with these queries here. So now I can ask eval. And the AST is nothing but one leaf. And I need to give it a variable. And the result goes here. The result is three, all right? So clearly, we want a recursive rule. And here is the rule for the case when the top of the tree that we want to evaluate is a plus with two sub-trees left and right. This is the case when it's a plus. And the entire sub-tree is bound to L. And the other sub-tree is bound to this variable R. Here we evaluate them. And Rv will contain the result of the evaluation. And then this variable here gets the value of the addition. So there is something special I had to do here. I wrote the rule as res is the value computed in the evaluation plus the value computed in the evaluation of right-hand side. Why didn't I say the same thing except with the equal operator? Can you take a guess what the difference would be? So let me first run this query here. Correctly evaluates. Now, if I change this to and reload the program, that's how it's true. This is the result I got. I guess I don't know how to write comments in prologues, hence these errors. So look at the result. So what happens in the case of equals? We get something from the sub-tree and something from the other sub-tree. And rather than performing an addition on them, we'll form another tree out of them with the plus as the symbol in the root. So no evaluation happens, actually. You take a tree, a structure, and another tree, a structure, and form it into a bigger structure. This is what happens when you use equals. The same operator as when we are taking a variable and substituting another value to it. If you do is, then the right-hand side is actually evaluated. So what we need here is indeed a command to the language to evaluate this right-hand side here. Now the evaluation actually took place as we went bottom up. So as an exercise, take a paper and pencil and change this interpreter to a disugaring system. So what comes in is an AST, like you have in the parser, on the output of the parser. And you want to write a few simple rules to change it into a system that produces AST that are disugared. So you want to write the rules that essentially rewrite part of the tree into something simpler. It's enough when you write one rule that convinces you that you would be able to do it. Say rewrite if into a call to ITE. Question. What would happen if we do that? Well, we need to invent a new variable. Let's just call it x. Now we do eval. I understand that this is what you want to do. You want to take the Lv plus Rv and think of it as a new tree, evaluate that. The result would go to variable x. And then x and res will be the same. And the result would appear here, right? So before even running it, what do you think would happen? So let me give you a hint. What we wrote here is the same as this. The plus can be in the infix position, but it's really the same as this particular pattern. So we are now running eval on a tree that has plus in the root, and it has children Lv and Rv. And now you need to look at the rules here on the left-hand side in here and ask, do we have a rule for eval that matches this plus? Not the plus the word, but plus the symbol. So here we have a tree. We formed a fine tree with plus, not the word, but plus with a subtree. And now we ask, what would be an x that the query eval would answer through? And it wouldn't match anything, right? Because none of these rules here have plus in them. Well, what we have really done here is we have rewritten the tree from a node where the operator was called plus the word into a node where it is now has a different name, but it's a different tree, same structure with the different nodes in them. So the sugaring essentially is a process like that, but you do not want to call eval again on it, but perhaps you want to call a rewrite on that, to rewrite the right-hand side further, right? Perhaps the sugar vial into lower-level operations. So this one works. So rewriting, how would we proceed? Any ideas? Let's create a new file, and we'll start from here. And how about if we change, if we do a simple optimization, I want to rewrite. So I would like to rewrite two times e into e plus e because multiplication is expensive. So whenever I find two times e, I would like to optimize it into a cheaper operation. Never mind that it doesn't always. It's more efficient, but let's try to implement this rewriting rule, okay? So I have here something that looks like a promising beginning of a program. I change eval to rewrite so that we don't get confused. Let's see what we need to do here. Let's put this at the end, and let's first deal with this special rule. So we want to do that. And whenever I find a tree with times in the root, two on the left-hand side and some are on the right-hand side, I want to somehow create a result that is, what do I do here? So let's see. This might work. Still need to take care of the base case, which somehow just copies the rest of the tree and only rewrites times. But before we go there and take care of the rule that just does the copying, are we happy with that? Do we want to rewrite R? I think that's the question. Do we need to do something special so that R is recursively rewritten? Okay, so let's try to do this. So rewrite R into rewritten R. I'll call it RR. And does that sound better? Well, do we want to make the equal sign is? What was is the use for? On the right-hand side, we had a structure, essentially a tree, which we wanted the interpreter to evaluate, because we knew it was an arithmetic tree. There was a plus and values on the side. So is essentially force that evaluation. Here we actually want the tree. So I think we are happy with just getting that tree. And res will be just bound to that tree. Or you could say we found a substitution value for that res. Now, we could also write it this way. It would be equivalent, right? We'll say this is the plus. And here what we do is we use the right-hand side to get the value for RR given a value of R. So, so far so good. What do we do about the base case? Could we just say that the rest is rewritten like that? So let me write here a query that we may want. Want to rewrite times int to... See, we would like to obtain four plus four on the output. So how do we take care of that case when we have encountered a node that does not need to be rewritten? I do have a rule here that might look promising. We find a tree. We do not want to rewrite it. So we'll make the tree to be the result. Is that going to do the trick? So is it the question here that we may wanna ask, if I write a rule like this, are we gonna decompose the tree into smaller chunks and process them? In another words, are we going to descend down the tree and rewrite it? Or are we just taking the whole tree and copying it to the output? This rule does not do any recursion, right? So we need to somehow have a rule which says, okay, times maybe L, R, is going to be rewritten into what? We don't change this R. We don't change the times, but we want to put something here and something there. What goes there? So what do we put here? So who likes this answer? Okay, so what is wrong about that? I have now decomposed the tree and got access to the left and right subtrees, but I missed something. Okay, how do I call it recursively? So do I do, now I'm invoking the recursion on that subtree both left and right. So what is still not correct? Okay, so we definitely do need to do the rest, right? We need to do plus in a similar way. So yes, we need that, okay? Let's assume for now that our tree only contains times, but line two is still not done. What do we need to do, fix in line two? Who knows the answer? Okay, all right, so not everybody. So what happens here? So we call it with something like this. By the way, I need to put here the answer. The result will go here. So we'll call the query, rewrite query, and this is our three times in two in four. So is this going to match our query? It is, right? L is going to be in two and R is going to be in four, right? So we'll be, after we call it, this will be in two. R is going to be in four, right? Now we need to construct the tree that will be the result. Now I'm constructing a tree with the same L and same R, which in this particular case is enough. But what if I do here times in two? So now I have a tree that is, contains twice the instance of two times something. So the rewrite should happen twice. So now L is in two and the right is times in two, in eight. So do I need to do something with R? I need to rewrite it, and I'm calling rewrite on R. Where do I get the result of the rewrite? In which variable is it stored? So who can see the error in the program? So that's a beautiful answer. The way I wrote it, which is incorrect on purpose, is essentially making the program work only for those cases where the input into the rewrite and the output is the same. So I'm insisting that the R is the same in both positions. Input must be the same as output. Whereas of course I want to handle cases where some tree comes in and a different rewritten tree comes out. So I need a different variable here and a different variable here to store the result of the rewrite. And in fact, if all goes well, our R should be now plus, plus int eight, eight. This is what we want the result to be. Now LR will be the same int two. That's not rewritten during this time, okay? So are we happier now? In other words, we have decomposed the tree here into times L and R. Well, L and R. Here we are rewriting L into a new tree called LR. And here we are rewriting in R into a new tree called RR. And now on the way back, we use these rewritten subtrees and compose them into a new tree. So we have now a rule four times that is special. This is the simplification rule. And now we have the rule four times that doesn't do anything. It just recursively goes down and rewrites subtrees and then puts them together back the same way, okay? And we'll omit plus. And now let's see whether this actually will work. It would be surprised if it did, but maybe we'll get lucky. Oh, at least no syntactic error. So let's try the rewrite. And the result goes here. And now I'll create a three times into the one that should be rewritten. Okay, so on the output we expect in three plus in three. And we didn't get it, okay? And now let's try to understand what is going on. Okay, so what is missing in the program? No base case. So what base case do we need? So how about this one? So when we run into a leaf that is an ind, we are not rewriting it back, okay? So let's try this. Hey, look at this. And but notice what happened. We have now two rewrites. It has, the program has multiple solutions, right? It has rewritten it the way we wanted, but it also did what? It produced the original. So what about this rewrite here? This is the bigger tree. Now we have four possibilities because there are two possible rewrites and the system produced a tree in which both of them were rewritten, one of them, none of them, right? The first one is the best. So how would we deal with this situation? Okay, so we could do, we could say here that L must be, let me put it right here. L must be different than two, right? So let's see, with a little prayer, maybe it will work. Great, and we've got no other choices. So we have done the right thing, but sometimes, yes, please. Okay, let's see. So let's look at the program here. So we are simply, in this case, making sure that the rules are mutually exclusive, right? That for any time's node, either the first rule fires, which performs the simplification into E plus E into R plus R, or the second rule fires, but not both. Oh, I see. Okay, perfect. You are really saying something else, if I understand correctly, you're saying that we really need also this rule, it should be actually L, okay? And similarly, we should, oh, okay. See, now I'm at a situation where I want it to be, to explain the so-called cut operator, right? Now I want to make sure that the rules are truly mutually exclusive, only one of them fires, so that we don't get so many possibilities. When we only handle two times R, it was easy because we added this L, must be different than two. Now it's harder, now the rules become messy. So there is this so-called cut operator, which, what it does, it says that, as soon as one of the rules from the possibilities fires, the others are automatically excluded. And I believe it will work like this. It's this exclamation mark. And essentially it says, if the execution gets all the way here, then we consider the left-hand side rule, a rewrite, to be done, and it will not then visit the other rules, okay? So here, if we match one times, sorry, two times R, right? We'll get all the way here, and then this rule and that rule will not be visited. So let's see if we get it to work. And it does, right? Now the rules are mutually exclusive, not by the set of conditions that we have attached to them, but operationally by this command, which says, if you match this left-hand side, right, it really matches only this. You get after this operator, which always matches, but once the execution gets here, then this rewrite and that rewrite are automatically excluded. You do not recurse into them. Okay, so instead of this you are saying, we could say maybe the proposal seems to be, say do this. So now I think what you want to support is, and you want to make it very easy in this program to support the rewrites of trees with arbitrary operators, right? So now I should be able to write a tree with power and minus and division and all of it without really adding extra rules. So think about it, would this achieve the purpose? I see, for the base case, which was the int case, okay? So you want to really replace this, okay? The rule where we had int x, int x, you want to replace with the one on the left. For the base case where we do not need to recurse deeper, that would work. We would still need the rules for plus and minus where we need to decompose the tree and recurse deeper, but this would indeed, yes, the rewrite x, x would be a way to catch the base case, excellent, right? If you can have a variable for a rule, so you want to do a, there is a way to do metaprogramming in it, essentially manipulate programs that are prologged programs, but not as simply as attaching these rules to two variables. But you could ask things like, how many solutions does this program have? Does it have one or more? So that's sort of simple metaprogramming where you enumerate solutions you can do. I believe not easily, some prologged systems have extensions for maybe doing some of it, but I'm not familiar how to do it, but look into the manual, okay? So I think that, yes, how about if I answer the question offline at the end of the lecture. So this is just gentle introduction to get you a feel for prologged. There is a reading assigned, which is going through a tutorial. Presumably, you will download the system and play with it, since by reading it, it may really be hard to get the hang of it. And that's it for today.