[MUSIC] So far our story for how to implement this two dimensional grid of operations and variance has been simpler than it sometimes is. And in this segment I want to show you a very interesting complication that arises. And that is when you have an operation that is defined over multiple arguments. Of the kind of data you're defining. I'll show you an example in just a second. And what we'll see in this segment is that functional decomposition handles this pretty well. And an object oriented programming style will either have to abandon LLP or do something much more sophisticated. And I'll show both of those in the next segment. So to show the issue, let's extend our language with two more kinds of data, strings and rational numbers. But much more interestingly, let's extend the addition operation to work over any pair of them. So if you add an Int and an Int, or and Int and a Rational, or a Rational and an Int, it's mathematics. If you have a String and a String, it's String concatenation. And if it's a String and some kind of number, then we convert the number to a String, and then concatenate. That is just the definition of our language. Now if we want to do it this way, we now have another two dimensional grid. It's different then the one we were talking about before, which is to implement addition, there are now nine cases. It's a binary operation, there's a let operand and a right operand, and you need code for every combination of Int, String, and Rational, Int, String, and Rational. So in our eval function, we're going to recursively evaluate the two sub-expressions. We now have three kinds of values in our language. Numbers, strings, and rationals. And addition works on all combinations of values. So we call addition a binary method or binary operation, because it takes two things. Where Int, String and Rational are the possible kinds of things. In general you could take any number, but two is already complicated enough. So, from here I'm really just going to show you this code and see how we do this in a functional program. So, here is my data type definition and you'll see compared to what we have before, I've added String and Rational. Now we know that when we add String and Rational we have to go in and change all of our old operations. So, I've already done that. Let me show you that quickly before moving on to the main point. So, here's the eval operation. Strings are values, so you just return the expression itself. Rationals are values, so you just return the expression itself. Here's the two String operation. A String, let's just return the underlying string. A Rational has a numerator and a denominator, and let's convert it to a String with this code here. For hasZero, neither a string is never a zero, so we return false for a rational, return zero if the numerator is zero. And for no negative constants, remember this was this pre-processing where we got rid of all negative constants. For a String we can just return the expression itself, it has no negative constants. And for Rationals I went ahead and removed any negatives in the numerator or the denominator by adding appropriate negation arguments, but that's really not the focus of this segment. So the focus of this segment is addition. So up here in eval, let's look at this add case. Because, as always, I want to recursively evaluate e1 and recursively evaluate e2. But now, I do not want add to raise an exception if the results are not Ints. That's what malt does. Look at malt. Recursively evaluate if they're both Ints, then build a new Int by multiplying the underlying numbers. Otherwise, raise an exception. But for add, we decided that any combination of Int, Rational or String, which is the only thing that eval ever returns, it always returns one of those three, is possible. And now we want to add those two things together. So I have made this a helper function just to separate it out. And make clearer that we are going to use a function to define this green grid. But you could have done it just as a nested case expression, okay? So, add values up here. Takes in two things. Each of which might be in Int a String or a Rational, and adds them together. So the most natural thing to do is to pattern match on the pair. So we can lay out the nine cases Int, String, Rational, cross Int, String Rational, nine positions in our grid. And just say what to do in each of them. It's a great use of nested pattern matching. The type checker doesn't know that these vs could be some non-value expressio. So I do have this tenth case that raises an exception if either v1 or v2 is not some cognation of Int, String or Rational. Now we just break it into cases. If I have Int i and Int J, then I return Int i + j. That's the case we had even in the previous segment. If I have an Int and a String, then create a new String out of concatenating. Converting i to a String in s, and you just continue down. And Int and a Rational, let's just make the Rational, which is i times k plus j over k, I'm not reducing this or anything like we've done before, but it is a correct rational value for adding. If I have a String and an Int, then go ahead and do the appropriate concatenation. If I have two Strings through the appropriate concatenate. If I have a String and a Rational, then, do convert the Rational to a String in a certain way. And then concatenate us on the front. Here's the interesting one I want to emphasize. If you have a Rational and an Int, I could have absolutely just written here, let me just paste it down actually. This code, right? And it would have worked just fine assuming that I didn't have wild card patterns over here. But when you're pasting code there's often a better way. I could have had a Helper function for this. But, the thing to notice about adding a Rational and an Int, is you get the same result as if you add an Int and a Rational. Right, the same one. It's a commutative operation, to use the math word. And what I did here, which is, I would argue, reasonable style. Is to say, I've already covered this case in the opposite order, so let's just recursively call add values, with v2 and v1. It's quite common, when you have a binary operation, to have lots of commutative cases. And I find this a convenient way to reduce the amount of code duplication or the number of cases you have to explicitly handle. It turns out that in this function, add_values, this is the only case where I'm able to do this. But in other things, even things you might see on your homework, there are more situations where this works. And just to finish up, if you have a Rational and a String, it's a lot like concatenating a String and a Rational. But the order matters, so you can't just treat it as commutative with this previous one. So for a Rational and a String, we do this concatenation, and finally two Rationals, a over b and c over d. This is the basic arithmetic that produces the result of adding them. So, this code, is functional decomposition of nine cases. This is our implementation of this green grid. I find this very natural, I find it far less awkward and cumbersome than the OOP decomposition of this same grid that we'll see in the next segment.