 Welcome everyone to the session to be really do FP in Java by Ben Evans. We are glad that he could join us today. Without further delay, over to you, Ben. Good morning, everyone, or good afternoon for those folks who are in India. And thank you for the introduction, Ashish. Let me just, first of all, share my screen. And also, so you get a couple of seconds to look at my lovely desktop while I start on my keynote show. So welcome, everybody. So the title of my topic is do we really do functional programming in Java? For those people who haven't met me before, my name is Ben Evans. I'm a senior principal software engineer at Red Hat. And before Red Hat, I've done a bunch of things. I was lead architect for instrumentation at New Relic. I was co-founder of Object Clarity with Martin Verberg and which we was acquired by Microsoft in 2019. And then before that, I spent a lot of time working in finance. So I was chief architect for this derivatives Deutsche Bank. I worked more with Stanley during the Google IPO, which was, gosh, 17 years ago now. That's that's a long time. And I've also had a career in gaming as well. As well as my career, I done a bunch of things in the community. I have Java champion and Java one rock star speaker. And I worked on the Java community process executive committee, which is the body which which decides upon and approves all new Java standards. So I did that for six years. And I'm also known by living Barcelona now, hence, hence my t-shirt, which shows the Barcelona skyline. But I lived in London for 20 years. And I did a lot of work with the London Java community, helping to organize a big community and also founding a couple of projects, including one that you might have heard of, called at Opt Open JDK. Okay, so we're going to talk about functional programming and what that means in Java today. Now, you may have seen a version of this talk, which is available that I gave at JBC and cons last year. That one is, I guess, more orientated towards people who are Java programmers, rather than folks who are specifically functional programmers. So I'm going to take a slightly different direction. So if you've already seen the video, that's fine. We're going to cover, obviously, quite a bit of the same ground. But we're also going to take it in a different direction for quite a lot of it because it's really those functional aspects that I want to talk about today. The story of functional programming in Java really starts, I guess, with Lambda 8, with Java 8 and landers, of course. You know, this is the point, Java had not really had a notion of Landers before that, not as a first level language construct, if you like. Now, of course, it was always possible to do the sorts of things that we do with Landers in earlier versions of Java. In Java 7 and earlier versions, which is quite old now, it was always possible to write a little tiny in a class, which only had one method on it. And that was then understood to be semantically the same as a Lambda expression. But in practice, it was pretty clunky and it didn't have, you know, functional programming to me is not just about the bare bones and the semantics of what it means to be functional and to be a function. There is a certain amount of syntax and other aspects blended into it. And that's the thing that I think I'll come back to a few times during the course of this morning's talk. So Java 8 is really the point which people focus on because at that point you have Lambda Expressions and, you know, the Amos that within Java's type system. A Lambda is automatically converted to an instance of a class, which implements some target functional interface as they're known in Java, which is basically Java's equivalent of a function type. Okay, I have more to say about that, but that's the sort of basic feel of it. I guess anyone that's programmed to Java in recent years will kind of know that, but that's the framing context. So why? Why do we do that in Java? I would say that there are really five reasons. The point is to provide a usage which is much more expressive, so that rather than having the functional nature hidden behind all of these tiny little limit classes, instead, you know, you can actually see things by writing function literals and having them auto-converted. That's much more expressive, it's clearer, it's more concise. You know, the library code is easier to understand as well. These top three reasons I think are really, really important. There's also the question about improved program safety. People debate this one. I think it certainly has helped. I think that the nature of the way that Java's Landers are constructed with what are called single abstract method types actually does improve people's overall safety quite a bit. The one thing which I think is more debatable, and I've kind of ranked these five properties that we wanted to get out of Landers in order of importance, in order of, you know, for the project to be successful. If they've delivered, you know, top down on these five things, then that's great. The bottom one is data parallelism. Now, before Landers first came out, when all we had was Java 7 and Java 8 was the new thing. There was a lot of talk about how this would automatically enable Java programs to take better advantage of multi-core environments. You know, you can kind of automatically spread out over the course by taking advantage of data parallelism. This has not happened. Number one, I think, is the only one of the five that we can raise a question about as to whether it was really successful, or whether it really hasn't caught on in the way that people might think. And I think the reason for that is, it's, you know, automatic parallelism is really hard. First of all, if you have a stream of tasks, which is in Java, how we do it. We have an abstraction called a stream, which is sort of lazily evaluated, and the data items flow through it. You think, well, okay, if I could split all these up to do some sort of aggregate operation on them. Providing the ordering doesn't matter, and providing there's no dependency between them. Maybe I can do something. And all those things are true. But actually, it turns out that for quite a large range of stream sizes or collection sizes, if you like, the improvement for parallelizing is not great. And in fact, the communication overhead in terms of something like Amdahl's law plays such a big part of this that it's actually not clear how useful automatic parallelization really is. Certainly I've been in the 10 plus years since since Java 8 came out. I think I've 10 plus or was it 2014? Let me see. Java 18 came out in 2021 Java 11 therefore came out in 2018 so it must have been around 2014 so some not quite 10 years, eight years, eight years Java 8 came out. I've seen a lot of code bases in that time. And I think I've seen the use of parallel streams, maybe half a dozen times. And of those half a dozen times, it was actually incorrect in three of them. So that's not a good track record. At least a couple of the other cases the remaining three, it was marginal whether it provided any performance improvement at all. So it's a lot of work for a performance technique which was not in practice proved to be useful. Okay, well, four out of five is not bad. We get the first three pretty clearly. So that's a success and lambdas have been successful. They have been adopted by Java programmers in huge numbers. Basically, the uptake of Java 8 was the fastest uptake and release of Java in history. People wanted lambdas. So it's become very much part of the culture of the Java programming culture. But is it functional? Let's dig deeper. So the standard thing, if you want to say any language is functional, you have to be able to treat code as if it was data. You have to be able to have some notion of a function literal and be able to pass these things around like any other objects. Now, in Java, as we know, there are two types of values, at least until project Valhalla comes along, whenever that will be. Right now, there are two types of values. There are bit patterns, aka primitives, which are identity free. They have no object header. They have no identity. They are just patterns of bits. And you have object references. That's it. Those are the only types of Java value that exists. Not to talk Java values at JVM level. So other other JVM languages such as Scala, Clojure, et cetera, also have to work within that data model. Now, is a lambda expression a pattern of bits? No, clearly not. Therefore, it is an object reference. So when we talk about about lambdas, what we actually are immediately led to is the fact that they are their objects. So the closest thing that we can build to a function in the JVM is an object. Now, you could imagine a very, very simple case. We want an object which has no fields. I'll come back and talk about lambdas and capturing like for now, no fields. And it has one extra method. Java, of course, has a strict inheritance hierarchy. So the type that our function object will implement will be the simplest thing it can be is a subclass of object with one extra method on it. And that basically is where the concept of a SAM type or single abstract method comes from. Or an interface, which only has one non default method on it. And that is the simplest, the most minimal representation of a function that we can make. And that idea has been around semantically since Java 1.1, when in a class is first arrived. So that's the basis. That's what all of this stuff right down in the JVM is going to be implemented at. Now, as I'm sure you remember, the way that this works is the lambda expression is converted to an object, which has the appropriate type. And what is that type? It is a direct subclass of object that implements the appropriate target type interface. Okay. As a result, we also get that the static typing of Java applied to the method signature must also work. So the compiler is able to check that that compile time and say, ah, yes, you've passed in the correct, the correct form of methods to satisfy the signature of the abstract method which has been generated. Under the hood, this uses, well, a couple of things uses an API called method handles and a method handle is kind of like reflection. The method handles are quite similar. It's sort of a lower level technique. It's not as well known. But in fact, it's become increasingly important so much so that in the latest version of Java reflection has actually been replumbed to use method handles. But until Java 17, there were two separate internal APIs for doing reflection and introspection at runtime. The reflection API and then there's method handles method handles we use for lambdas and some other things which we'll talk about. And the reflection API has now been just rebased on top of the on top of on top of method handles. So to API still exist the reflection API the method handles API, but underneath there is only one implementation and that is method handles. So this speaks to the transition the change of Java towards something which is using more of this deep level technology. Which is more advanced and more sophisticated down in the runtime, but also provides us with something which looks as though it's it's closer to a functional language. So I'm does despite the similarities despite the fact that you might have guessed that this is just some tactic sugar over in the classes that is not the case. Instead, there is a new mechanism in the JVM. It's been around since Java seven, but it's been a very slow burn for how much it's starting to just spread throughout the platform. It's called invoke dynamic. Interestingly, invoke dynamic. One of the things that enables you to do is it enables you to change the implementation of lambdas. So the first versions of lambdas came out in Java eight update zero are different to today's lambdas. The implementation for how lambdas are implemented has been modified in a backwards compatible way. You can take Java eight bytecode and you can apply it to a Java 17 runtime and you will see a different implementation be used without recompilation. This is one of the powers that the invoke dynamic has it's an extremely powerful mechanism it's been well thought out and well designed. It uses a new bytecode called invoke dynamic, which is the first genuinely new bytecode that Java has ever had. And today it's still the only one that's new. They'll have a bring a couple of new ones, but until that happens invoke dynamic is all we've got. You can relax parts of the static typing system here. You can you can do all sorts of really cool things with invoke dynamic and method handle. The other thing which is which is awesome about it is that it in many cases it's now as fast as regular method dispatch. The whole idea behind this one of the goals was to reimagine reflection in a way that was as performant as as regular code and without the performance hits that reflection had. Okay, so there's some cool stuff happening at the end level is to take away from here. But what about, you know, the the actual functionalness of it how how much does this provide us with a basis that in real functional programming. Well, the other piece of Java Landers is a move from external iteration, where we have the Java collections the client code has control. We you know when you get an iterator and you loop over it and this is this is external iteration you deal with individual elements one at a time, you have to deal with boilerplate. You are exposing to a certain degree, a level of internal implementation this is sort of encapsulation violating you can, you can argue about how significant it is. But there's no getting away from the fact that the idea of functional collection handling is to do internal iteration is to is to provide the collection the aggregate to have control, and then to be able to say, here is a function, which describes what I want to done. Now you do that is up to you. You know just just don't go and deal with it don't do the needful run this function over the collection. And that basically is to to reduce boilerplate and to to to allow the scope of the code to collapse, you know, part of this, I said that the syntax matters a lot not not as much as the semantics is hard to say the two are tied together. Without clear concise syntax, you cannot compress meaning. In the way that we can with with functional approaches that's that's that's the idea that by having transparency we can we can put more code, more concentrated code on the page and still have it be comprehensible to the programmer. So syntax does matter for that. And that's one of the areas where Java, as we'll see this fall down. So what have we ended up with we've ended up with functions as first plus values we've ended up with ultimate reduce. We've ended up with a Java util function package, which has some function objects in it with functional composition and things like that right. This is table stakes, in my opinion. Is this functional programming. Well, maybe. What I would say is that it gives us a view of Java's type system which I can express like this now. So I think this is really a decent description of Java eight or 11 with 17 of course we've got we know the amount to which this is slightly functional is shifted and I'll have something to say about some of these specific words a bit later on. But you know if I use use words to describe Java's type system like static and nominal and object imperative type raised, modestly type inferred and slightly functional. That is, you know, and if at this point you want to go and watch another talk by all means do that's kind of my conclusion. I think there's an interesting journey to be had to explain that and to also provide some nuance around what I mean, precisely by slightly functional here. But that's that's essentially the conclusion like we're not going to ever turn Java into Haskell. So let's, let's take a step forward and see what is it that would make a language functional what what else can we add. Well, I would argue that for things which are really functional. It's a question of how many of these seven things do you have. My magnificent seventh functional program and you want to talk about purity of functions, we want to talk about meat with data, we want to talk about higher order functions, tell recursion closures laziness and currying. Those are the seven things, which, which you know, I say if you're going to go beyond just just simple lambdas closures and and filter map reduce this is the next the next step. It's a big step up from where where languages like Java are to the next level. So, so how do we do. Okay, so pure function function that doesn't look all to the state of any entity side effect free like a mathematical function. This is the standard description that we have of pure functions. And because it just, it returns results and dependent only on the arguments not on any external state, which could be modified. It's, it's transparent, and you can memorize it and so on and so forth. Now, the JVM of course doesn't have functions. So the JVM has methods and the way that the JVM is structured is the code goes into methods. And the methods go into classes. And the classes go into packages and packages go into go into modules. So there was there was a hierarchy namespace as a hierarchy of scopes as you go up, but the fundamental point is the JVM only loads and links classes. It is the minimum unit of functionality that the JVM will ever deal with. And the last point is, that is a basic point of the Java security model, you can't just take a free function and load it in. There's no way of doing it has to come in through a class, which of course is another constraint on why why landers in Java have to be function objects. Okay, because they have to be instances of a class which has been linked. So we only have methods. So pure method is one that doesn't modify object or static state it doesn't contain objects to to modify fields, which are mutable. It doesn't contain any external state. And it does not call any, any non pure method. Well, this is quite a restrictive set of conditions. And I would say that this is one of the places where it shows up the real difficulty of using the JVM for pure functional presenting functional programming. And that is, you know, that's not a great start. If we if we have this, this very much the sense that the JVM is based around mutability. It also makes it difficult to write pure functions. It also makes it difficult to write immutable objects. So, immutability is extremely powerful it makes code much easier to reason about fundamentally because objects have a trivial state model. They are constructed in the only state that they will ever exist in. And they are, you can copy them around between threads, even between, you know, VMs or instances, and the mutability of data is really very, very powerful. And in fact, you know, you can you can even extend the idea of immutability to say, you know, are there are there any almost immutable approaches where we still get a lot of benefits. But we can still, we can extend it slightly and make it slightly mutable or whatever. Now, of course, famously, Java has string classes, and the Java string class is effectively immutable. And by which I mean that you, you, it does have one piece of mutable state, but you can prove that any data races which result from computing that state, which in the case of Java string class is the hash code, all data back races of the nine. And in fact, the way the string class works is the first, you check to see whether the hash code has been computed, as you use it. And if not, you have you could have multiple threads racing to compute the hash code, but they will all end up with the same value. So in that case, it's kind of like a promise or a Java completable future where you can have multiple threads that race but the resulting data races of the nine. So that is one example of how you can extend immutability. But Java doesn't have great support for it. Yes, we have final. And you can mark fields and local variables as final, and they will not be updated, but only the references immutable. So you can't mark an entire object tree or object subgraph as being immutable, only the first link in it is matched. So you have to make sure that it's it's final everywhere. And that obviously is prone to bugs. What happens if you need to make use of data which comes from a library which isn't immutable. Your nice reasoning about immutability has been broken now. So of course, there are some horrible things you can do with reflection to invalidate it should you wish to. But generally speaking, not what not what you should do. So Java does have some attempts to do proper immutability. We have copying and with us so we have things like the Java time API, which was a complete rewrite of the unfortunate Java utility original Java functionality for for date and time. So the Java dot time is completely mutable and it sets up this idea with using factory methods everywhere and these with us. And that is an idea which is reflected in in project Valhalla, which I've mentioned a couple of times. Okay, so that's immutability. Yes, there is basic support for it, but it is only basic support. Let's move on. Let's take a look at what's next. So next up we have higher order functions. This is straightforward. It's a function which takes either takes function parameter or it returns a function. So for example, in Java, you have the function types. So that is perfectly fine. You take in a function, which takes in a string and you return a function of strings to strings. So I've called my function, my higher function here, prefixer, it takes in a prefix and it returns a function, which, which, which prefixes a string with with whatever you passed in. So so far, so good, we can do that. And as you can see that we have this code and you can actually, you know, the Java compiler is smart enough to be able to convert it to this form here. So you don't really need the extra brackets here. You can, you can write this all in one line, which is, I guess it's okay. So that's fine. This actually has some subtleties to do with type erasure. Because notice that we have the generic type arguments here that this is takes in a string and it returns a function strings string turns out there are some corner cases where where type erasure can come back and haunt you. So I shan't I shan't go into that now. I haven't got enough time really, and instead just say that the higher order function support in Java is reasonable. It's not, you know, it's not the same poor story that we saw with with with pure functions, or that kind of half story that we saw with the mutability. Oh, well, let's move on. Let's talk about recursion. Okay, so the standard view. This is a Java view of a factorial calculation. This isn't a great example, because you basically overflow even Java's longs very very quickly. So if you actually try to use this code, you will find that it gets up to about 31 or 32. I think it's 32 factorial, which is larger than Java's longs. So that's not, that's not actually a lot of space to demonstrate the property that we're showing here, but because you get overflow of the arithmetic. Or you actually get a stack overflow, which by the way is what happens when you run this. So you, you can convert it into a form like this. And of course this is a standard trick by using a helper function, which is private. Now in a language like JavaScript, or something which supports nested functions, you could put that nested method inside this one but Java doesn't do nested methods. Instead, you have to use this, you have to have a helper factorial shakes into elements and returns this. You could do this using a lambda expression, but actually at that point you'd need a private interface. And the resulting bytecode isn't anything like as clear, whereas this one I think kind of is you use a two argument. And if you've seen this trick before, you know what's happening. You're using the fact that you have a two argument function, and you are storing state effectively in the stack. That's how this does implement implementation works. I won't show the bytecode for the actual the front door function. It's our factorial, because it's not very interesting. But what I will do is I'll show the bytecode for help factorial to help factor. This takes in two parameters, both of which are long and I don't know if you folks know how to read Java bytecode. This is pretty straightforward. What we did what we're doing is we're taking reloading our first argument, loading zero and comparing them. And if it's not zero, then we're going to continue down into this part. If it is zero, we're going to return. So bytecodes zero through seven are basically the test condition to check to see whether it's zero or not. And from bytecode eight onwards. And if the numbering seems a little strange, by the way, that's because Java bytecode. Some of the top codes have arguments to them. So for example, a bytecode three, we have an if not equal instruction. And the target of that is instruction eight. So, so what that means is that the two bytes that follow. So bytecode four and bytecode five are actually the bytes zero zero and zero eight. And they're combined to make the target for the jump instruction. So that's why the stream seems to skip from three to six. It actually it actually uses up some of the stream in order to hold the arguments. Okay. And then so if bytecode eight onwards, we produce the two multiplications and then notice the online 14 we do an invoke static. So we do a recursive call right before the return. So this is a classic case of tail recursion or generally tail calls. And now we could do this. Notice the bytecode is identical for a lot by code zero through 13. But now in this version of this, we produce modification of the two, the two variables zero and two, and jump back up to the top again. So there we, there we go. We are, we are repeatedly moving through the, the code and when we hit the end, rather than doing another call with jumping back to zero again. So this is a conversion of a recursive function call, which is always represented by an invoke instruction into iteration. Okay. So that's, that's very standard technique. And some languages do this. So, Scala, for example, Kotlin, they will do this. But Java doesn't. Java, a method call like this one to help fact return help fact by minus one items J is always translated into a function call into a method call. It is never converted automatically converted to iteration. And there is no way at Java language level of making it do that. So the tail recursion in Java is not optimized and come by the stack. Java code, compile it, and then run some sort of bytecode transformer on it to convert it into the form. And that would be totally legal. And your program would run fine. And that's basically what, what things like Scala did. But by default, job without doing that rewriting, Java cell recursion is not optimized and will blow this up. Okay, the reason for this and there's been some discussion about about whether we could fix this. And, you know, it's been an idea which has been talked about for at least 10 years. Basically, it comes down to the expectation that the stack trace produced by Java will be completely valid. So, so that you will be able to see every frame on it. And that that's more important than fully optimized cell recursion. You can see that argument it is a behavior change. But there's no getting away from the fact that this this feels not only sub optimal, but that actually it's a bit of a bit of a poor show that we can't really do proper to our recursion here when it's clearly possible in other languages. Never mind, move on. Let's close us. So, closure broadly a lambda expression which captures some state. You can think about this type of thing, where you, you have a lambda who, which is pulling in that I, of course, what happens if we uncomment the line. Interesting. So for people that don't know what will happen is your program will fail to compile. And that has a requirement that any parameters that appear in in lambdas, so like the eye here that they have to be either final or effectively final it is a compilation error to attempt to reassign after the lambda expression. Okay. This restriction is is is is, I think a little onerous. I'll explain why it happens in a second. But you are. But it is there. I personally always preferred that we had to explicitly write out the final, just to make it clear that we were performing the capture, but other programs seem to seem to resemble that. So, now you can get away with it, not writing final in time, providing you don't actually do a reassignment later. So let's take a closer look at some more by code and see what's actually happening there. So if you look at what what happens lambda bodies in Java turned into private static methods. That's why they're not really in the classes. If you if you compile a Java class for the lander in it, you will not see a second file on desk, which of course they would be if it was an inner class. So instead the lambda body has been turned into a private static method, and the captured variables are passed as arguments. And what happens is the, the int. So this is a function from strings to strings. So it's going to require one, what we call dynamic argument, which is the string, but it's also prepended with the int, which is what we call a static argument. Because the static argument is captured at the point where the lambda expression is created. The line I, the value of I 42 is copied as a static argument at this at the point where the object F is created. So that is already has already happened has already been captured when we execute the line, which creates F is captured at that point, and then it's passed into the body of the lambda later on. By the way, if you if you if you're playing along at home. So this is Java rate by code. The Java 11 and Java 17 by code will look quite different to that to this, because the code and the way that string concatenation is done in Java 11 and 17 is different to how it's done on Java rate. So, but the method signature the fact you're capturing the end will be the same. It's just the implementation for the string concatenation is different. So, the big question this is this is where we come back to think about this from a functional perspective, is this really a closure. Well, I'm not sure. Java is strictly a pass by value language. Okay primitives are passed as the value objects are passed as a bitwise copy of the reference. Okay. So this is passed by that pattern, because that's kind of what it is. It's also worth, worth noting that this means the, the changes to primitives that you make. If you if you change modified a primitive inside a inside a lambda like this one, you try to modify I hear all you're doing is modifying the copied value. You have to pass that back to call a context. So languages that have a notion of environment like scholar, I think J Ruby as well. There is no notion of the capturing environment that's been that's being passed in here, because typically closures will pass by reference, and we can't pass by reference for an primitive. So non Java languages that want to support true closures or pass by reference, their runtime environments must provide the support for it. The JVM is not going to do that for you out of the box. So that you if you wanted to do that, you have to provide it within the runtime. I think they might have been a question about that so I hope that answered the question. Okay, how are we doing. Okay, I've got about 15 minutes. Let's push press on. We have five minutes. Five minutes for questions. Okay. So let's speed up a bit. So laziness values that are not completely needed. Java screams do use laziness. The operations are usually lazy. Some of them are eager. And generally speaking, the, the, the terminal operation, the thing which completes the pipeline of a stream is typically eager as well. That always has to be eager. So values are pulled through through the pipeline until they hit an eager operation where the stream is materialized. So for example, if you're sorting, you need to see the entire stream. So that has to be an eager operation. But in general, as far as possible, Java streams are lazy, but that's it. There's no direct language level support. There's no JVM support for general lasing values. And in fact, not many JVM languages support a laziness directly. Clojure is, is lazily evaluated. And you only have laziness for sequences. That's so that that is, you know, laziness is not just a Java problem. It's also a general JVM language problem. I think Java gets a swing and a mess here for this one. For carrying and parcel application. So supplying some but not all arguments for function. Java has no automatic support for carrying. And in fact, Java does not have a complete set of general function types. It has function and by function and, and, and operator interfaces, but no general function one through function N, like we see in, for example, Scala. Related to this, Java syntax also trips us up here because Java syntax doesn't allow hiding of the method calls. So in a language like Kotlin, you can just have a, an object which contains a function. And you can just have a core syntax on it, which is just some brackets in Java that is not allowed you always write out the name of the method when you're when you're calling it. So it's typically the apply method. And that's why Java code which uses function objects is typically more, more of a buzz. Now we could imagine a different world. We imagine something where we decided we did want to do carrying in Java. So we have the standard interface for a by function, which has gotten an application of a function to T to you. This is the full generic form where you have to you and are, and then you have a generic default method called and then which, which is placed application. Okay, so you return this and what you're actually doing is you're binding up some, some data inside it by returning a new lambda expression. So you can then imagine that we would actually have two functions like this. A curry left and a curry right where basically you would just apply different functions to this. Okay. So the main problems that we're seeing with the type system and collections are the Java's type system is not single rooted into an object do not have anything to do with each other as types. So what that means is you cannot put an int into list, for example. There are also problems with void. And the design of the Java collections in themselves implies mutability. Mutability is not a well separated concern of the Java collections interfaces. So and all of the collections have on the modification and adding methods and the way that that's handled is not is not good. So if you, if you try to use a method, you want to define an immutable collection, like a, a, oh, I don't know, an immutable list, for example, the way that you would do that is you would throw unsupported operation exception on any attempt to modify. That's not an exception solution, in my opinion, because unsupported operation exception is a is a runtime exception so it's unchecked, and there was nothing in calling code which will tell you that it might do that. So there's no encoding of the of the immutability of the of the implementation at type level. Streams gets something's right, but the problems with streams are collections of first class and they are embedded everywhere through the JDK. There's lots and lots of of the JDK uses the collections and all sorts of places. Streams are not because streams were only added in Java eight, they have limited applicability. It's also true that Java has no tight level traversable concept, as we see in Scala. So we have a, you know, just only the ability to iterate with iterable was no notion of a type which represents an internally iterable. object. The collections in addition are not functional collections, they don't have any ability to do map and filter they're not functionally transparent. Streams are somewhat, but there is a limit to how far you can push that. And in particular, because of the necessity of using the stream constructors first as not first class flat map is extremely limited in Java. It's not obvious and it's not intuitive how to use it. So lots of Java programmers don't really they do map and filter and reduce, but they get very worried as soon as flat map shows up. Okay, so the here is our type system. It's static, it's enforced, we use class files for everything. And, you know, I think I kind of like this quote from my buddy Curtis bow that strong and weak typing so I always make sure I say static typing. It's phenomenal that we are not a structural typing language at all types are only compatible with explicit inheritance exists and everything has a has a name, at least for L values. So for at least for things which are on the left hand side one assignment is the only way to talk about the type is by name, and there is no duck typing. The model is one of Java strong points it's very simple. It's not pure oh, it simplifies teaching it because you have primitives. The type are raised because we have parameterized types only exist at compile time, and the generation of bike code removes information information destroyed, even a class file level. Lots of complain about this. It's often quite misunderstood. And finally, slightly functional things. The collections are very concrete. The final point is, it is only slightly functional but does it matter. Java developers are well used to lambda expressions now they're well used to the support of map filter and reduce and basic functional patterns. And perhaps that's all they need. If people would grow up to be functional programmers and decide that that's the style of programming that works for them. There are plenty of other languages to choose from there is closure and scholar and Kotlin and Haskell beyond that. So, perhaps in the end, it's not very functional, but it doesn't really matter. Thanks very much. If you if you're interested in approaching this more deeply from from the Java point of view, and also talking about closure and Kotlin money book, the well grounded Java developer has got a lot of material about about this topic in it. Thanks for having me today. Thank you. We do have one question, one more question from Michelle. Is this modern Java support pattern matching or are there any plans of what you're aware of to support it. Do you think it's an important feature of FB language. Yes. Okay, so this is a really interesting subject. So I've talked all the way through about a project called Valhalla. And at the moment, the future of Java. There are four main projects that are in flight to to evolve Java into into its next major form if you like. It's kind of like Pokemon evolution forms. But the, the, the next form which is going to come is really defined by two of those projects one of them is project Valhalla, which is a rethinking of the data model, and is about VM level concerns. Solve some of the functional programming problems for us, things around unification of the type system, and dealing with with primitives versus object references. So that solves a bunch of nasty problems that there are in one part of the language. But the other project, the one which I think is, is closer to the developer is called project amber. And project amber is different from the other projects because it's been focused on small incremental changes into the language as time has gone on so Java now has a release cadence of every six months. So we were actually seeing new versions coming out and little increments to the language of being delivered. So probably the first one was switch expressions. So this basically it was a first step towards pattern matching by changing Java switch statement, which is very very see like, and providing a form of it which actually uses is an expression. So from there, the intent was to expand the notion of what switch could be by introducing patterns. The patterns were actually introduced. First, not in the switch statement, but actually in Java's instance of. So basically that provides a pattern, which matches against the type, but then also introduces a new local variable, which is scoped to the to the block, but is of the appropriate type. So that is the basic first instance of a pattern, and incrementally things are being added to the language now. So Java has a process which adds in features using what are called preview features. So feature shows up and you can only use it by explicitly enabling it. And then we do a couple of iterations of that until until the feature is solid. And then it becomes a final part of the language. So where we are with Java 17 which came out last September, and it's the most current and up to date version is, we have some, we have switch expressions. We have the type patterns, and that's kind of about it. There are some other things which you can switch on by by by adding on the preview features, but it's still in the process of being delivered. The intent is to you to deliver an entire set of features for pattern matching, but we're still getting there. Another thing which has also been added related to that is algebraic data types. So Java 17 sports records, which are product types, and it also supports sealed types, which are some types. So that's that that is one really nice feature. And with with just the addition of algebraic data types there has been some movement there as well. Okay, should we take another question. Okay, we have two more. Jyothish is asking, so is it not recommended to do FP in Java, better to use other suitable languages. What's your opinion about writing FP style code in Java. Well, you can do it. I mean, of course you can do it lots of and it depends what you mean. That's one of the messages of the talk is that it depends what you mean by functional programming. It's a different view. Functional programming is not a monolithic thing any more than object oriented programming is a thing. I sometimes say about OO that you could name any property of OO languages, and I can find you an example of a language, which we both agree is object oriented, and which does not have the feature that you that you mentioned. So you can say classes and I can say JavaScript, you can say encapsulation and I can say Perl, you know, and we can, it's FP is like that. Different languages have different takes on what FP is. And I always think that rather than it being, you know, a binary is this language FP or not. It's a question of a sliding scale, and also a personal preference what works for you, what works in, you know, in the way that you choose to program. So can you write FP code in Java? Yes, of course you can. And an experienced programmer who has learned to program in an FP language, quite often will write better Java code when they come back to Java and they apply what they've learned. This is why I think it's important to have the different styles and to have experience in several different languages and several different styles of languages. Because they are, you know, they provide insight and a better way of approaching problems in, you know, even in a language which is not formally in that style and tradition. Yeah, we have last one. I will take last question. And maybe next questions can be taken up at the hangout area. The last is what are the other languages that overpass the Java in FP. Also, will Java be able to catch those other FP languages? Okay, so there's a couple of things here. So first of all, it depends on whether you're talking about languages which run on the JVM or not. Now, again, this is a choice. This is a trade off that engineers have to make. The JVM is the most well tested and highest performing general purpose language runtime that we have, you know, I'm not knocking beam beam is a great virtual machine. But the JVM is just much more widely deployed. And, you know, in its domain, which is general purpose, specifically general purpose, it can't be beaten. So you decide, do you want to stay on the JVM, or do you want to go somewhere else? If you want to go somewhere else then beam is an excellent choice, you know, you have a line of things like that. Or if you want to go down the static type route you have, you have Haskell, but both of those approaches require you to leave the JVM. And it's rich ecosystem, and it's community, and it's ease of finding programmers, and all of the things which make you want to be on the JVM, you lose if you go to one of those non JVM languages. So, to answer that question, what about non Java languages on the JVM. Okay, sure. You basically have a choice of three. The three major non Java languages, which do FP, well I would say are Kotlin, Scala, and Clojure. And of those languages, Kotlin is a modern Java. It's basically someone designed a language with all the things that we've learned about Java over 20 plus years, and made them into a language which was better at doing clean up a lot of the rough edges, had a lot of nice new language features that people find to be productive. So that is why Kotlin is essentially a better mouse trap, a better version of Java, which does have better FP support. So if you want that, have that. Now Scala is an interesting case because it's been around for a long time. Scala's been around since before Java 8 came out. So it goes back to at least Java 6, that sort of timeframe. And it was originally designed as a research language, which just got more popular in industry. And originally Scala was the way of doing functional programming that was better than Java. It was the better mouse trap and instead, it's now moved on to become its own thing. So Scala is now a very solidly functional programming language. And I used to make jokes that sometimes in the Scala community, you'd find two sorts of folks. Folks that wanted to write Haskell on the JVM and folks that just wanted a better Java. And over time, I think the language has developed much more in the strongly functional direction. So it's an excellent choice if that's what you want at the cost of moving further away from Java. And then there's Clojure. And Clojure is different again. It's dynamically typed. So it's not a statically typed language in the same way that Java is. It has a very interesting, very lightweight runtime. It's very lisp. People can argue about whether Clojure is really a lisp dialect or not. I tend to think of it as a lisp dialect with just a bit of extra syntax. I really like it. I actually do programming Clojure from time to time if I have something which I feel like I want to want to explore it a bit more. So yeah, so that's your choice. Do you want something a bit like Java but better? Do you want something which is statically typed and heading more off in the Haskell direction, in which case pick Scala, or do you want something dynamically typed and a bit lispy in which case Clojure? So as with everything in software, it depends what you're looking for and it depends what you're trying to achieve with it.