 A lot of stuff about pattern matching and related things on the type level dot board blog which you can check out later. And this work kind of falls out of it and addresses a common misconception about how pattern matching works, which we'll see in a second. But first of all, I should mention, if you're watching this on a video, this talk is about Scala 2.11.4. This is always changing. This was different in Scala 2.10. The information I'm going to tell you was not true in 2.10. I got to give you a different set of information today. And it will probably not be true in 2.12. In fact, I know that it won't be true in 2.12 because they are definitely changing now this course. Okay, this is just a bit of a review. This is just a basis of the Scala let's call, I would say basics, but I mean, like rules of the Scala language that are not entirely basic. First, we have the idea of a subsumption in our subtyping. So this thing, duck face, less than colon, duck face represents a subtyping relation. It means that x, here, x duck face y means that x is a subtyping y. Now that includes the possibility that x and y are the same type. It's possible in this relation that x and y are the same type, but they may not. As it turns out, it's more useful to talk about subtype or equal rather than strict subtype, which means it's a subtype. That is, it has a duck face relation, but it's not the same type. So that brings us to Seamless and Types. Every time you create a value of a stable type, such as when you declare this val x is string. When you say x colon string down here, what you're also saying is that x dot type is a singleton type, which is the singleton type of the definition x. That singleton type is a subtype of string. That is the x dot type, duck face string. Moreover, it's a strict subtype. X dot type is always a subtype of string where a string is not a subtype of x dot type, all right? Any time you create a value, you're creating an associated singleton type, which is a subtype of whatever inferred or ascribed type that you give to that definition. And here we have a pattern. I just wanna give you the names of the parts of the pattern because I'm going to use them without explaining what they are if they're on. We have the theme here that you're matching is called the selection. And then the things here on the left sides of the rocket ships are the patterns. And we call it equals greater or rocket ship. And I don't think that's the official name probably. And the thing on the right hand side of the rocket ship is the block. So we look at patterns sometimes and say, are these, this is just structural checking. We're just checking whether it's a particular subtype or we're just checking that it has a particular subtype and then calling some methods to get pieces out of it. So it's like, oh, well, you can do that in Java. You just write this kind of thing, right? The problem is that it doesn't have a logical implication that this Java doesn't understand this logical implication that when this instance of check passes, select your instance of box, therefore you know in the body here in this block that the value selector, it's singleton type is a subtype of box. So the confusion that people run into when they try and use types and try to match means it's here. This is the most common one by far. So we have a value, we have an argument X of type T that is to say X dot type is a subtype of T. And we try to match it and do something different based on what the pattern match means. So we say, okay, well in this first case, well I, so X must be an int, therefore the type T must be the type int. And in the second case, well X is a stream, therefore the type T is the type stream as well. And actually, Scalacy gives you the Sarah. And today Sarah, we're gonna see some errors that are good for Scalacy to give you and some errors that Scalacy should not give you and some errors that Scalacy gives you, well Scalacy doesn't complain at all but we really think it really should complain. And here's one that it should absolutely give you. It says that you can't do these things, you can't make these assumptions. Now this one works, this one is sort of similar. But we put that type in a type parameter instead. And we kind of box it up. So we have box do, it has two subtypes int and str2. The thing to note is here, we have, we don't have T, we say that this case is the same case as the stream case. And when we do basically the same thing, we check the end case and we check the stream case, we can do the application that we're trying to do in the last definition. All right, well before we get to why that works, let's talk about why the first function is bad. So we have this value gg, it has a singleton type, gg.type. We take that singleton type and we pass it as the type parameter to double one. There's only one value of a singleton type. And in this case, in the case of gg, it's the stream gg. Orges is not uptype gg.tip. We have made the type system on-sand. In other words, the singleton type of four character streams whose characters are all gs is not a subtype of gg.type. Which is what we claimed here. Incidentally, just to go a bit further, Scala CD seems to put inline ability into the singleton type system just a little bit. So when you make this declaration, we do the doubling here, the unsafe doubling, but it throws away the result and gives us the original one instead. Which is, it's right, it should do this. And it's another reason that you should not try and outsmart the type system when you aren't absolutely sure that what you're doing is the right thing because you usually aren't. All right, if you're not satisfied with using singleton types to demonstrate it, I'll just put it into element form, all right? Here's our goal. We're gonna be talking about goals. We're trying to prove this stream in the string case in the subtype of t. All right, so imagine that singleton types of our argument X is the set of elephants. It's a type, types of sets of values, well, well. And we can say that elephants are gray. Therefore, the set of elephants is a subset of the set of breakings. We can say that the set of elephants is also a subset of animals. And here, animals stand in for streams and breakings stand in for teams. So we prove that this set, X dot type, is a subset of these other two sets. We have proved that those sets are the same set. We're nowhere close to proving that. They could be the same set. They could be overlapping. They could be not overlapping at all. All of these are possible. It just so happens that there are gray animals so that they do overlap in this case. But that's not required by a solid type system. Okay. This is why the second function that we wrote works. Because this error happens. We try to create another kind of box too. We have a string case and we need a case. And we tried to create one that would claim to have both cases here. We said this is both a string and an inbox. In other words, we have both of these subtyping relations are true. But this doesn't compile. You can't create this class. And it gives this error message which I don't understand why this is the error message that it gives. But the outcome is that it gives the rule that we need. And this is the rule. If you remember nothing else from this talk, then you should remember that, you should remember the rule of this rule and how the power matching works. It's the injectivity rule, right? Giving you the freedom to do multiple subclassing classes with different type parameters built in which many people ask for on the scholar user mailing list. Many people have asked for it. Would take away your freedom to do sensible pattern matches. Freedom is slavery. The rule is here. If you have a value y, and you have some trait or abstract class, this is important. It's a trait or an abstract class or a regular class. It's a k of some type parameter r. If you know that y, that y has type k of s and for some s, and you know that y has type k of t for some t. And you also know that the types s and t are equal. And that's a little obscure. So let's put it a couple of different ways. Every time you take the type constructor k, which falls out of the trait or class k and apply to a different type t, you end up with a different type k of t. In other words, we get just the rule that's being enforced by the type error that we saw earlier, which is that if you have two different types, s and t, or string in this example, there are no values of the type of k of s with k of t. Or in our example, there are no values of the type box two of string and box two of interval. It's not allowed. So as a result, you sort of just do the contrapositive and say, well, if we have a value of this type, this kind of intersection type, and we know as a consequence that the type parameters are the same. So I said that it was important for it to be the traits or classes earlier, and this is why. Scholar lets you talk about types and innums like this one as like fully fledged types with a kind of their own. And here's an example. This type constructor is not injective like traits and classes are. So if we say k of anything, really we end up with set of columns because that's what's on the right-hand side. We don't use t anywhere. So if we have this like k of int, then we have set of elephant. Obviously, the type set of elephant is inhabited. Even if there are no elephants, you can still have an empty set. We'll get into how you can get elephants later. So now we can finally go back to double two and talk about why it works. And it takes you a little while to get there, but it will always take you a little while to get there. If you take the existing rules that you understand about how subtyping works and then you add in this new rule of injectivity, which I've just presented, then you should be able to make all sorts of interesting derivations about what your patterns mean when you are matching values. So let's do the derivation here. Why does this work? All right, our goal again as with double one is that string is a subtype of t in the case of stir two of s. All right, we know that x subtype is a subtype of stir two. That's the implication of the pattern. We matched the case stir two, therefore we know it's a stir two. We know that stir two is a subtype of box two of string because that's what's said in the extends clause when we declare stir two. Therefore, because of one and two, by the transitivity rule of the subtype of relation, we have that x as type of box two of string. And yes, you do actually have to go through all that. You don't get quite enough information if you go. We also know because of function declaration that x is a box two of t. That is, it seems like it's subtype of that. That's just in the argument text of addition. You said x is a box two of t, therefore that's the premise. We can combine those using our transitivity rule, sorry, our injectivity rule. We have a value x which is of pipe box two of string and of pipe box two of t. It's a actual trait or class, therefore we know that we can use the injectivity rule to prove that string is equal to t. As a subtype of that string is equal to t, it's just the sub case of string is a subtype of t. So we're done. You can return a string in that case and satisfy the requirement to return a t. All right, and you might think, well that's just weird GADT stuff that you were doing up there. That's like something people don't run into ever. You can use this all the time but if you do pattern matching at all, okay? You just weren't quite aware of it. You absolutely need it and we'll see why in a second. This is like your basic ADT, right? You think, oh, you don't need any fancy injectivity for this to work at all. That's for a word, you need to do well, of course. All right, so basic ADT, all right? List, it's either an empty list, nano or it cons, which is an element and another list, right? Surely you've done this if you've done the ADTs. And both of them, there's no concrete type in here anymore. It's just t being passed through and everything. Okay, so you're supposed to put on this type. We take a list of t and we return an option of t. It's if we got an empty list, then we have none. If we have cons, then we give that head up, right? So the tricky part here is proving this. X has to be a t for this return type to be satisfied. And you need to do all this in order to get there. Absolutely, so let's do it. So we need X to be a t, in other words, X is singleton type is a subtype of t. And that's the X that we got when we applied the cons pattern, where we matched the cons pattern values. All right, so what do we have? We have that whole list, the argument, is if singleton type is subtype of list of t, that's from the function, all right? We also have, from the pattern match, that it is a subtype of cons of q. We don't know it's cons of t yet. All right, we're not quite there. We know that it's cons of q for some unknown type. We're gonna say this is existential. We introduce a new existential type and say that list of type, in addition to being a list of t, it's a subtype of cons of q for some type q. We keep talking about that, all right? We also know that cons of q is a subtype of list of q and that just comes from the definition of cons. It extends, all cons of t extends list of t, therefore cons of q and list of q. So we take these two and we combine them again using the transitivity rule. And now we have that list is of type of list of q, which is the same one we introduced earlier. Now we already knew that list was a type of list of t. So now we apply injectivity and finally, we have that existential we introduced earlier is the same as the t that we took in as type parameter to our head option function. We know from the pattern where we pulled out of the cons that x has type q and from the equality we derive from transitivity and injectivity and all that good stuff, we finally have that x satisfies the return type. In other words, we take the x and put it in sum, we have an option of t. So, you think, well, we can get away with that one. Again, proof by contradiction, we can't get away without using injectivity to make this derivation. So let's look at a counter case. Let's say that we decide to make Scala more pragmatic or practical or whatever. And oh, it's convenient to be able to extend the traits in classes multiple times with different type parameters. So let's go ahead and do it. All right, we've got a cons. It's just like the normal cons. We've also said it lists a class order. That's convenient. So let's take that cons and do some fun stuff with it. All right, so we do head option. We make a list that has a stream high there and I like using the stream high there for these examples. And okay, every tree cons of string, is a list of string because we extended cons and cons extended list and transitive involved a lot. And accordingly, you get an option of stream back which is some of high there. All right, now let's do it again. But we'll put a slightly different description here. All right, so we have our tree cons. It's the same. It's a tree cons of string because we gave it a stream. That's where it incurred. But every tree cons of any type is a subtype of a list of class orders. So we went ahead and we put it up to list of class orders. So the type parameter that we incurred is not string anymore, it's class order. And accordingly, the return if spell allows to file this. Again, I have to emphasize this is hypothetical only because you're not allowed to do this. The return type of this would be in that universe. Option of class order is some high there. So therefore the stream high there happens to also be a class order. I hope this is not a sense. You don't need a value. We put a value in our earlier cases. But you don't need one. Here's a case that doesn't use a string. That is, our data structure doesn't contain it. It's, as they would say, isomorphic to Boolean. It's an, this puts a lie to using simple isomorphisms in a system like Scala where you have parametric volume. Because we can introduce things that are obviously isomorphic to Boolean because they're only two values, but they actually give us more things. They give us more features. In other words, the thing we've been trying to do, which is to like say that this is evidence of what this type parameter is. When we match gimme int, that is evidence that the type parameter t is equal to int by the same kind of derivation that we've already been doing. Incidentally, you do have to kind of know what you're doing here because Scala can't quite figure it out. You can write this without the description t, then it will just infer a return type of any. Also, and the reason for that is kind of this. The type of quality that you work out by doing a pattern match only exists within the block. So here we prove inside this block, t is equal to int. And we prove within this block t is equal to string. You honestly can't do that outside the pattern match, outside the block, on the right hand side of the appropriate profit chip. So t is a phantom. Out-of-type parameters that have no data associated with them is how you do this kind of thing. You can add information about type level expectations that you have at your data layer, right? So it's encoding actual, safe runtime type information. This is the only way to do it. Reflection is not safe. This is safe. Absolutely. We know it is because we did all the logical stuff. All right. So here are some things that don't work. All right, it'd be nice to be able to write equals come equals this way. This is how you basically write leadness type of quality. And if you could do it this way, it's called. All right? It means that if you have a value t of what t and p becomes this quality case, biological implication, the types t and p are the same type. Because we can only instantiate it with e-key ruffle, which just takes one type parameter and passes it in for both cases. All right? And so you should be able to use that to do this match right here and substitute this t for p given a t of t and p. But you can't do that. You get this error. It'd also be able to be really nice, especially for a scholarly stream. Paul, Chisano, and Rudard are complaining about this one all the time. If we're able to introduce new type parameters in some classes here, they don't have to pass it up. Right? So here we have this extra s. And we do here in this list and here in this function. But we don't pass up the s because we're actually not gonna need it. Our purpose of this is to introduce the existential. So we'd say, well, the way that you evaluate this, this is just like a math application. You will list the best and a function from s to t. You never want to get a list of t up, right? And there it is, that's our evaluation. And that compiles, but not for a really good reason, right? The reason that it compiles is because the type that it pulls out that it substitutes for s is any. So this is any to say because any list of any type is any, that's fine. This any does not say. This function takes s. This function doesn't take any. And the return type should be something like this at the end of the bottom. List of s and s to t are some type s, which is much like the existential q introduced earlier. This would be appropriate return type. And in fact, you can use this bug in Scala C to write on safe course. All right, you could do all this for covariant and contrarian parameters too, but it takes way longer and it takes long enough just to explain how to do this for invariant parameters, which are much simpler. You don't want to use covariants and contrarians. Anyway, that's your tariff. They are so complicated. No idea. Wow, we still haven't worked out a correct model to talk about variance and higher times in Scala. It's crazy. Yeah. Anyway, this is safe. This is a safe in 2.11. It's not safe in 2.10. It's finally safe though. Basically what we're saying is we have some value and if it happens to have the type nil dot type, which is the single type that comes out of the definition of nil, which is just the one that's in Scala's collection in the loop, then we, hey, we've got the same as the type pattern in MASH. Give it back to nil dot type because this binding has the type nil dot type, which is what you expect from a type pass. Indeed, if you pass an empty vector to that, it uses the fallback case instead and returns nil, which prints like this open ground, close ground. So that's good. And if we give it nil, it says the single type pattern match. Great. All right, let's do it again for our as pattern instead. Very similar. This binding right here, X, gets the type nil dot type. So, let's give an empty vector in. Oh, well, this will match because obviously empty vector is a list. Wait, what? Yes, wait, what is the correct response to encounter this with a person? All right, as it happens vector, the empty vector is not upside nil dot type, but it just gives X that binding. It just gives it to us. That's kind of because as David McIver noted in 2008, that's what. It happened six years and four days ago, right? Okay, six years and four days ago. Instantly, this is going to be very annoying to fix. It just might take us years and years and years to fix this problem. It is totally unclear what type X you have in this match. So first, let's talk about cons, the one that we introduced earlier. So when you do a match on cons, where you say case cons or whatever, that's of the kind we introduced earlier, as a absolute typo implication. If we successfully match cons, not because we used unapplied, but it's a similar rule. We know that the selector's type is a subtype of cons for some existential. But when we use an as pattern, the as pattern with an associated value pattern, where we're just matching like, is it equal to this value? We're actually using object equals, the one from Java that was invented in like 1995 or whatever it was that Java was created. We do everything we need to know about type systems back then. So I'm sure it's a perfectly sound design. So it could say, oh, is it EQ? Is it Hamilton type patterny equivalent to nil? If it did that, then this would be fine. But that's not why it does it does this. Is it any kind of Gen C? Is it empty? Okay, and it's nil. Every, yeah. Okay, so we can get the type model implication out of that. And this is what, this is what Darmark-Iver meant by, it's in totally unclear, all right? It's this type, this Gen Seq thing, that's the type that the pattern binding X should have when you say X at nil, that is case X at nil. It should be Gen Seq of Q for some existential type Q. So there's nothing in the encoding of nil or anything that says that's what the application is. So there's no way that the pattern matching system can find that out. We're talking about lists in general, it's the same problem. If you have any kind of list and you put it in a value pattern, it just checks whether it's Gen C and it has the same elements, which I'm sure are compared by a method that's just as sound. Oh yeah, yeah, we can trick, we can do any kind of trick we want just by exploiting this single bug in Scala C. This is the case where we make these two empty buffers, right? This is a buffer, right? You can't possibly put any value to the buffer. So there are no functions of type A and B for some unknown types, A and B. We've got this other empty buffer, A's, and you've got a value like this, right? It's identity function, all right? So let's say, okay, Scala C, I've got this A and B's buffer, right? Does it have the type of the A A's buffer? Oh yeah, they're equal, so they must have the same type. So now this A and B has two funnels and one of them says, you know, you put values in a funnel. One of them says, no, you can't put any values because you don't have a value like this. And the other one says, yeah, give me identity function. So we put it in, in the R-friendly new funnel that we introduced here. Drop an identity function in there. Number five, your detection of A and B's for A and B's. And then we go ahead and grab that because we have a function back off the other side. The one that says, hey, there are no values of this type, but I have one, so here you go. And yeah, we then use the back function to tell the scholar component that all streets are elements. All right, let's talk about some long term. This is a design space. This is not a fixed bug. This is where if you want to go further with this, then you should start thinking about what you think this should do. You're taking what you know about deriving implications, the typo loan implications of doing pattern matches. Well, it could just be the selector's type, which in the case of the function we wrote as N, it's not very convenient. It sounds, it's not very convenient. I'm kind of in favor of it anyway though because value patterns are terrible. All right, here's what Ordersky said. Around the same time, around the same time back in 2007, I think. Yes, what this does is that when you either decoded what I'm reading again, I already did that last week. So, yeah, I fully sympathize if you're having trouble decoding what's going on there. It's written in standard ease, just a little bit, but what it's saying is that when we have a value pattern like nil, if we put an at sign before and a pattern binding, then before we say that this case in the pattern match passes, we also ask whether it has the runtime type. It has the static type. That is, you have nil dot type. This is the type pattern associated with that is like colon nil dot type. So whenever we put x path nil, we say, okay, nil equal to it, but according to object equals. Oh, is it? Okay. Well then it also does it pass the type pattern. Nil dot type, which is using eq to reference equality, which is sound. Yeah, that does not work very well because it means whenever you put a as binding in front of a value pattern, that's totally different semantics than when you just have a value pattern. Let's see, you can also do this. This is kind of, I mentioned earlier the problem that was that nothing communicated to the outside world that Gen C was the type of implication of that equals method returning true. And so I just say coded here. This is just like a type of function. We say list is associated with Gen C for an existential type. And we say that like list of the type list that we defined earlier, that's associated with list from existential type. And you can kind of write these and this doesn't really work because implicit lookup doesn't work the way that it really means to you for this work. But theoretically you could create a universe where this sort of works. It wouldn't prevent it from lying to the type system still. But we wrote Gen C over there. And that's correct. It happens to be correct in this case. But there's nothing checking that I put the right thing. Let's see, we could stop using object equals for value patterns and introduce something very similar which says, okay, well, you have to do this runtime type check first before you can do this equals, basically. This is what this is saying. It's very similar to the previous case. It says, well, first check that it's a Gen C and then we can do the equals check. And then if that passes, then we already knew that it was a Gen C. So we can go ahead and give that as binding the stack type Gen C for something existential. I don't know. That'll work either, you know. This is, as Odersky said, this will be more and more likely to take more than this. All right. The thing here to remember is that what are presumably, they'll have to fix it eventually or it'll never get fixed on them. And if it does get fixed, then it will definitely get fixed. So it's definitely an area to pay attention to. So, just a quick review. Pattern matching and types all tied together. You can't really use pattern matching without its type level implications. The essential rule that is missing when you're talking about the rules of subtype relations is injectivity. That's what makes all of this fit together. The uses of this are unknown and that they grow as the type system gets better. When the bugs I showed earlier are fixed, this system will get even more powerful. So the thing to do is to look at the patterns you have and think about if this pattern passes, what does that prove about my types? How can I use this proof in the design of my data structures? Because again, this is a sound way to get type level information into the runtime and it's the only sound way to do that. Incidentally, doing these kinds of things is not a violation of the Scholarzy no type test rule. It's not a type test. The explanation for why that is is too long. I just wrote a blog post on typelevel.org blog about why this is. And again, it would be a whole other talk. So I won't inline that here. You can check that out if you like. Anyway, that's it. Are there any questions people have for you for not to launch a peer-adjectivity rule? Talk about whether or not that's specific to the Scholar or if that's the general statement about types. I think I've still got to go back to the back to the specific statement. It's not specific to Scholar. It generally appears. For a while it wasn't known if this was true in Haskell, for example. Just because the way Haskell is designed, you should be able to write code that actually proves that it's true. And eventually, and other features were introduced in Haskell prove that it's true. That a real type is injected. The Haskell does it a little bit differently. I mentioned that synonyms have kinds, and therefore that's why you have to talk about traits and classes. But synonyms in Haskell don't have kinds. So you can only do this with real types, and every real type is injected as a result in Haskell. And it probably applies to most systems that have any kind of pattern matching. Well, the existing where you can go to this in this way probably has what amounts to an injectivity rule. Whether it's by construction or whatever. I mean, you just can't write this returns an intersection type, right? You kind of need intersection types like with extents, with law, with law, to really have to worry about this. So in a lot of cases, it just says, well, you can't have intersection types, so you can't write this in the first place. Therefore, we have injected. Anything else? Okay, we're good.