Primitives in patterns - Providing foundational changes to next-gen Java types
About this talk
This talk discusses foundational changes to Java through the introduction of primitives and patterns in the context of Java Development Tools and the Eclipse Compiler for Java. The speaker, Manoj Manujan Palat, provides insights into how the new features aim to enhance the usability of primitive types in pattern matching and switching constructs. He explains the role of preview features in Java, emphasizing the significance of the Java Language Specification and how changes have been made to allow primitives in all pattern contexts. The speaker also elaborates on the differences between existing Java language features and the newly introduced ones, including primitive patterns, their implementation, and their implications for developers. The session concludes with a demonstration of real code examples showcasing these advancements and their relevance to modern programming practices.
Full transcript
[music] Good evening and thanks for coming to attend this talk. Primitives and patterns providing foundational changes to next generation Java types. First disclaimer, next generation is not AI here. Okay, it's more about going back to primitives. So that's why the primitives are there. very happy to see some of the very old faces right from the EclipseCon days. Uh so for those you have seen my presentation the
next few slides are just very small changes you would have seen so many times if you remember I I'm uh Manoj Manujan Palat I work with IBM I'm a committerer in Eclipse Java development tools also uh one of the project leads in the Eclipse JD uh so we'll come what Eclipse JD is how many of you have used Eclipse IDE Oh wow that's really nice you know
so uh you see something like this I'm not sure whether some of you would use the dark theme but if you use the dark theme you can see see this particular stuff which is of course uh not sure I'm preaching to choir but everyone knows this it is based on uh the plug-in architecture there is an eclipse SDK and we have the platform and we have the
different plugins the PD which glues everything together and we have the J JD or the Java development tools so that's the part which I primarily uh work on not I alone a lot of same so what what exactly we do so if you are if your completion or debugger a formatter code refactoring quick assist you name it on Java that is given by the uh Java development
tools we have a disassembler too and Uh we do release every 3 months as a Eclipse quarterly release. Anyway uh that what exactly does JDT contain? they provide these features based on three pillars of JD what we call that is Java model DOM and Java search. uh these details are irrelevant for this particular talk. But uh the one which is the the foundation on which the Java
development tools rests upon these pillars rests upon that is a bottommost layer that is a Eclipse Java source to bite compiler and it is not Java C and every time get to ask so how do you use Java C no it is not Java C it's aon compiler Eclipse compiler for Java or ECJ so if you look at the release page you see the ECJ release separately
Okay. So today's talk is mostly concentrated on on this and how many of you use VS code? Okay. So that's not a disjoint set anyway. Right. But uh have you installed the language support for Java by Red Hat? Oh, you don't use it for Java? Yes. Okay. the silence I take it as uh some use it. So if you are using it, you are again indirectly using
the my Eclipse JD because uh Redat internally uses Eclipse JD. If you can see the that shaded portion uh that is to you know reinstill confidence that we are still important. Anyway that is so what exactly does a compiler do? It compiles it takes a X on Java class X Java file and it compiles to into X.class. If it compiles, if it aderes to Java language specification,
if it does not align to Java language specification, it throws out throws out an error. So that's all it does. I mean everyone everything we we all know that. So what does Java language specification give? Know different features like records, pattern, pattern switches. Uh I'm not going into those details that will be taken by the next speaker Brigit. She'll go into details of this patents. What we
are interested in in this little uh top primitives in patents. It's a preview feature. Uh as everyone know what a preview feature is. Yeah. For those of you who don't know, uh it is just that you know standard features will remain and preview features would be like no guarantee that it'll be carried over or whether it will be cancelled or whether it will be modified but it
goes through multiple multiple versions before it becomes standard or they decide to throw it away. At this point primitives uh primitives is in the fourth uh preview fifth preview is going to come and this is Jeff 530. Java 26 was released as we all know a few weeks back. So I'll be talking from the Java 26 perspective. For Java 27 this this continues uh un changed uh
at least as of now the Jeff is Jeff 532 is the submitted stage. So yeah, so we when we talk about uh compiler and Java language specification and the changes sometimes these changes see when we we the the compiler generates a class file and that class file should adhere to the Java virtual machine specification and some some features would have changes both in the language specification as
well as some changes in the uh Java virtual machine specification. The the prior to lambda Java 7 we had the invoke dynamic instruction that had come in the virtual machine but the lambda came in in Java 8 and we had the changes there. We had records and uh records was primarily a langu uh language specification feature but it had one small uh addition in the virtual machine
specification. It we had to emit code saying that it's a record. uh so we'll we'll get into what exactly penalty types is and uh whether we have delta in language specification or virtual machine specification so uh this slide is dated so the flexible constructor is already a standard feature primitive patterns is still ongoing it's still it's a prim is a preview feature so what exactly is this
primitive feature primitives and uh two things and we are enhancing the primitive pattern matching the we enhancing the pattern matching by allowing primitive types in all pattern contexts. We'll see code examples for this and we'll extend the instance of not the pattern matching instance of the our classic instance of and the classic switch to work with primitives. We'll also get to know why these uh fellows do
not work with uh primitives soon. Yeah, this is taken shamelessly from verb batting from the jet. It is like know enable uniform data exploreration uh allow pattern matching to use primary provide easy to use constructs that eliminate the risk of losing information due to unsafe cast. I mean very very complicated statement but still uh uh finally what what does it amount to for the programmer? It uh
amounts to removing multiple restrictions pertaining to primitive types that impose friction between using pattern matching instance of and switch. Pattern matching, instance of and switch. So these are the three things what we are going to cover. Pattern matching has sub sub items like instance of pattern matching, switch pattern matching and record pattern matching and eliminating these restrictions would make it more uniform especially from a programmer's perspective.
ideas. We we are looking at uniform data exploration. So now is your time to get out. But anyway, let's get into the code. Did I close? No, I didn't. Yeah. Um you know I wanted to compare I I wanted to compare the current uh one and the preview one. So I have created uh a a project which is called primadat classic and which would have the primitive
uh primitives disabled. I'm just showing that so that you are confirm that I'm not doing anything here. It's prim is disabled and in the in the in the primad. The primities are enabled so that we can do a couple comparison on what works and what does not work. Now to the code. So this is our uh motivation for removing the multiple has anybody quoted such a complex
instruction? All of us have. Right? What what what does it mean here? Uh we have an instance of and where does this instance of come from? the language specification and uh this is the Java 26 language specification. So I kind of uh marked up so that I impress you which is the uh instance of operator. So we are talking about the language the instance of operator coming
from this language specification which says uh forget relation expression which says instance of either it's a reference type or pattern but we are we are looking at the uh what this this is where this is where the compiler says okay I look at the specification say that this is a very valid statement. Now when we go back and you we look at the class file what does
the class file generate? Class file will generate an in So uh in the the bite code will have an say it it just loads the loads x and then checks whether it's an instance of uh class. Now this instance of is of course coming from the java virtual machine uh specification and that is uh that's here. So it says uh so what does it take? It takes
an object ref and it takes an index and it returns it returns something. So what does it return from a diagrammatic perspective? Let me just move it here. Sorry for juggling this but it's like this. You can consider instance of of a of the byte code something like a machine know which takes an object reference and which in this case is X and then it takes a
class type which in this case is Y for us then it does a magic and outputs one if it is a yes and zero if it is a no. So that's all it what it does internally. Before you're wondering why this is so complex, let's go to the next one. Something similar. Suppose instead of y we change it to integer. Integer not int integer that would still
be same. So previous see we can we can uh see it in a different way. We can see that if I is instance of uh integer we can see that can is it safe to cast I to this type of this type which is integer. So that's a question which we are answering and the class diagram there is no difference in the class diagram if you can
see it says instance of java integer instead of y no no rocket science here. Now, can we can we start with um why is why is this an error? Can someone clap? Yeah. Yeah. thing this probably very simple question for this audience yeah it is an error because as for the Java language specification the the it has to be a reference type and reference types are there
are two types right primitive types int uh care etc and reference types which are class any anything that's related to class so int is a primitive type so it is not a not a reference type and hence It's a it's an error. Now, if I want to implement this in the new one. So, what should I do? So, this is the preview board. The preview board is
where we enable the preview and then I so before we get into that the the we are not asking the question whether I can cast uh this we are asking the question whether I can convert this I to an int. It's a small difference but I I'm checking I'm telling whether I can convert to an int. Now can you tell me what should the what should the
what else should change for this to implement the class file the bite code the instance of operator should change in the instance of operator in the virtual machine is supposed to take uh uh supposed to take an object reference now this is not an object reference this is a primitive so it is it has to change when you look at the specification change it says no nice
try We are changing only the source source language specification. We are not changing the Now, now what we do? Any any suggestions? Oh, you're you're going far ahead. [laughter] yeah. So uh what see when we look for a type the when you check in for an object of it can be either true or false in this case what is a true value and what is a false
value what how can what can be a false value here so I I open up the uh I will open up the uh machine which we talked about and in this machine we are instead of objective we are sending a primitive type and and the class type class type is a primitive type. So uh this cannot be uh there cannot be a false here right because I
is always an integer you agree or you don't agree I'm not very sure okay so in the case of uh if it was integer what could be a what could be a null right now in this case can there be No. So, do we really need that machine? We don't really need that instance of machine of uh byte code, right? Can we can we say that it's
always true? We can. So, let's see what does the what does it generate? So, it generates I const one. That's all. So, that in instead of instance of it just generate I const one. It's always true. and it returns. Yeah. Again, I'm sure you're wondering why such a job for such a very complex. [snorts] Yeah. So, let's get to do one more. I forgot which number. Okay.
Ox CX3. Okay. 64. Is this fine? Sorry. This is the classic thing. So when I go when I go into the uh uh preview board, we allow this. We allow it. We allow short s instance of int. should we allow it or should we not allow it? Okay. So, I I'll we we'll just don't bother about the classic one. This is the primitive. So, we are allowing
it. Oh, there is a there is a there is a clue here. Should not have put that anyway. So, so it's a s is short and int is. So, what what's happening here? Can this be false? No. Why? It's a two different data types. Why is it why cannot it be false? Yeah. Always. So, why the the fundamental the the fundamentals behind that is like know I
don't know how many of you cricket fans I think not very many in this thing. So it's it's like widening the thing and if you look at the number line it is like you know short short will have only minus 3 to 768 because 16 and on the left the integer is like quite quite long and on the other side no rocket science. So whatever is short
here it can be represented in int as it is. So there is no problem. It's a widening primitive and we uh we have to uh I think we should be returning uh one right so anyway so this this is not important we have already covered this so we just look at oh this is what you're talking about the comma so I didn't realize the comma so and
if I look at the class do we get the icons one we do get icons one again no issues we are good Okay. So, before you guys sleep off, let's see. This is fine. That's always false. That's correct. But it's not always Yeah. So I I but it's it's saying set in widening primitive. What is the size of int 32 what is size of float? Oops. 32.
Still 32 size size the the storage in Java. Yeah. But but the the the key differentiation here is like we call it a we call it a widening primitive because of the range of values although the it's only 32 right the the range representation is huge yes right so why because in 754 uh we have the mantisa which is represented in 23 bits and exponent and blah
blah blah. So there is a difference. I I know I'm taking you back to the college or you know old times. So especially in these AI times maybe it looks so paleolithic but you know this is the uh the next Jeff is based on this. So we we don't know whether it is true or false. So when we say we don't know we we are saying that
at compile time I don't know whether it is a whether it it's always true or whether it's always false. It can be true can be false. It depend depending on the value. So essentially I cannot emit uh yeah this is again you know not going to go behind these but uh these are the different conversions. So I'm let's get back to this x.flow and in the essentially
I cannot emit I const one here. So what do I emit? What can I emit in the class file? Yeah, I is I'll emit this I I load I and I emit a method method call and this method call is uh coming from the JVM uh runtime. So essentially you are you're not using the instance of uh byte code but we are using a function a method
which would do the do the check which is uh which is this function. Uh so you can ignore this. This is the special case but essentially what it tries is it converts the value first to float and then to int. we are passing int and then it checks uh whether we get the same value or not. If you get the same value, it's true. You don't get
the same value, it's false. As simple as that. But what the compile all the things the compiler does it for you. It checks what is the type. It check it decides what exactly is the uh code that is uh has to be instrumented and it'll uh instrument. It's all transparent to the user. okay. Um, any guesses? Is this going to be true or is this going to
be false? I Yeah. [laughter] Yeah. We don't know. We don't know. So, we have to uh use our uh method. So, why? because we are passing int and it could be a short and uh that's that's this right integer to short conversion we are so if it is beyond those boundaries of short it has to be a false the same old trick we put okay this class
has to be only the Only the so we in we instrument the method only thing is the method which we instrument is changed it is int to short exact if it is a conversion and the the definition is more or less the same so not going into those details. I'm closing the rest of the things. Ah now uh what do you think the bite code would be
here? Yeah that's true that's all. So the the thing is the bite is bite is signed car is unsigned. So they don't agree. So they go to a third party and that third party is uh this fellow or integer. So you know there it's not a clear overlap. It's like you know partial overlap. So first the bite would be expanded widened to the to the integer. Oh,
I think I think the next one would is it? Yeah. So, it'll get expanded to uh without without any any special uh in instrumentation required at later point of time that integer would be narrowed narrowed to the character. So what's happening is the bite to car is like first it gets widened and then which would mean like you have a bite which is minus one as uh
uh said yeah this gets widened with a sign extension it goes to an int and then in the int this minus one becomes uh from the int it goes down to 65535 in the Yeah. So that's just the explanation here. Widening to by to int then narrowing from in to okay and one last one more last thing. I think people already sleepy. One last thing any any
takers what would be what would be instrument here we are converting boolean to car oh that's bad I'm showing the preview part so it's an error why it's an error because uh this depends on something called table of conversions And here not sure whether you can see it. So uh it's not very clear but let me read out something that looks like equal to is like the
identity conversion. So bite to bite, short to short etc. That's like no trivial conversion. Then we saw our uh uh our integer to short as narrowing. So this thing is called narrowing conversion. And we had the by to caras widening and narrowing conversion. So this table is given in the language specification. But if you look at the boolean there is no uh boolean cannot be converted to
to anything. So it's an error. So it's like boolean space boolean. It's only identity conversion here. Essentially there is no change. So you don't think of you there's no change in new there's no additions no new conversions to the language no change in existing conversions. you write a code as you were writing earlier and no change in which conversions are allowed in existing context such as assignment.
How uh I mean you don't have to know about this but how do you do you know I mean how many knows about the context there are there are different contexts in Java and different rules apply there. So if you look at the context there is an assignment. Wow. Yeah. So there is assignment context when you assign something there are rules available there. When strict invocation context
when you're looking for a method to figure out which method to invoke you don't find that then there are loose invocation context etc etc. But something which we are familiar casting context when you cast something then then there are rules there. With 22 there was a small section that was added called testing testing context which says that when you are doing a type comparison type and compare
then it comes under the uh testing context and there are set of rules. So not much rules were added at that point in time. But if you look at the Jeep for the Jeep for uh for this Jeep 530 you can see that the testing context have lot of uh additions and uh rules list there. So if you're writing existing code there's no change but if it
you're looking at this primitives and patterns these are the so you don't have to you don't have to know about all this the compiler will flag an error if there is something just that these it's just there so enough of these things now we are moving to 15 minutes okay so I think this this we all know if it is uh uh less than uh uh 24
bits then we flag float here the output and if it is above that we flag it is inaccurate. So this way you can write the program without any any uh specific type checks etc. And here if you notice we we are looking at the uh instance of patterns. This is how we extend it to the instance of patterns. Same rules, no difference. Like what we saw in
the instance of Okay, I think I'll have to go into the uh classic one. So now let us go into the uh uh switches. How does the switch look? Uh let me put it little simple. The if you look at the class file, this is a classic one. If you look at the class file, the switch would be represented by something called a table switch. Uh everyone
familiar with this or okay. Uh so when you what is the switch The switch here is uh it is taking a expression I and then it says case one case two case three right three cases and if you look at here table switch says it takes uh it takes an i. This is the one that gets consumed and it will if it is case one it says
go to 28. What is 28? 28 is the instruction address and it says push 100 and go to 46. 46 again is a return. So it returns 100. Similarly case 2 33 it says if it is case 2 go to 33 which is a machine address and here you push 200 it is equivalent to this 200 and then go to 46 which is a return simple no
complications here now if I change this why am I getting an error here it's a classic one it's a so I I'm not sure whether you can read it says cannot switch on a value of type long only convertible int values only which are integer values like know byte and uh strings or inium variables allowed not even boolean is allowed that's more for historical reasons that's what
the internet says because it uh uh we have redundancies we have if then else we have uh 32-bit and unlike C it's not like you know you cannot assume that there is a there is a bite is I mean zero I mean trul is one or false is zero something like that so even for boolean it is not allowed for long it is not allowed now why
for integer you get case 1 case 22 case 3 but for long it's a 32-bit the so the it's it's a 32-bit bit stuff. So you cannot represent this. So if we are thinking of implementing this in the classic in the preview version, how would we do this? sorry you know cause I to int. Yeah. Yes. So you you somehow want to change this map this long
to an int so that we can use the table switch. Right. So if you are able to hashmap something like 10 20 30 to 1 2 3 and then you can reuse the table switch. So we do something in principle we do something like that. Uh since uh we have already a solution for type switches. We kind of reuse that here. And uh you forget about the
line number four. You see that table switch. It has uh it has something it says 0 1 2. So somehow if you are able to map 10 to 0 uh 20 to 1 and uh 30 to two then we are and how how do we map that? So for that we use something called type switch. Again, it is a built-in function from the uh JDK where first
we load I we load I and we load a constant zero. This constant zero is index the index of this this this this zero zero. So we say I send uh uh I is a long so that's why it's long and then I uh then the index and by some magic this fellow would 10 30 10 20 and 30 and will return 012 respectively. But where are
we passing this 10 20 30 somewhere we have to pass this 10 2030 otherwise how do we get that so for that believe me this is the same class file and uh here if you notice it's a is a this mechanism of invoke dynamic is out of context uh for this talk primarily due to the lack of knowledge of the speaker and we have uh uh this
10 20 30 being passed here and that gets into as a call site and that gets replaced for the you know dynamic part and internally this 10 20 10 10 L 10 L 20 L 30 L would be returning this the the method would be returning 1 0 1 2 respectively is is this clear or okay now this is clear let's any any questions If someone this
is a fundamental uh change you don't have to know this it is just that the internal stuff which is which is internally happening here uh in the compiler will take care of this okay so I'm not going to go into the boolean because it's like only nine Yeah. U this also the the uh floating the float also does a similar story. So I'm not going to show
go get into the details but what I want to show is uh It'll if I go to the class file it'll send a float and it'll send the uh index zero and it says okay if it is zero go to 28 and push 100. So which is like which is this 100 if it is 1.0 zero. No change. The the type switch will take the float will
hashmap will hash it to uh zero here and return if it's a default it'll uh if it's a default default one is uh sorry 300. Now when I once I run what what will have what is the output of this program? It is 100. I mean no no doubt about this right because we passing one and if it is one it is 100 see you can guess
only two there only two values to guess so 300 let's run you think the compiler has error posit because 1.0 and 0.99F are uh float equivalent. So so if they are equivalent then I should have because you you don't know at the calculation time when you at the end of the calculation you might get one the float represented 1.0 as 1.0 Z or it can be 0.9999.
So that's the nuance when you when it is not an integer but it's a float. So in such a case then I should get a duplicate error here. I if I put because these two are equivalent this is equivalent to this. So I should get an error and that's exactly what I get. I get a duplicate case error. Okay. And any questions? Okay. Okay. Now going to
I think I have just one more two more important cases. Okay. I I'll skip this. I'll get into something which is more interesting. So now we are talking about type This is has been existing for uh some time but only thing is now it is a now it is a uh primitive type and if I go to the class how does the class look the class looks
uh a little complex but you can see that this is a type switch which takes the status which is the which takes the status as an input here which loads the status and it takes the index of the case K0 and start from there. So if it is a short it'll return uh uh it'll go to 32 the machine code 32 and load the uh load the
value of short and if it is uh not 32 it go it's the integer value. So so no no complication the same principle. Suppose we have no we have a small problem here. Uh the thing is both are integers. Both are integers and the so you you you see that the status so far is fine. invoke dynamic it's an integer integer and then uh this type is
capable of only telling that whether it is an integer or not because we are if you look at what we are passing we are passing the class of the uh class of this uh data type so here we are passing the class of the data type to the uh can it says I which is the integer class I It's a what I'm getting is a is an
integer or not. Now how do we with that only that capability we can model only case int. We cannot model when i less than 10. We can say that it's an integer. It's an integer. Thus we can say with a type stitch. So that the compiler will have to will have to stitch here. It uh you send something. It says it's an integer. Okay. Then uh uh
it says okay 10 I compare with 10 and if I if the integer compare is greater than or equal to uh 10 then go to 43 then here that means it has to jump to this particular place. How do we make it happen? It goes to 43. I load one I const one and I go back. there is a loop to the four like I go back
and restart send this uh value status and check whether it's a integer or not. Now what I do here is in this case I start from the index zero but in this case I start from index one. So the I just check for data type. The rest of the stitching the compiler does inside the bite code and uh yeah there is a there is a uh implicit
loop there. But that's how it is. Okay. So I have only two and a half minutes. So I'll just finish off with I'll just skip some of I'll finish up to records. Oh back to motivation. Sorry. Uh most of us have used records. Oh yeah. Now records the thing is you can create a record. You can create a record with uh primary types. There is no problem.
Still you're allowed to. But the problem comes when you are uh you have a record I which you you I mean you have a record which is defined as taking double. you're passing a passing an I and you're creating a record that's perfectly fine. Uh I will be converted to uh double when you're constructing that record because it's a constructor. There's no problem. But when you're taking
it out when you are uh taking the data out of the record then you don't get uh I you get you can get only double. This is actually an error. It'll it'll be an error if I show it in the classic. So just believe me due to lack of time. So we implement this in a uh we get this implemented so that the record when we extract
the data uh r int i is also recognized and rd of course is recognized because it's a it's a the definition is and uh if you are really interested in the class file I am not going to explain it afterwards we can get into it. It is like it again is a type. search and then it takes an object and and then it'll go and check what
use the record extractor uh figure it out and then it'll use the I mean it'll take the double out and then it'll check whether this double can be converted to int. If it's converted to int then we uh the first the record r int i will be accepted. If not uh if not you can see this loop that same principle it go back to nine but with
a index of constant uh one and then it'll return to doubled. So yeah I think that's all. So before we conclude I just wanted to show you the last one last slide and that is um so we what what we talked about is the anything in the white is concept so testing cont context which is something which has come in we have the exertness concept and the
conditional checks I mean these are depending on whether if it is exact if you just put as one and if it is conditional you put that uh uh method and anything with a black one this these are The features which get implemented that is the uh instance of type checking and instance of type the typical instance of and the switch with all primitives allowed and the next
level we have instance of patterns primitives switch patterns with primities and record patterns with with the conversion of primitives also allowed all this at the top you have the uniform data exploration. Uh thank you thank you folks and [music]