I’m a programmer with a fair amount of reasonably diverse experience, e.g. C, C#, F#, Python, Racket, Clojure and I’m just now trying to learn how to write good Java. I think I understand most of the language, but I don’t understand how to like it yet. Most Java programmers seem to basically not believe in many of the ways I have learned to write good software (e.g. be precise and concise, carefully encapsulate state, make small reusable modular parts which are usually pure functions, REPL-driven development, etc. etc.) or they apply them in ways that seem unfortunate to me. However, I feel foolish jumping to the popular conclusion that they are bad and wrong.
I would really like a book—or heck, just a blog post—which is like “Java for Functional Programmers” that bridges the gap for me and talks about how idiomatic Java differs from the style I normally consider good and readable and credibly steelmans the Java way. Most of my coworkers either don’t like the Java style, only know the Java style, or just don’t care very much about this kind of aesthetic stuff, so none of them have been very good at explaining to me how to think about it.
I don’t know Java books, but I would like to react to this part anyway:
Most Java programmers seem to basically not believe in many of the ways I have learned to write good software (e.g. be precise and concise, carefully encapsulate state, make small reusable modular parts which are usually pure functions, REPL-driven development, etc. etc.) or they apply them in ways that seem unfortunate to me.
There are much more bad programmers than good programmers, so any language that is sufficiently widely used is necessarily a language mostly used by bad programmers. (Also, if the programming language is Turing-complete, it also means that you can reinvent any historical bad programming practices in that language.) On the other hand, there are often genuine mistakes in the language design, or in the standard libraries. So here is my opinion on which is which in Java:
precise and concise—sorry, no can do. Using proper formatting, merely declaring a read-only integer property of a class will cost you five lines not including whitespace (1 line declaration, 1 line assignment in constructor, 3 lines read accessor). (EDIT: Removed an obsolete info.)
carefully encapsulate state—that’s what the “private” and “public” keywords are for. I don’t quite understand what could be the problem here (other than bad programmers not using these keywords; or the verbosity).
make small reusable modular parts which are usually pure functions—this is not how Java is typically used, but it can be done. It has the garbage collector. It has immutable types; and for the mutable ones, you could create an immutable wrapper class (yes, a lot of writing again). So you can write a module that gets immutable values as inputs, returns them as outputs, which is more or less what you want. The only problem is that “immutability” is not recognized by the language; you only know that a class is immutable by reading the documentation or looking at the source code; you cannot have the compiler check it for you.
REPL-driven development—it could be technically possible to make an interactive functional shell, and maybe someone already did it. But that’s definitely not how Java is typically used. A slightly more traditional solution, although not exactly what you want, would be to use the Groovy language for the interactive shell. (Groovy is more or less a “scripting Java”. Very similar to Java, with minor differences; can directly call functions from the Java program it is included in.) The traditional solution is to do unit testing with JUnit.
As a beginner, avoid Java EE like hell. That is the really ugly part. Stay with Java SE until the Stockholm syndrome kicks in and you develop feelings for Java, or until you decide you do not want to go this way.
Feel free to give me a short example in other programming language or pseudocode, and I will try to write it in Java in a functional-ish style.
I might try Groovy for the REPL stuff—I was trying Clojure before, but I ran into problems getting it to get the dependencies and stuff all into the REPL (I work on a big project that uses Gradle as a build system, and Clojure doesn’t usually use Gradle.)
carefully encapsulate state—that’s what the “private” and “public” keywords are for. I don’t quite understand what could be the problem here (other than bad programmers not using these keywords; or the verbosity).
One pattern I have in mind here: if I have some algorithm I have to perform that has some intermediate state, I will break it down into some functions, pass the state around from function to function as necessary, and wind up composing five or six functions to get from the start to the end. Java programmers seem to often instead do something like make a class with the five or six functions as methods, and then set all the state as fields on the class, which the methods then initialize, mutate, and consume, and call the methods in the right order to get from the start to the end. That seems a lot harder for me to read because unless there is also great documentation I have to read very closely to understand the contract of each individual method.
(I’m also incidentally confused about when in Java people like to use a dependency injection tool like Guice and when people like to pass dependencies explicitly. I don’t think I understand the motivation for the tool yet.)
Java programmers are usually familiar with procedural programming, not functional. The older ones are probably former C/C++ programmers, so they mostly write C/C++ code using Java syntax. That probably includes most textbook authors.
Nothing in Java prevents you from having intermediate states, and composing the functions. You just have to specify the data type for each intermediate state, which may require creating a new class (which is a lot of typing), or typing something like Pair, List>, so yeah, there are inconveniences.
As a crazy creative solution, I could imagine wrapping the “class with the five or six functions” into multiple interfaces. Something like this:
(e.g. be precise and concise, carefully encapsulate state, make small reusable modular parts which are usually pure functions, REPL-driven development, etc. etc.)
I am a Java programmer, and I believe in those principles, with some caveats:
Java is verbose. But within the constraints of the language, you should still be as concise as possible.
Encapsulation and reusable modular design is a central goal of the language and OO design in general. I think Java achieves the goal to a significant degree.
Instead of using a REPL, you do edit/compile/run loops. So you get two layers of feedback, one from the compiler and the other from the program itself.
Even though Java doesn’t emphasize functional concepts, you can still use those concepts in Java. For example, you can easily make objects immutable just by supplying only a constructor and no mutator methods (I use this trick regularly).
Java 8 is really a big step forward: we can now use default interface methods (i.e. mixins) and lambda syntax with collection operations.
I don’t understand how to like it yet
My feeling towards Java is just that it’s a very reliable old workhorse. It does what I want it to do, consistently, without many major screwups. In this sense it compares very strongly to other technology tools like MySQL (what, an ALTER TABLE is a full table copy? What if the table is very large?) and even Unix (why can’t I do some variant of ls piped through cut to get just the file sizes of all the files in a directory?)
why can’t I do some variant of ls piped through cut to get just the file sizes of all the files in a directory?
Nerd sniped. After some fiddling, the problem with ls | cut is that cut in delimiter mode treats multiple spaces in a row as multiple delimiters. You could put cut in bytes or character mode instead, but then you have the problem that ls uses “as much as necessary” spacing, which means that if the largest file in your directory needs one more digit to represent then ls will push everything to the right one more digit.
If you want to handle ls output then awk would be easier, because it collapses multiple successive delimiters [1] but normally I’d just use du [2]. Though I have a vague memory that du and ls -l define file size differently.
(This doesn’t counter your point at all—unix tools are kind of a mess—but I was curious.)
Your vague memory is probably that ls -l gives file size, while du give “disk usage”—the number of blocks used. On my computer the blocksize is 4k, so du only reports multiples of this size. (In particular, the default behavior is to report units of historical blocksize, so it only reports multiples of 8.)
A huge difference that I doubt you forget is how they define the size of directories—just metadata vs recursively. But that means that du is expensive. I use it all the time, but not everywhere.
I’m a programmer with a fair amount of reasonably diverse experience, e.g. C, C#, F#, Python, Racket, Clojure and I’m just now trying to learn how to write good Java. I think I understand most of the language, but I don’t understand how to like it yet. Most Java programmers seem to basically not believe in many of the ways I have learned to write good software (e.g. be precise and concise, carefully encapsulate state, make small reusable modular parts which are usually pure functions, REPL-driven development, etc. etc.) or they apply them in ways that seem unfortunate to me. However, I feel foolish jumping to the popular conclusion that they are bad and wrong.
I would really like a book—or heck, just a blog post—which is like “Java for Functional Programmers” that bridges the gap for me and talks about how idiomatic Java differs from the style I normally consider good and readable and credibly steelmans the Java way. Most of my coworkers either don’t like the Java style, only know the Java style, or just don’t care very much about this kind of aesthetic stuff, so none of them have been very good at explaining to me how to think about it.
Does this book exist?
I don’t know Java books, but I would like to react to this part anyway:
There are much more bad programmers than good programmers, so any language that is sufficiently widely used is necessarily a language mostly used by bad programmers. (Also, if the programming language is Turing-complete, it also means that you can reinvent any historical bad programming practices in that language.) On the other hand, there are often genuine mistakes in the language design, or in the standard libraries. So here is my opinion on which is which in Java:
precise and concise—sorry, no can do. Using proper formatting, merely declaring a read-only integer property of a class will cost you five lines not including whitespace (1 line declaration, 1 line assignment in constructor, 3 lines read accessor). (EDIT: Removed an obsolete info.)
carefully encapsulate state—that’s what the “private” and “public” keywords are for. I don’t quite understand what could be the problem here (other than bad programmers not using these keywords; or the verbosity).
make small reusable modular parts which are usually pure functions—this is not how Java is typically used, but it can be done. It has the garbage collector. It has immutable types; and for the mutable ones, you could create an immutable wrapper class (yes, a lot of writing again). So you can write a module that gets immutable values as inputs, returns them as outputs, which is more or less what you want. The only problem is that “immutability” is not recognized by the language; you only know that a class is immutable by reading the documentation or looking at the source code; you cannot have the compiler check it for you.
REPL-driven development—it could be technically possible to make an interactive functional shell, and maybe someone already did it. But that’s definitely not how Java is typically used. A slightly more traditional solution, although not exactly what you want, would be to use the Groovy language for the interactive shell. (Groovy is more or less a “scripting Java”. Very similar to Java, with minor differences; can directly call functions from the Java program it is included in.) The traditional solution is to do unit testing with JUnit.
As a beginner, avoid Java EE like hell. That is the really ugly part. Stay with Java SE until the Stockholm syndrome kicks in and you develop feelings for Java, or until you decide you do not want to go this way.
Feel free to give me a short example in other programming language or pseudocode, and I will try to write it in Java in a functional-ish style.
Lambda syntax is definitely present in the currently available version of Java. I use it on a daily basis.
Oops. The version is out there for almost a year. I missed it, because we do not use it at work.
Embarassing to find this out after pretending to be a Java expert. Does not add much credibility. :D
No worries, you obviously know what you’re talking about in general. I just wanted to make sure false impressions don’t spread.
I might try Groovy for the REPL stuff—I was trying Clojure before, but I ran into problems getting it to get the dependencies and stuff all into the REPL (I work on a big project that uses Gradle as a build system, and Clojure doesn’t usually use Gradle.)
One pattern I have in mind here: if I have some algorithm I have to perform that has some intermediate state, I will break it down into some functions, pass the state around from function to function as necessary, and wind up composing five or six functions to get from the start to the end. Java programmers seem to often instead do something like make a class with the five or six functions as methods, and then set all the state as fields on the class, which the methods then initialize, mutate, and consume, and call the methods in the right order to get from the start to the end. That seems a lot harder for me to read because unless there is also great documentation I have to read very closely to understand the contract of each individual method.
(I’m also incidentally confused about when in Java people like to use a dependency injection tool like Guice and when people like to pass dependencies explicitly. I don’t think I understand the motivation for the tool yet.)
Java programmers are usually familiar with procedural programming, not functional. The older ones are probably former C/C++ programmers, so they mostly write C/C++ code using Java syntax. That probably includes most textbook authors.
Nothing in Java prevents you from having intermediate states, and composing the functions. You just have to specify the data type for each intermediate state, which may require creating a new class (which is a lot of typing), or typing something like Pair, List>, so yeah, there are inconveniences.
As a crazy creative solution, I could imagine wrapping the “class with the five or six functions” into multiple interfaces. Something like this:
Old version:
New version:
This would force users to write things like:
I also admit my colleagues would kill me after doing this, and the jury would probably free them.
I am a Java programmer, and I believe in those principles, with some caveats:
Java is verbose. But within the constraints of the language, you should still be as concise as possible.
Encapsulation and reusable modular design is a central goal of the language and OO design in general. I think Java achieves the goal to a significant degree.
Instead of using a REPL, you do edit/compile/run loops. So you get two layers of feedback, one from the compiler and the other from the program itself.
Even though Java doesn’t emphasize functional concepts, you can still use those concepts in Java. For example, you can easily make objects immutable just by supplying only a constructor and no mutator methods (I use this trick regularly).
Java 8 is really a big step forward: we can now use default interface methods (i.e. mixins) and lambda syntax with collection operations.
My feeling towards Java is just that it’s a very reliable old workhorse. It does what I want it to do, consistently, without many major screwups. In this sense it compares very strongly to other technology tools like MySQL (what, an ALTER TABLE is a full table copy? What if the table is very large?) and even Unix (why can’t I do some variant of ls piped through cut to get just the file sizes of all the files in a directory?)
Nerd sniped. After some fiddling, the problem with
ls | cut
is that cut in delimiter mode treats multiple spaces in a row as multiple delimiters. You could put cut in bytes or character mode instead, but then you have the problem that ls uses “as much as necessary” spacing, which means that if the largest file in your directory needs one more digit to represent then ls will push everything to the right one more digit.If you want to handle ls output then awk would be easier, because it collapses multiple successive delimiters [1] but normally I’d just use du [2]. Though I have a vague memory that du and ls -l define file size differently.
(This doesn’t counter your point at all—unix tools are kind of a mess—but I was curious.)
[1] ls -l | awk ‘{print $5}’ [2] du -hs *
Your vague memory is probably that ls -l gives file size, while du give “disk usage”—the number of blocks used. On my computer the blocksize is 4k, so du only reports multiples of this size. (In particular, the default behavior is to report units of historical blocksize, so it only reports multiples of 8.)
A huge difference that I doubt you forget is how they define the size of directories—just metadata vs recursively. But that means that du is expensive. I use it all the time, but not everywhere.