I’m skeptical of the value in doing this. There are a mountain of tools like NullAway, ErrorProne, Immutables that make it so much easier to write safe code in Java. New developments in the language like first-class record types improve the DX as well.
I think Kotlin helped push Java in the right direction, but at this point it seems like a weaker choice. Spending years to migrate a massive Java code base like this feels like wasted time.
Java did improve over the years. But it still defaults to doing basically everything wrong by default. Parameters are non final, classes are open, things are mutable, etc. It's unsafe by default. And it's hard to fix because it breaks backwards compatibility. Kotlin doesn't have that problem and it has been doing the right thing by default for a long time. It's still getting better.
I think on Android the choices are either to start from scratch or to migrate to Kotlin for Facebook. Sticking with Java on Android just makes everything harder. All the new frameworks are Kotlin centric. Most Android developers they'd be hiring would have been using Kotlin for years. Etc. So, I can see why Facebook is making the effort to keep their code base alive and future proof. Android is more Kotlin focused and has been for years. And that's only going to be more the case now that compose multi platform is a thing.
Btw, most of the stuff you mention is probably more applicable to server side Java and less to Android. IMHO, Spring + Kotlin is a very nice combo on servers. Very well supported by Spring and they bundle a lot of convenient things for Kotlin. And I don't need any of the tools you mention because it does the right thing by default.
> And I don't need any of the tools you mention because it does the right thing by default.
I mostly agree with you, but Spring+Kotlin does require the allopen plugin, so it's kind of hacking Kotlin to do the wrong thing (all classes open) in order to support an arguably bad design choice by Spring.
I, personally, happen to like writing in Kotlin more than Java - with a lot of experience in both (though admittedly, all? the Java I wrote is in the pre-Streams style).
I like:
* data class Foo(val theImplementationAndStyleOfDataClasses: String)
* elvis?.operator?.chaining
* the order of variable declaration (type afterward)
* how function pointers at the end of a method call can be auto-inserted as the next curly bracket
* how you can have fun foo() = returnValue; in one line.
* fun `method names that can have spaces - and dashes - in them`()
The preceding 3, combined, allow for:
@Test
fun `much easier testing`() = commonTestSetupWrapper {
// the code under test
}
* val immutable by default
While I agree that Kotlin has definitely helped push Java in the right direction; and I agree that it's probably not especially necessary to migrate 10MM lines of Java code to Kotlin, especially since they're fully interoperable, I definitely would prefer writing _new_ code in Kotlin over Java for the for-the-lazy devex improvements, if nothing else.
fwiw, my "good at it" programming history follows the lineage in historical order of:
* Java (8?? years of it including competition programming)
* C# (5+ years)
* Python (2016ish to current)
* Ruby (3-ish years, lots of Rails)
* Kotlin (2-3 years, through current - written over 40k lines of Kotlin in the last year, alone)
`method names that can have spaces - and dashes - in them`
Eugh, that is a turnoff. Maybe I have programmer Stockholm’s, but at least with a single connected word, I can always double click to select the token. Maybe, I might have wanted dashes at some point, but spaces seem like a step way too far.
The feature isn't just about spaces. It's also how you'd work around interacting with JVM code that uses what would be Kotlin keywords. For instance, Java classes with a method named "is" or "val" can be called through
val isBar = foo.`is`(bar)
rather than needing to use reflection like you would to accomplish the same in Java.
The Kotlin style guide basically says "never use this outside of test names" and some targets, such as old Android, don't even support the feature in test cases.
> at least with a single connected word, I can always double click to select the token
I'd expect an IDE for Kotlin to detect `stuff like this` and select the entire name. Considering Kotlin came from JetBrains, IDEs are most likely already in the Kotlin community's DNA.
(i never wrote or anything related to Kotlin myself but i do appreciate IDEs helping with stuff like this)
What benefit do they provide in testing scenarios? I've never written Kotlin, but from an outsider's perspective, it seems like a slim benefit, outweighed by the cost of the mere existence of this syntactical oddity in the language's grammar.
When writing tests, you can name the methods more useful things, such as:
class MyTestableClass {
fun `methodName - when input does not parse to valid regex throw exception`() {
}
}
It's pretty clear what is under test in a situation like that. That's basically the only situation I ever see it used in (and would code-smell the heck out of it if I saw it in other circumstances).
People who are familiar with RSpec-style testing are very used to this sort of thing.
describe MyTestableClass do
context 'input parsing issues' do
context 'when not valid regex' do
it 'throws exception' do
...
end
end
end
end
Anecdotally, I've also found that such style naming for tests allows me to write out the desired names for all the tests ahead of time more easily and then implement them. That happens to be my flow.
This study only compared camelCase to snake_case and what the results are mixed, depends on what you call "hard to read". They only seem to have tested finding identifiers from a word cloud, and it did take more time to read camelCase identifiers (although they were also resulted in more accuracy).
It's important to note that the researchers only tested short phrases with 2 and 3 words. "getNextPath" is a different beast compared to "methodName_whenInputDoesNotParseToValidRegexThrowException".
On the other hand, there is a good body of research that generally shows that proper sentences (not just 2-3 words) without spaces are harder to read, at least for English speakers[1]. Early medieval Latin manuscripts did not use space between words[2]. The fact that spaces were seen as an improvement when they were introduced, is probably telling.
[2] Roman-era inscriptions did use an interpunct (a small middle dot) to separate words, but this practice fell out of fashion in Late Antiquity and Early Middle Ages and I'm not sure if it was ever the norm in handwriting.
Sure, but I like writing the other more. It's entirely a style thing and you're not required to do it :) it's also basically only ever seen in tests :)
That doesn’t seem to be the case based on the study. And normal for one thing, such as paragraphs of prose, might not translate to another thing, such as a sequence of words in a line of code.
I like how tests are made in Python - you don't even need classes, just functions, and use a single assert keyword for everything. Also it's easy to parametrize them using decorators.
Also, once you get used to assertj, you'll never want single-assert again. It's fantastically useful to know (for example) not just that two lists aren't equal, but specifically what elements differ.
Parameterized tests are a huge, huge win, yeah. I am a big fan of them and look for how to do them in every test framework I use. RSpec was the weirdest for me; but NUnit and JUnit have been quite easy, and I'm not surprised at all they're easy in Python too (admittedly, I don't remember if I ever wrote them in that language).
It reminds me of Spock tests written in Groovy. I have to help maintain a code base like that now at work where a Groovy zealot (who has moved on) convinced the teams to write tests in Groovy instead of Java.
When a test fails, the method name describes what it was supposed to do written out like a sentence enclosed in double quotes which seems like a win but not much of one.
When you need to add a new test or analyze if an existing test needs to be changed, you have to eyeball all the code in the test class because even with methods named with spaces, it's not always indicative of what it does.
With Java, I can sort a list of method names and have a better idea immediately what needs to be updated or added.
It's a place to put intetion that isn't so much of a second class citizen that it will likely be ignored and/or not updated if it ceases to be true. You want your test methods fine grained (many methods that each check one variation, instead of one method that checks a whole list of them) and every extra redundancy (method naming vs the variation of data actually passed vs documentation) increases the the chance of those redundancies getting out of sync.
In fact I've occasionally found myself writing reflection code to parse the test method name as input just to avoid that problem altogether (for name and data, not for documentation). And that was in plain Java even, the pattern could be far more useful with that kotlin naming freedom.
Isn't this breaking Jakob's law of the rest of the tooling though? I can se flows borking with something like "x failed at expect" in a majority of reporting tools not specifically meant to deal with this spaces in functions stuff.
As others have said, I would not expectany any style guideline to tolerate those names outside of tests. But it's not entirely test-only: When you are dealing with code generation for schema defined in/for other languages, being able to just drop names in backticks and be done with it would be far less hassle than making up your own transformation and deciding between verbosity and safety from collisions.
While that is avoidable by sticking to a coding style, that horrid and unavoidable 'fun' instead of 'function' (or 'fn') just saps my will to work on such a codebase.
When it's "sleeping" code, not as in unused but as in not currently being worked on (no changes in open branches), then it can be quite valuable to have some bulk translation run while the code actually is sleeping, and not at some future time when there'll likely be a whole burst of activity happening in parallel. Repo host products might actually add "transform while sleeping" a possible feature, with their cross-branch knowledge, and perhaps some history mangling for a best effort approach to retaining some of th knowledge available through blame through the conversion.
As for Kotlin in general, I agree with your list. I really enjoy the way Kotlin improves the dev experience not so much with audacious new (or "new to a java-like environment", of course I'm looking at you, Scala) concepts but with small trivialities like being able to assign the outcome of an expression chain to a name without polluting that name's scope with intermediates that really don't have any purpose after the result of that chain is assgned.
And I don't hold it against Java that it does not follow that path (it focuses on more impactful changes), I would consider it almost out of character if it introduces e.g. .let. The only thing I'm a bit torn about when it comes to Kotlin is wether the "this" variants of scope functions were a good idea. They are certainly part of kotlin, and some "DSLy" styles would really not be the same without them, but if I were to pull what I like about kotlin into other languages I'd probably introduce let and apply (and the this-less run) and skip run/with/apply (assuming that the target language even has a this to run/with/apply on)
> * data class Foo(val theImplementationAndStyleOfDataClasses: String)
For the purists there are records now in Java (which can be combined with destructuring / pattern matching - there were significant improvements in this area in the last few JDK releases, and more are coming!). In this regard Java had surpassed Kotlin by some margin.
For the enterprise coders, there is the Immutables project (https://immutables.org) which you can say is Lombok on steroids (but implemented correctly, that is, by generating classes which you can inspect as regular source code, instead of relying on hacking the compiler...).
> * elvis?.operator?.chaining
This will hopefully be finally resolved by one of the subprojects of project Valhalla, as a prerequisite for value class support (which cannot be null, so...).
The others on your list are small and subjective stylistic differences, of course your preference may vary, but should not weigh heavily in any Java vs Kotlin discussion.
> This will hopefully be finally resolved by one of the subprojects of project Valhalla
I think we'll see them come in through https://openjdk.org/jeps/8303099 although I'm not a huge fan of the approach chosen (tri state nullability: explicitly nullable, explicitly non-nullable, legacy code that should just be treated as nullable).
Except for native support for reification, almost all of Kotlin can be reimplemented by Java if you wait long enough and stack enough class annotations. It's all stylistic up to a certain point, but the classic Java style combined with the glacial improvement process is one reason why some people want to use a different language.
I think I'll have an easier time convincing someone to consider Kotlin than I'll have convincing someone to add Immutables+Manifold to the dependency chain. You end up writing Java that's not really Java like, but also not something that there's a good standard style for, and that usually ends up in a mess.
I'm glad with pattern matching, Java is now pushing Kotlin to improve rather than the other way around. I'll hope to use those features one day, when companies finally upgrade from Java 11 to Java 17 in five or ten years.
Java records do not have a copy method (or with clause or whatever equivalent), and there are no keyword arguments.
This makes Java records extremely painful to use for any data has more than 3 fields or so. You could use them together with an annotation processor like RecordBuilder (or Immutables or Lombok, but I don't think they support records?), but that's just not pure Java. Annotation processors that generate byte code always come with their own complexity.
Kotlin data classes have their own warts (order-based destructuring can be dangerous and the copy() method is a bit hacky) but the overall ergonomics for large value objects are better than Java records at this point.
Lombok supports `@With` on records, which adds wither methods. And there are ongoing JEP discussions on how to support this natively in Java.
Lombok is so ubiquitous today that I effectively consider it part of the language. And even with Lombok, the Java toolchain is so much faster, easier to use, and more stable than Kotlin.
Kotlin is a great language, marginally better than Java, but I'm not sure it's worth enduring the toolchain. And Java keeps improving.
If you think Kotlin is marginally better than Java, you have a superficial understanding of Kotlin. Kotlin is vastly better than Java, which isn't surprising because Kotlin was designed 15 years later with the explicit goal of being a better Java and fixing as many of Java's design mistakes as possible. For example, the designers of Kotlin went through the book "Effective Java" and tried to avoid the listed pitfalls by design.
Java will never get close to Kotlin because even though Java can, and thankfully does, still improve, it's too late to revise many of it design decisions, and large language features often need to be considered in a language's design from the start.
On Android, Kotlin is an even bigger win because Android uses an old Java version and has become Kotlin-first.
In my opinion, Java will remain the best choice for libraries that target multiple JVM languages. For everything else, I use Kotlin without thinking twice. Kotlin 2.0 laid the groundwork for toolchain improvements, which have indeed been a long-standing pain point.
> pattern matching ... In this regard Java had surpassed Kotlin by some margin.
I did just glance at Java's pattern matching; and yeah, it does look like it is a bit more powerful than Kotlin's pattern matching in that it can, at the same time, look for both type and a conditional. That's relatively neat - not something I've personally needed / wanted; but neat just the same :)
The JVM is a really nice virtual machine in how it lets us use both of these languages fully interchangeably to pick the one we like more.
I'm glad Java has been investing in some of these areas, too. Everyone improves when paradigms and new learnings are distilled.
I like Kotlin, but what I hate is that in the meantime I migrated all my programming to Neovim and Helix. All programming? No, not Kotlin, because the LSP isn't there and JetBrains clearly has no interest, they want to sell their IDEs.
Now don't get me wrong, I have an IntelliJ license since 10 years, even back when I was employed and paid it out of my own pocket.
It's not about the price. I would gladly pay for the LSP implementation. But I don't want to use IntelliJ anymore.
So a new project on the JVM where I have a say in the stack? Java or maybe Scala. No more Kotlin.
Honestly, none of those differences you listed seems especially compelling to me, except possibly for the ?. operator.
What would be compelling: Array types that don't hate generics, or generic collection types that don't hate primitive types. Does Kotlin improve on Java at all here? It's such a pain having to remember this bonus complexity when all I want is a consistent way to manage a bunch of things. (I suspect not, as I think the limitations are at the JVM level, but I don't know.)
Kotlin does improve on the primitives/boxed situation. For example in Kotlin there is just "Int", not both "int" and "Integer". The language will use stack-allocated primitives wherever possible and boxed objects when necessary (in Collection generics or when you've explicitly opted into nullability). The distinction is abstracted away from you as a programmer. Sounds like the consistency you want.
10+ years in the making, at this year's Devoxx Brian Goetz said that they are almost ready with the design of Project Valhalla and can start JEP-ing it in future JDK releases. No timeline yet, so we probably will not see it until the next-after-next LTS release (29?)... But Java, like Cobol, is not going anywhere, so the wait will be worth it.
Great, though is that perhaps orthogonal to my complaints? My vague understanding is that Valhalla is about transparently allocating local objects on the stack instead of the heap when possible (i.e., when escape analysis confirms that they can't "get out" of the method that declared them), the way primitives are now.
But these are the same problems! I'm not intimately familiar with the current state and internals of project Valhalla, but they were proposing `primitive classes` backed by primitive types, allowing said class to be used as a type in generic code. Moreover, primitives would become ValueObjects, so you would have your int[] which would also be an Object[]. In other words, the difference between primitives and objects would be much smaller in terms of the language.
Bottom line is, you could have your cake and eat it too - have an int[] that looks like a List<Int>, and vice versa, while retaining the performance characteristics of an array.
Thanks, I'd thought that the goal was to reimplement aspects of the compiler+JVM to preserve existing language semantics but be more performant, but from what you've written I take it that the goal is actually to expand the language semantics themselves, in a way that would bring primitive types and objects (and thus generics) closer together.
Stripping out Maps/Lists with boxed keys and values is the first easy thing to do when perf tuning a piece of code. It's so frequently impactful that it's worth doing immediately without any measurement.
You just swap them out with fastutils or trove and call it a day.
I think you're right, that's probably the best (lowest mental overhead, fewest footguns) way -- always use a List instead of a raw array, and always box primitive types. This also avoids the parallel explosion of Stream types and methods (IntStream, mapToInt(), etc. (and the fact that there's no BooleanStream or ByteStream, etc.)).
That said, I tend to get bitten by the fact that == on 2 boxed numeric values is roughly as misleading as possible -- it does reference comparison, but interns small integer values only, so (Integer)42 == (Integer)42, but (Integer)420 != (Integer)420. (So the small values you probably used in your unit tests will appear to work just fine with your broken, ==-using code.)
ETA: Deleted a misplaced "new". I also don't love that, e.g., ++x does a heap allocation + deallocation if x is boxed (since boxed types are immutable). But usually I care more about the cycles I spend coming up with correct code than cycles the CPU spends executing it.
The value in the conversion of existing code in this particular case isn't 100% clear to me either, but I think calling Kotlin a weaker choice than Java at this time is naive, particularly when preceding that with "there are a mountain of tools" that you can bolt on to Java to give it features that are built in to Kotlin.
What makes Kotlin such a strong choice for many orgs today is its batteries-included multiplatform capability. We are able to write data models, validation logic, business logic, etc just once and compile to JVM, WASM, and native targets. For orgs with very large codebases and frontend applications (web + iOS + Android) this is an attractive capability because we can have a single codebase for a ton of core functionality, and have each frontend import a library which is native to its own platform.
Of course many technologies with this promise have come and gone over the years, but this is the first one with a strong backing that has allowed us to _natively_ interoperate with each target platform.
I believe these are all driving factors that have been pushing well known companies, that were previously Java shops, toward Kotlin. If you speak to a broad range of people in the industry you'll find many more orgs moving from Java to Kotlin than from Kotlin back to Java. We can simply get more work done with less code and ship to all our frontend platforms, and unless Java can do the same, I don't see the industry moving in that direction.
It’s like marriage. Yes there are tools that can give you most of the same rights and privileges as marriage, or you could just get married and get all of that for the cost of writing one check and signing some papers. No lawyers. Done. Move on with your life.
The question of optional things is always laid out as if the choices of my coworkers do not exist. Are you expecting me to work solo, or to be a bloody tyrant who pushes through all of my own ideas over the objections of an entire team? These are some of the most antisocial people in any debate on software. No I don’t get to just chose to use or ignore a tool in an ecosystem. That’s why picking a system with batteries included is a simpler transaction. I can go to a new company and have expectations, not arguments.
> What makes Kotlin such a strong choice for many orgs today is its batteries-included multiplatform capability. We are able to write data models, validation logic, business logic, etc just once and compile to JVM, WASM, and native targets.
Not familiar with Kotlin but how does that work? Does it come included with a PAL? Because it you want to be platform agnostic, you can't for instance use a Java RegularExpression in your platform agnostic code.
The PAL (Platform Abstraction Layer I assume) is just the stdlib that is provided. The stdlib is not the same for all platforms, as can be seen in the documentation. A regex implementation is provided for all platforms, but is not quite the same on all platforms: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/-r...
In shared code you can define interfaces that have to be implemented by any platform you use.
As long as they're already writing new code in Kotlin, translating the existing code makes a ton of sense, if they can do it cost effectively (which is sounds like they did).
One of the huge problems with a language migration is that you're left with old and new and all the parallel tooling, context switching, and impedance mismatches. There's often a claim that the existing code will be migrated eventually, but IME that doesn't actually happen with large code bases. Then if there's ever a third migration, you're left with three languages in play.
It's much better to aim for 100% migration, as close to 100% automated as possible. Then when you're done, you're really done. Maintenance, training, and the next migration will be easier.
There are a lot of nice things I enjoyed when I was still working with Kotlin extensively. Null safety, data class, sealed class, exhaustive when, top level functions, object class, higher order function, extension functions etc. They fundamentally change the way dev think, producing safer, lighter and more maintainable code.
Contrary to the popular notion of dismissing certain syntax differences as sugar, I consider it one of the most important factors simply because we spend more time reading than writing code. To me Java has always been verbose and dreadful to read, there's something fundamental wrong if you need your IDE generate so much then force to train your eyes to skip over most of them while reading.
I find Kotlin to be more elegant and fluent especially with native syntax support of the above features. I can read it at least 25% faster than Java. Perhaps which one is better is personal taste, but I'd maintain syntax is very important, just like natural languages.
Especially because outside Android, Kotlin Virtual Machine will never happen, it will always be a guest language on the Java ecosystem.
Nothing on Kotlin will ever matter on the decisions taken by Oracle, IBM, Amazon, Azul, PTC, Aicas, microEJ, Microsoft, Red-Hat,.... on their JVM/JDK implementations and related tooling.
Yeah Facebook was (in)famous for autogenerated code in their apps and frameworks. In fact early on they had to do some hack on Android since they had more classes in their FB app than was supported by the operating system lol.
So super inefficient binaries, but I guess more efficient to develop (or I assume that was the idea)?
You are getting the details wrong (I was there). This was the single DEX limit, and Google would just bump it in AOSP every time their own apps hit it (as their apps didn't support older OS versions). FB at the time was supporting back to froyo, which had a much lower limit than "modern" apps needed. See this note for more info: https://www.facebook.com/notes/10158791578712200/
By the Dalvik Virtual Machine (DVM). 65k method limit is what Facebook hit. tbf, DVM was engineered to run on embedded systems with 64MiB RAM (as a replacement for J2ME).
Most important reason for the rebirth of golang (after it's initial dip in early '10s) and wide spread adoption of Kotlin is the ownership of Java by Oracle. It doesn't matter what fine print exists, but everyone knows what kind of a*holes Oracle and Larry Ellison are, they will find a way to squeeze you. Everyone is going to sprint away from Java as soon and as much as possible. For someone like FB, Kotlin being a nicer language is just cherry on the cake.
I would argue that Scala drove Kotlin. And over the last 10 years at least, it's been Kotlin driving Java.
What I saw happen was Kotlin taking over the mantle of "better java" from Scala, and aiming for an 80/20 type language compared to Scala. And for the most part, it's those 80% that are finding their way into Java.
Many of Kotlin's profound improvements will never find their way into Java because it's 25 years too late to do so. Java improves where it still can, which is the right thing to do. Sometimes Java even manages to improve on Kotlin because now it can learn from Kotlin and other languages. Nevertheless, it's impossible for Java to catch up to Kotlin, which was designed 15 years later with the explicit goal of fixing as many of Java's design mistakes as feasible for a JVM language.
The legal issues around Java between Alphabet and Oracle are settled at this point and no longer a risk for third-party software vendors. But it's pretty clear that Android is moving away from Java, so anyone with a strategic commitment to that platform has to plan around that reality.
It's safe to say that Android is definitely moving away from Java in terms of new language features. I mean, if you want to support older Android versions and use modern Java features or newer parts of standard libraries, you'll usually have to rely on desugaring or making sure you're using classes that are supported in Android.
IMO, Android is moving away from modern versions of Java. Java and its underlying standard library will always play a big role in Android development.
The way I see it, Kotlin makes a lot of sense for Android development because Kotlin can introduce new things in its standard library that make older versions of Java look ancient. It's not like you can use new Java features in Android, so using Kotlin makes people not care as much about new features of Java that are only available in modern versions of Java.
Android folks were forced to acknowledge modern Java, otherwise they would lose access to Maven Central ecosystem, as everyone adopts newer Java versions.
Hence ART is updatable via Play Store since Android 12, and is currently on Java 17 LTS.
unless you're not using the jvm (which is owned by oracle, despite being opensource), you won't have any difference between kotlin and java from a legal perspective tbh.
Meta operates at such a large scale that the engineering management decision process becomes qualitatively different from smaller organizations. They can justify enormous investments in keeping their code base fresh for small improvements in productivity and quality.
The nice thing about null-safety in Kotlin is that it is built-in and it requires no extra annotations or added tooling. Not having @ProxySingletonAbstractJunkFactoryFactoryBean sprinkled all over your code does it ever so slightly more readable.
But if built-in null safety and lower verbosity was all that Kotlin had to offer I doubt it would have won. Kotlin offers a lot more features that Java does and most probably will not offer in the next 10 years:
- Extension methods: No more terrible FooUtil or BarHelper classes.
- Context parameters (preview feature)
- Coroutines (they are not just about concurrency - you can use them to write generators as well[1]).
- Keyword arguments: This makes using constructing data classes possible without generating builders, and in general lets you deal with multi-argument methods in a safer and more readable way. It also cuts down the boilerplate of defining 20 different overloads that call each other.
- Interface Implementation by delegation: This feature makes it easy and painless to follow the motto "composition over inheritance". Java makes implementation inheritance 50 orders of magnitude easier than composition with manual delegation, so it's pretty natural that the vast majority of Java code- bases overuse inheritance in very bad ways.
- Properties: This reduces the need to worry about direct field access causing API breakage in the future, and removes the need for the absolutely massive boilerplate associated with getters and setters in Java. It's true that records remove the need for that, but records only work for purely immutable data classes (and right now they are only practically usable with a small number of fields).
- Delegated Properties: Not something I use every day, but it's very useful when I do use it.
- Unsigned integers (specifically bytes): Writing any byte-wrangling code in Java is a downright nightmare.
- Reified Generics: Java may get something equivalent to that with Project Valhalla, but I'm not sure how comprehensive it would be.
- If expression: Java made switch an expression with JEP 361, but if is still a statement. This leads to a lot of bloated and somewhat unsafe code where you have to define a variable before the if and then assign to he variable in each branch.
- Expression Bodies for function: This is a very small feature, but it's pretty nice to cut down boilerplate and make functions more readable when you need it.
DSL Features
------------
Kotlin is perfect for DSLs thanks to features like:
- Closure blocks: Kotlin lets you pass the last closure argument to a function as a block of code inside curly braces that follows the function call. This features lends itself very well to readable DSL. In Java you would need to embed the closure inside the function argument list and add an extra parentheses. This gives you some of (see the next couple of points) the DSL capabilities of Kotlin, but the DSL becomes very hard to read and use when a lot of blocks are involved. There is a good reason why Kotlin DSLs are based on blocks, while Java DSLs are based on the less-flexible fluent interface pattern: block-based DSLs are just too hard to read and write in Java.
- Inline functions: Inline functions allow the compiler to perform some optimizations in advance that may be harder for the JIT to do (namely inlining the provided block closures), but the real kicker is that you can use flow control statements (break, continue and return) that affect the calling scope.
- Multiple "this" in context. You can have multiple values for this inside a nested scope. The compiler manages to find the right reference for the "this" alias based on your usage, but in case of ambiguity (where the deepest "this" wins), you can disambiguate your choice. This feature sounds overly complex and unnecessary at first, but it allows a lot of power for the Kotlin DSL system.
- Closures with this receivers: Using the multiple this values from above, you can have closures that receive their own "this" to introduce DSL methods. Being able to introduce these methods without shadowing the "this" from the parent context is crucial for powerful DSLs.
- Infix functions and operator overloading: Needless to say, these features make DSLs even nicer.
I've probably skipped a couple of features I can't recall right now, but I hope that it shows that Kotlin is a lot more than just "Java with null-safety and data classes" as some people think.
> Interface Implementation by delegation: This feature makes it easy and painless to follow the motto "composition over inheritance". Java makes implementation inheritance 50 orders of magnitude easier than composition with manual delegation, so it's pretty natural that the vast majority of Java code- bases overuse inheritance in very bad ways.
While we're on this subject, I'd like to rant for a bit. Why is this not a standard feature in every OO language today? The adage that composition should be preferred to (implementation) inheritance is itself older than Java, and was already fairly common wisdom 20 years ago - yet PL design has seemingly not reflected this.
At my last job, the management greenlighted a full rewrite in Kotlin in order to attract/retain developers bored with Java and Python. The actual business project was boring since all the important design work was already finished by the architect. No language rewrite, no interested devs. So management made a quiet trade with ICs where everyone got what they wanted at the cost of future maintenance.
I learned that social whims (developer fun, preferences , dopamin) are weighted as much as technical rationales (performance, maintenance)
Initially, Kotlin attracted mid-level folks to transfer. I recall a roaming staff engineer who lead the project for a bit, and then wisely rotated outside of the org.
Long-term, the folks who remain are stuck and unhappy. The business got what it needed and moved on, leaving a system half in maintenance-mode, half feature-complete. Any Kotlin-only changes are mostly just bug-fixes or glue work with other team's SDKs. Any new features carry a lot of risk because the few people motivated to drum up new business (and write up requirements) left or quiet-quit.
In a weird way, the project naturally selected for people who would get stuck maintaining the artefacts of the project. It's a paradox that I can't fully comprehend.
On the contrary. The fact that there are two languages that “coexist” means that now we can have politics. The same would not be true if the choices were Java and, say Go, because then it’s a complete rewrite. There would still be the possibility of politics but the engineering decision would be so one sided as to make it difficult to leverage. With Java vs Kotlin, disingenuous folks pretend they are the same thing.
I view this as great news. It will attract those would rather code in Kotlin for whatever reason, to FB, leaving more Java opportunities for the rest of us who like and prefer Java, even after 29 years with it.
Personally I find that it's an interesting indicator of the capability of the programming languages. Moving from language A to B can be extremely easy if B is as powerful or more powerful in terms of expressiveness than A. It can be an absolute horror if it is less powerful.
Being not null-safe in fact brings additional expressiveness. I guess most would argue that it's not a good type expressiveness. Nonetheless it is a type of expressiveness that can cause trouble during such a transition.
In general it feels like Java is making faster progress than Kotlin nowadays. I wonder where Kotlin would be if it weren't for Android. I hope those folks don't have to migrate back before they even finished.
Without Android, Kotlin would just be Cool Alternative Language #4721. Java has been a safe, non-controversial pick for decades. Who is going to endorse writing their hot new project in it just because some IDE uses it?
When Google says they support a technology going forward, that gives the project some cache it otherwise never would have received.
(Android dev at Meta here, though not involved with the Kotlin migration. Opinion below, completely my own)
We have: public and internal apps, tests for those, tons of wrappers around Android APIs, autogenerated infra code, did I mention the API wrappers?, custom layout engines, duplicate code that people wrote because they couldn't be bothered to search the monorepo, code that was copied without much thought because people searched the monorepo and found something, app architecture tools like dependency injectors, annotation processors, custom build tooling, etc
If you can think it, someone has probably committed Java/Kotlin or native code for it already.
> duplicate code that people wrote because they couldn't be bothered to search the monorepo, code that was copied without much thought because people searched the monorepo and found something
This is also a very common way in Object-Oriented code bases to ensure you're not messing anything up by re-using classes. Concatenating yet another class to the code base is the safest and laziest way to ensure you're not possibly changing the behavior of anything else when you inevitably need to add/change something in the class.
Why is this a OOP problem and not a problem in large code-bases in general? I can easily see it happening in functional or imperative languages where one creates an almost identical copy of a function instead of reusing and modifying an existing one.
Wouldn't it make more sense to do a massive refactoring given the situation is as you described? And, if needed, rewrite to Kotlin later. This would just cause the same problems to be reproduced in a different language.
A rewrite, too, would take less than years, would have a better end result. Honestly the FB application is not exactly rocket science, and the massive amount of code they have points more on the direction of broken internal process and a total lack of simplicity in the design and choices.
I don't think people realize that "Android" at Meta is not just the "Facebook" app, but also: WhatsApp, WhatsApp for business, Instagram, the whole Oculus layer on top of AOSP, whatever they use for Ray Bans, etc.
As a consumer you're only ever seeing the tip of the iceberg of Meta apps. There are at least 3 major user groups: The consumers, the producers, and the advertisers, and each of them is at least as complex as the others. Then you have to consider the global audiences, and that e.g. ads are handled very differently in the EU than in North America, and that needs to be accounted for.
There's a lot of subtlety in what exactly a "line" is, especially for Java and especially for a legacy enterprise codebase: hard to say that Meta's 10M is actually twice as big as someone else's 5M.
I have no clue what's in their code, but I would expect a lot of "almost redundant" stuff around specific Android versions, manufacturers, etc, which can really pile up.
Java's how you wrote Android apps before Kotlin came out. I expect they have __all their existing Android code__ in Java. 10MM lines doesn't seem out of line for a very, very established company with 100k developers across several products. It's one of the 3 main platforms that people interact with Facebook on and so they'd want it to be as good and as fast as possible, especially on older phones for the time when Android phones were new.
That is mostly a meme. What really adds up is putting every little thing into its own file and having an interface and a (usually trivial) implementation for everything.
One of the things that surprised me in the article was their usage of J2K. They’ve been using it as part of IntelliJ, alright, but why did they have to run it headless? They’ve even mentioned that it was open sourced. And later they’ve said that they were not able to do much improvements because it was on maintenance mode at Jet Brains.
I mean, with the ressources meta has I’m sure they could have rewritten the tool, made a fork or done any other thing to incorporate their changes (they talk about overrides) or transformed the tool into something better fitting their approach. Maybe it has been done, just not clear from the article
Mostly, I find it far less verbose with a couple huge convenience features that I'm constantly using and would feel very lost without; and, they all come as part of the language, rather than bytecode hacks / pre-processors, like lombak
My biggest pet peeve with Kotlin is that they got rid of checked exceptions and replaced them with nothing at all. All error code flow is now just invisible unchecked exceptions, the single worst kind of error handling imaginable.
I know they added the Result type later on, but there's zero convenience to it, so you have to manually handle every error, even if you just want to bubble it.
Checked Exceptions were better, though Rust still has the best error handling in any non-functional language.
Depends on what you're writing I guess (as always). I write backend services and if there is an exception thrown, generally the whole request is forfeit. So a top-level exception handler is all that's needed.. no checked exceptions ruining my lambdas or streams like in Java.
I admit I have no direct experience with it. For my perspective on it, I rely on one of the best developers I've ever worked with - especially when it comes to debugging and deep investigation and she *hates* it. If I had to assume (we don't work together anymore), it's because it did some freaky stuff that made debugging the weird bugs really, really hard.
“More specifically, from JetBrains, the makers of the world-famous IntelliJ IDEA IDE, whose primary claim to fame is its lovely orange, green, purple and black 'Darcula' theme”
This must be a bad attempt at a joke, right? Darcula is a nice theme (I personally prefer high contrast), but surely IntelliJ’s code inspection and automatic refactoring have always been its claim to fame.
> maximize our gains in developer productivity and null safety
Java is to too verbose. Kotlin have features like null safety, better functional programming, better coroutines, advanced sealed classe. Java virtual threads is still not ready and development was very slow. Blame oracle for being too complacent.
Kotlin's null safety is a huge win, but the language has it's own set of flaws such that I try to avoid using it.
The language's "declarative style lambda syntax" makes code overly-complex. Every library is basically it's own DSL and every lambda context has it's own set of specialized keywords. If I write `test { somthing(value) }`, depending on the context, `somthing` could be invoking a higher-order function, calling a method on an object that the lambda is extending, or calling a static method. The muddling of data/declarations and procedures, makes jumping into new codebases confusing and time-consuming. Rich Hickey has pointed out in numerous talks that syntax itself is complex because rules and order are complex, but in languages we generally trade a little complexity for terseness. Kotlin encourages this trade-off far too much in the direction of complexity, and the language would be almost unusable if not for it's IDE support.
Getting to the root of the previous problem is that method extensions in general feel like an anti-pattern. Without introspecting a method call, it's not possible to tell where that functionality is coming from. It's a huge amount of added cognitive strain to have a classes functionality split into various files, possibly across various code bases.
Another problem is coroutines (oh joy, another new DSL to learn). By now, it should be known, from languages like Go and Erlang, that preemptive lightweight threading is almost always going to be better than cooperative. It avoids function coloring, keeps throughput of applications more consistent, and is generally easier to reason about. Java also now has preemptive lightweight threading via virtual threads, but because much of the Kotlin ecosystem is reliant on the coroutine API, it don't get any of the ergonomic benefits.
With some limitations. Mostly writing Kotlin code that is a used friendly enough for Java developers requires a bit of extra effort. It's not something that a lot of Kotlin developers waste time on.
I don't have any numbers, but we know that the Meta family of apps has ~3B users, and that most of them are on mobile. Let's assume half of them are on Android, and you're easily looking at ~1B users on Android. If you have a nullpointer exception in a core framework that somehow made it through testing, and it takes 5-6 hours to push an emergency update, then Meta stands to lose millions of dollars in ad revenue. Arguably even one of these makes it worth to move to a null-safe language! I know your point is that you need to have that sort of crazy scale to make it worth it and that's true, I'm just annoyed at the comments suggesting that the move to Kotlin is just to pad resumes or because Meta let a bunch of bored devs run amok.
I think its great for recruiting. This signals to the world their investment in making Devs happier (one of top two reasons mentioned was "devs were happier with Kotlin")
Does it matter why? They asked their devs and got an answer. Kotlin is not just another cool language, it's now the main Android language. Staying with Java if you're on Android definitely should feel like staying behind...
I only write backend Kotlin code, lots of it, and we have freedom to choose Java or Kotlin, the code base is mixed. Nearly everyone uses exclusively Kotlin for new code. Sometimes they go to the trouble of even converting some existing Java code to Kotlin before working on it.
If you need to ask why devs prefer Kotlin, you probably don't have any experience with the languages.
In addition to what @phyrex already pointed out, without any Java in the code base, they probably hope to hire from a different cohort of job seekers.
Younger developers are far less likely to know Java today than Kotlin, since Kotlin has been the lingua franca for Android development for quite some time. Mobile developers skew younger, and younger developers skew cheaper.
With Java out of the code base they can hire "Kotlin developers" wanting to work in a "modern code base".
I'm not suggesting there's something malevolent about this, but I'd be surprised if it wasn't a goal.
You're already expected to learn a number of exotic (Hack) or old (C++) languages at Meta, so I'm pretty sure that's not the reason.
To quote from another comment I made:
> I don't have any numbers, but we know that the Meta family of apps has ~3B users, and that most of them are on mobile. Let's assume half of them are on Android, and you're easily looking at ~1B users on Android. If you have a nullpointer exception in a core framework that somehow made it through testing, and it takes 5-6 hours to push an emergency update, then Meta stands to lose millions of dollars in ad revenue. Arguably even one of these makes it worth to move to a null-safe language!
An NPE means an incomplete feature was originally pushed to production. It would still be incomplete or incorrect, in Kotlin and would still need a fix pushed to production.
It's even worse with Kotlin, without the NPE to provide the warning something is wrong, the bug could persist in PROD much longer potentially impacting the lives of 1 Billion users much longer than it would have if the code remained in the sane Java world.
I think you're on to something here. When recruiters contact me about Java jobs, I tell them my level of interest in a Java job is about as high as RPG or COBOL, and that I'm wary of spending time on a "legacy" technology. Most of them are fairly understanding of that sentiment, too.
If I had someone call me about Kotlin, I would assume the people hiring are more interested in the future than being stuck in the past.
> The short answer is that any remaining Java code can be an agent of nullability chaos, especially if it’s not null safe and even more so if it’s central to the dependency graph. (For a more detailed explanation, see the section below on null safety.)
One of my biggest gripes with an otherwise strictly typed language like Java was deciding to allow nulls. It is particularly annoying since implementing something like NullableTypes would have been quite trivial in Java.
It wouldn't have been particularly hard from a language, standard library, and virtual machine perspective. It would have made converting legacy C++ programmers harder (scarier). Back then the average developer had a higher tolerance for defects because the consequences seemed less severe. It was common to intentionally use null variables to indicate failures or other special meanings. It seemed like a good idea at the time
This article absolutely reeks of ChatGPT to me. For example:
>With this in mind, we set out to automate the conversion process and minimize interference with our developers’ daily work. The result was a tool we call the Kotlinator that we built around J2K. It’s now comprised of six phases:
followed by a list of descriptions of the "phases" which only sort of make sense for the name given to them, and are utterly incoherent as actual phases in a process (and grammatically inconsistent). For example, one of the cited "phases" is... "headless J2K". In other words: they have one piece of software that wraps another, and it - gasp - doesn't use the wrapped software's GUI. Aside from being entirely unremarkable, that's neither a phase in a process nor a component of a tool. It's a fact about the component.
LLMs write like this all the time - and it's clear evidence that they do not, in fact, do anything like reasoning, even if they can sometimes be manipulated into generating a second piece of text that resembles an analysis of the first one. The resulting description is so weird that I question whether the authors actually checked the LLM's output for accuracy.
Any human writer who gives a damn about good writing and has any skill, would not allow "it" to refer to "the conversion process" two sentences back when "a tool called the Kotlinator" has been introduced in the interim (or, if that were the intended referent, would notice that tools are not "comprised of phases"). Such a writer would also not come up with abominations like "the conversion process is now comprised of six phases" where "we now use a six-phase conversion process" would be much clearer. Certainly, a six-point bullet list produced by a competent writer would label them in a grammatically consistent way (https://en.wikipedia.org/wiki/Parallelism_(grammar)) - not with an abstract noun describing an action, two participles, two concrete (well, as concrete as software ever is) nouns and a command (who, exactly, is being told to "build" the "error-based fixes" - whatever that means - here?).
I'm starting to feel like Mark Twain.
----
On the other hand, I was cynically expecting some mention of using AI for the actual task, and that doesn't seem to be the case.
(Also, the "reactive" web design is broken. The page overflows horizontally for some range of window widths, without causing a horizontal scrollbar to be added.)
Calling it a 'phase' doesn't seem that weird to me? For each file, they go through a number of steps to translate it, and one of those steps is to run the J2K tool on it. The next section is just describing how they implemented that step: the J2K tool is normally enmeshed into the rest of the IDE, but they managed to jerry-rig a solution to run the relevant code by itself, without running the rest of the IDE with it every time.
It could possibly make sense if the phases were described as e.g. "performing a deep build, preprocessing, running J2K, postprocessing, linting and applying error-based fixes" (i.e., all participles). But then the descriptions should be about how those phases are implemented (i.e., this is the place to mention the work of setting up a headless version of J2K so that it could be run in an automated pipeline) and how they contribute to getting the desired result. The description "The J2K we know and love, but server-friendly!" is utterly useless here, and also practically the archetype of what ChatGPT writes in "promotional material" mode.
You are tilting at windmills. The article exhibits the kind of English I'd expect from someone sharing a technical development in an acceptably-perfunctory style for a company blog.
It's as if chatgpt got its style from somewhere... For a hyper sanitized LLM like chatgpt, it's not surprising that it will sound like a sanitized corporate blog (that I still personally liked).
> cynically expecting some mention of using AI for the actual task
Well, you could combine AI with correctness preserving transformations, so you get the best of both worlds (i.e., correctness AND a translation that keeps the resulting code close to what a human would write).
I am surprised they did not use LLMs like Claude or maybe even train their own Llama version to do this. In my experience LLMs have been very reliable in translating code.
Wait, no AI is used ? Amazon claims big about their code conversion from Java 8 to 17 using Q developer (GitHub Copilot equivalent). Why not use Llama3 models here? Can't they help doing such?
This is an application where LLMs should be the obvious choice, it is Machine translation the thing they are supposed to excel at. Why not use them? Likely a lack of data. You would need lots of Kotlin data (I’m sure lots of Java data exists), and the data would need to overlap so the LLM could understand the mapping.
"Android development at Meta has been Kotlin-first since 2020, and developers have been saying they prefer Kotlin as a language for even longer."
Not one link to an opinion piece or two regarding: "kotlin vs java". The nearest thing I found was "What makes Kotlin different".
This sounds somewhat like a debate about which Germanic language is best. German, Dutch and English are all "Germanic" but which is "best"?
Obviously: That's the wrong question to ask and so an answer is doomed to failure.
In the end, does your generated machine code implore the CPU and associated hardware to do what you want it to more efficiently in some way that is not an abstraction?
Pissing contests rarely excite me. Why did you do this?
You literally quoted the "why": their developers prefer writing Kotlin over Java for Android development. That's it. They don't need further justification. They didn't need a "Kotlin vs. Java" comparison, and they're not really evangelizing Kotlin all that much. They're simply stating a fact for their organization: Kotlin is a better fit for their developers than Java is.
> In the end, does your generated machine code implore the CPU and associated hardware to do what you want it to more efficiently in some way that is not an abstraction?
Most shops don't care about this too much. The most important thing is developer productivity.
(And yes, this is why we have bloated garbage like Electron these days; sometimes some people value developer productivity to unhealthy extremes.)
I think a pissing contest is precisely what they were hoping to avoid by not linking to some "Kotlin v Java" blog post. That their developers prefer writing in Kotlin is a basic premise for the rest of the article, not its thesis.
The target audience for this kind of article - Android engineers - do not need any more convincing on why Kotlin is superior to Java. This is a debate that has been settled in the Android community since a long time ago. I'd be willing to bet money on there only being a rounding error of Android engineers advocating the use of Java over Kotlin these days.
I’m skeptical of the value in doing this. There are a mountain of tools like NullAway, ErrorProne, Immutables that make it so much easier to write safe code in Java. New developments in the language like first-class record types improve the DX as well.
I think Kotlin helped push Java in the right direction, but at this point it seems like a weaker choice. Spending years to migrate a massive Java code base like this feels like wasted time.
Java did improve over the years. But it still defaults to doing basically everything wrong by default. Parameters are non final, classes are open, things are mutable, etc. It's unsafe by default. And it's hard to fix because it breaks backwards compatibility. Kotlin doesn't have that problem and it has been doing the right thing by default for a long time. It's still getting better.
I think on Android the choices are either to start from scratch or to migrate to Kotlin for Facebook. Sticking with Java on Android just makes everything harder. All the new frameworks are Kotlin centric. Most Android developers they'd be hiring would have been using Kotlin for years. Etc. So, I can see why Facebook is making the effort to keep their code base alive and future proof. Android is more Kotlin focused and has been for years. And that's only going to be more the case now that compose multi platform is a thing.
Btw, most of the stuff you mention is probably more applicable to server side Java and less to Android. IMHO, Spring + Kotlin is a very nice combo on servers. Very well supported by Spring and they bundle a lot of convenient things for Kotlin. And I don't need any of the tools you mention because it does the right thing by default.
> And I don't need any of the tools you mention because it does the right thing by default.
I mostly agree with you, but Spring+Kotlin does require the allopen plugin, so it's kind of hacking Kotlin to do the wrong thing (all classes open) in order to support an arguably bad design choice by Spring.
Can't blame Kotlin for that. And it's a very minor sin frankly.. set-and-forget.
I, personally, happen to like writing in Kotlin more than Java - with a lot of experience in both (though admittedly, all? the Java I wrote is in the pre-Streams style).
I like:
The preceding 3, combined, allow for: While I agree that Kotlin has definitely helped push Java in the right direction; and I agree that it's probably not especially necessary to migrate 10MM lines of Java code to Kotlin, especially since they're fully interoperable, I definitely would prefer writing _new_ code in Kotlin over Java for the for-the-lazy devex improvements, if nothing else.fwiw, my "good at it" programming history follows the lineage in historical order of:
The feature isn't just about spaces. It's also how you'd work around interacting with JVM code that uses what would be Kotlin keywords. For instance, Java classes with a method named "is" or "val" can be called through
rather than needing to use reflection like you would to accomplish the same in Java.The Kotlin style guide basically says "never use this outside of test names" and some targets, such as old Android, don't even support the feature in test cases.
> at least with a single connected word, I can always double click to select the token
I'd expect an IDE for Kotlin to detect `stuff like this` and select the entire name. Considering Kotlin came from JetBrains, IDEs are most likely already in the Kotlin community's DNA.
(i never wrote or anything related to Kotlin myself but i do appreciate IDEs helping with stuff like this)
> Considering Kotlin came from JetBrains, IDEs are most likely already in the Kotlin community's DNA.
Idea understands and performs dramatically better with Java.
I my experience it performs quite similarly, but Kotlin code can be a lot denser so similar files don't look similar at first glance.
It also really doesn't like Java with generics for some reason. Still beats any other Java IDE I've seen.
This also means that JetBrains will never write a language server for Kotlin, locking you into their ecosystem.
It doesn't lock you into their ecosystem given that other people can (and have) written language servers for Kotlin.
Generally these are only used in test methods, which is fine. I've never seen anyone use them outside that.
What benefit do they provide in testing scenarios? I've never written Kotlin, but from an outsider's perspective, it seems like a slim benefit, outweighed by the cost of the mere existence of this syntactical oddity in the language's grammar.
When writing tests, you can name the methods more useful things, such as:
It's pretty clear what is under test in a situation like that. That's basically the only situation I ever see it used in (and would code-smell the heck out of it if I saw it in other circumstances).People who are familiar with RSpec-style testing are very used to this sort of thing.
Anecdotally, I've also found that such style naming for tests allows me to write out the desired names for all the tests ahead of time more easily and then implement them. That happens to be my flow.methodName_whenInputDoesNotParseToValidRegexThrowException
They did a study and it isn't hard to read camelCase. https://ieeexplore.ieee.org/document/5090039
This study only compared camelCase to snake_case and what the results are mixed, depends on what you call "hard to read". They only seem to have tested finding identifiers from a word cloud, and it did take more time to read camelCase identifiers (although they were also resulted in more accuracy).
It's important to note that the researchers only tested short phrases with 2 and 3 words. "getNextPath" is a different beast compared to "methodName_whenInputDoesNotParseToValidRegexThrowException".
On the other hand, there is a good body of research that generally shows that proper sentences (not just 2-3 words) without spaces are harder to read, at least for English speakers[1]. Early medieval Latin manuscripts did not use space between words[2]. The fact that spaces were seen as an improvement when they were introduced, is probably telling.
[1] https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=unsp...
[2] Roman-era inscriptions did use an interpunct (a small middle dot) to separate words, but this practice fell out of fashion in Late Antiquity and Early Middle Ages and I'm not sure if it was ever the norm in handwriting.
Sure, but I like writing the other more. It's entirely a style thing and you're not required to do it :) it's also basically only ever seen in tests :)
I mean, it's harder to read compared to normal text with spaces and punctuation.
That doesn’t seem to be the case based on the study. And normal for one thing, such as paragraphs of prose, might not translate to another thing, such as a sequence of words in a line of code.
That’s just one study, and I’d argue we should write books in camel case to make them more compact if there’s truly no difference.
> That doesn’t seem to be the case based on the study.
The study does not say that camelCase is unequivocally as easy, or easier, to read.
Selecting the correct identifier from a list of 2-3 words (expandAliasTable, expandAliasTitle, etc.) is a wholly different exercise.
I like how tests are made in Python - you don't even need classes, just functions, and use a single assert keyword for everything. Also it's easy to parametrize them using decorators.
Parameterized tests in Junit are basically the same as decorators:
Also, once you get used to assertj, you'll never want single-assert again. It's fantastically useful to know (for example) not just that two lists aren't equal, but specifically what elements differ.Parameterized tests are a huge, huge win, yeah. I am a big fan of them and look for how to do them in every test framework I use. RSpec was the weirdest for me; but NUnit and JUnit have been quite easy, and I'm not surprised at all they're easy in Python too (admittedly, I don't remember if I ever wrote them in that language).
It reminds me of Spock tests written in Groovy. I have to help maintain a code base like that now at work where a Groovy zealot (who has moved on) convinced the teams to write tests in Groovy instead of Java.
When a test fails, the method name describes what it was supposed to do written out like a sentence enclosed in double quotes which seems like a win but not much of one.
When you need to add a new test or analyze if an existing test needs to be changed, you have to eyeball all the code in the test class because even with methods named with spaces, it's not always indicative of what it does.
With Java, I can sort a list of method names and have a better idea immediately what needs to be updated or added.
It's a place to put intetion that isn't so much of a second class citizen that it will likely be ignored and/or not updated if it ceases to be true. You want your test methods fine grained (many methods that each check one variation, instead of one method that checks a whole list of them) and every extra redundancy (method naming vs the variation of data actually passed vs documentation) increases the the chance of those redundancies getting out of sync.
In fact I've occasionally found myself writing reflection code to parse the test method name as input just to avoid that problem altogether (for name and data, not for documentation). And that was in plain Java even, the pattern could be far more useful with that kotlin naming freedom.
The test frameworks typically output the passing/failing function names. Adding spaces like this make them more human readable.
expectInternalServerErrorOnBadInput
Vs
expect internal server error on bad input
Isn't this breaking Jakob's law of the rest of the tooling though? I can se flows borking with something like "x failed at expect" in a majority of reporting tools not specifically meant to deal with this spaces in functions stuff.
So that programmers have one more bikesheddibg reason, obviously.
As others have said, I would not expectany any style guideline to tolerate those names outside of tests. But it's not entirely test-only: When you are dealing with code generation for schema defined in/for other languages, being able to just drop names in backticks and be done with it would be far less hassle than making up your own transformation and deciding between verbosity and safety from collisions.
Name is wrapped in ``, so it’s not “true” use whatever you want. You also have to use `` on call site.
The better benefit is that you can use the right name for something instead of tiptoeing around reserved keywords.
>Eugh, that is a turnoff.
That's because you're too used to C/Algol style languages
I like it for unit tests. Makes naming them easier
> Maybe I have programmer Stockholm’s, but at least with a single connected word, I can always double click to select the token.
If you're using anything that can do syntax coloring correctly, you can still do that.
While that is avoidable by sticking to a coding style, that horrid and unavoidable 'fun' instead of 'function' (or 'fn') just saps my will to work on such a codebase.
If that's the case, it doesn't sound there was much will to work to begin with...
The best part about lots of languages being around is that you can pick the one that resonates most with you :)
Right. You have selection of Kotlin and Kotlin in Android/Kotlin heavy company.
Why do you hate fun so much?
When it's "sleeping" code, not as in unused but as in not currently being worked on (no changes in open branches), then it can be quite valuable to have some bulk translation run while the code actually is sleeping, and not at some future time when there'll likely be a whole burst of activity happening in parallel. Repo host products might actually add "transform while sleeping" a possible feature, with their cross-branch knowledge, and perhaps some history mangling for a best effort approach to retaining some of th knowledge available through blame through the conversion.
As for Kotlin in general, I agree with your list. I really enjoy the way Kotlin improves the dev experience not so much with audacious new (or "new to a java-like environment", of course I'm looking at you, Scala) concepts but with small trivialities like being able to assign the outcome of an expression chain to a name without polluting that name's scope with intermediates that really don't have any purpose after the result of that chain is assgned.
And I don't hold it against Java that it does not follow that path (it focuses on more impactful changes), I would consider it almost out of character if it introduces e.g. .let. The only thing I'm a bit torn about when it comes to Kotlin is wether the "this" variants of scope functions were a good idea. They are certainly part of kotlin, and some "DSLy" styles would really not be the same without them, but if I were to pull what I like about kotlin into other languages I'd probably introduce let and apply (and the this-less run) and skip run/with/apply (assuming that the target language even has a this to run/with/apply on)
> * data class Foo(val theImplementationAndStyleOfDataClasses: String)
For the purists there are records now in Java (which can be combined with destructuring / pattern matching - there were significant improvements in this area in the last few JDK releases, and more are coming!). In this regard Java had surpassed Kotlin by some margin.
For the enterprise coders, there is the Immutables project (https://immutables.org) which you can say is Lombok on steroids (but implemented correctly, that is, by generating classes which you can inspect as regular source code, instead of relying on hacking the compiler...).
> * elvis?.operator?.chaining
This will hopefully be finally resolved by one of the subprojects of project Valhalla, as a prerequisite for value class support (which cannot be null, so...).
The others on your list are small and subjective stylistic differences, of course your preference may vary, but should not weigh heavily in any Java vs Kotlin discussion.
> This will hopefully be finally resolved by one of the subprojects of project Valhalla
I think we'll see them come in through https://openjdk.org/jeps/8303099 although I'm not a huge fan of the approach chosen (tri state nullability: explicitly nullable, explicitly non-nullable, legacy code that should just be treated as nullable).
Except for native support for reification, almost all of Kotlin can be reimplemented by Java if you wait long enough and stack enough class annotations. It's all stylistic up to a certain point, but the classic Java style combined with the glacial improvement process is one reason why some people want to use a different language.
I think I'll have an easier time convincing someone to consider Kotlin than I'll have convincing someone to add Immutables+Manifold to the dependency chain. You end up writing Java that's not really Java like, but also not something that there's a good standard style for, and that usually ends up in a mess.
I'm glad with pattern matching, Java is now pushing Kotlin to improve rather than the other way around. I'll hope to use those features one day, when companies finally upgrade from Java 11 to Java 17 in five or ten years.
Java records do not have a copy method (or with clause or whatever equivalent), and there are no keyword arguments.
This makes Java records extremely painful to use for any data has more than 3 fields or so. You could use them together with an annotation processor like RecordBuilder (or Immutables or Lombok, but I don't think they support records?), but that's just not pure Java. Annotation processors that generate byte code always come with their own complexity.
Kotlin data classes have their own warts (order-based destructuring can be dangerous and the copy() method is a bit hacky) but the overall ergonomics for large value objects are better than Java records at this point.
Lombok supports `@With` on records, which adds wither methods. And there are ongoing JEP discussions on how to support this natively in Java.
Lombok is so ubiquitous today that I effectively consider it part of the language. And even with Lombok, the Java toolchain is so much faster, easier to use, and more stable than Kotlin.
Kotlin is a great language, marginally better than Java, but I'm not sure it's worth enduring the toolchain. And Java keeps improving.
If you think Kotlin is marginally better than Java, you have a superficial understanding of Kotlin. Kotlin is vastly better than Java, which isn't surprising because Kotlin was designed 15 years later with the explicit goal of being a better Java and fixing as many of Java's design mistakes as possible. For example, the designers of Kotlin went through the book "Effective Java" and tried to avoid the listed pitfalls by design.
Java will never get close to Kotlin because even though Java can, and thankfully does, still improve, it's too late to revise many of it design decisions, and large language features often need to be considered in a language's design from the start.
On Android, Kotlin is an even bigger win because Android uses an old Java version and has become Kotlin-first.
In my opinion, Java will remain the best choice for libraries that target multiple JVM languages. For everything else, I use Kotlin without thinking twice. Kotlin 2.0 laid the groundwork for toolchain improvements, which have indeed been a long-standing pain point.
> pattern matching ... In this regard Java had surpassed Kotlin by some margin.
I did just glance at Java's pattern matching; and yeah, it does look like it is a bit more powerful than Kotlin's pattern matching in that it can, at the same time, look for both type and a conditional. That's relatively neat - not something I've personally needed / wanted; but neat just the same :)
The JVM is a really nice virtual machine in how it lets us use both of these languages fully interchangeably to pick the one we like more.
I'm glad Java has been investing in some of these areas, too. Everyone improves when paradigms and new learnings are distilled.
Kotlin 2.1 has added "Guard conditions in when with a subject" as an opt-in feature https://kotlinlang.org/docs/whatsnew21.html#guard-conditions...
fwiw in modern-Java they have data classes:
The string is final. While sadly there is no elvis operator, the Optional type helps: I still strongly dislike working in Java but Java 23 is a long way ahead of the Java 6 I first learnt Java with.I also really like conditionals like switches and ifs returning values.
try statements being expressions is also especially lovely
Java 14 introduced switch expressions.
https://docs.oracle.com/en/java/javase/17/language/switch-ex...
> how function pointers at the end of a method call can be auto-inserted as the next curly bracket
This is one feature I wouldn't mind making it into other languages. It makes so much sense for doing UI stuff.
I used to do Java and Scala.
In 2020 I started a new project in Kotlin.
I like Kotlin, but what I hate is that in the meantime I migrated all my programming to Neovim and Helix. All programming? No, not Kotlin, because the LSP isn't there and JetBrains clearly has no interest, they want to sell their IDEs.
Now don't get me wrong, I have an IntelliJ license since 10 years, even back when I was employed and paid it out of my own pocket.
It's not about the price. I would gladly pay for the LSP implementation. But I don't want to use IntelliJ anymore.
So a new project on the JVM where I have a say in the stack? Java or maybe Scala. No more Kotlin.
This is the biggest issue with investing in Kotlin IMHO. The stewards of the language have a conflict of interest in democratizing the tools.
Honestly, none of those differences you listed seems especially compelling to me, except possibly for the ?. operator.
What would be compelling: Array types that don't hate generics, or generic collection types that don't hate primitive types. Does Kotlin improve on Java at all here? It's such a pain having to remember this bonus complexity when all I want is a consistent way to manage a bunch of things. (I suspect not, as I think the limitations are at the JVM level, but I don't know.)
Kotlin does improve on the primitives/boxed situation. For example in Kotlin there is just "Int", not both "int" and "Integer". The language will use stack-allocated primitives wherever possible and boxed objects when necessary (in Collection generics or when you've explicitly opted into nullability). The distinction is abstracted away from you as a programmer. Sounds like the consistency you want.
10+ years in the making, at this year's Devoxx Brian Goetz said that they are almost ready with the design of Project Valhalla and can start JEP-ing it in future JDK releases. No timeline yet, so we probably will not see it until the next-after-next LTS release (29?)... But Java, like Cobol, is not going anywhere, so the wait will be worth it.
Great, though is that perhaps orthogonal to my complaints? My vague understanding is that Valhalla is about transparently allocating local objects on the stack instead of the heap when possible (i.e., when escape analysis confirms that they can't "get out" of the method that declared them), the way primitives are now.
But these are the same problems! I'm not intimately familiar with the current state and internals of project Valhalla, but they were proposing `primitive classes` backed by primitive types, allowing said class to be used as a type in generic code. Moreover, primitives would become ValueObjects, so you would have your int[] which would also be an Object[]. In other words, the difference between primitives and objects would be much smaller in terms of the language.
Bottom line is, you could have your cake and eat it too - have an int[] that looks like a List<Int>, and vice versa, while retaining the performance characteristics of an array.
Thanks, I'd thought that the goal was to reimplement aspects of the compiler+JVM to preserve existing language semantics but be more performant, but from what you've written I take it that the goal is actually to expand the language semantics themselves, in a way that would bring primitive types and objects (and thus generics) closer together.
Valhalla is what you want, at least for some of it.
https://openjdk.org/jeps/402
https://openjdk.org/projects/valhalla/It is also about value types. I think the talk parent is talking about is [this], a bit long but worth watching
[this] https://inside.java/2024/12/16/devoxxbelgium-valhalla/
Scala solves the primitives-in-generics, and it significantly improves on the array-hates-generics.
So it's possible.
Out of curiosity, what are you wanting that autoboxing doesn't resolve?
Stripping out Maps/Lists with boxed keys and values is the first easy thing to do when perf tuning a piece of code. It's so frequently impactful that it's worth doing immediately without any measurement.
You just swap them out with fastutils or trove and call it a day.
I think you're right, that's probably the best (lowest mental overhead, fewest footguns) way -- always use a List instead of a raw array, and always box primitive types. This also avoids the parallel explosion of Stream types and methods (IntStream, mapToInt(), etc. (and the fact that there's no BooleanStream or ByteStream, etc.)).
That said, I tend to get bitten by the fact that == on 2 boxed numeric values is roughly as misleading as possible -- it does reference comparison, but interns small integer values only, so (Integer)42 == (Integer)42, but (Integer)420 != (Integer)420. (So the small values you probably used in your unit tests will appear to work just fine with your broken, ==-using code.)
ETA: Deleted a misplaced "new". I also don't love that, e.g., ++x does a heap allocation + deallocation if x is boxed (since boxed types are immutable). But usually I care more about the cycles I spend coming up with correct code than cycles the CPU spends executing it.
The value in the conversion of existing code in this particular case isn't 100% clear to me either, but I think calling Kotlin a weaker choice than Java at this time is naive, particularly when preceding that with "there are a mountain of tools" that you can bolt on to Java to give it features that are built in to Kotlin.
What makes Kotlin such a strong choice for many orgs today is its batteries-included multiplatform capability. We are able to write data models, validation logic, business logic, etc just once and compile to JVM, WASM, and native targets. For orgs with very large codebases and frontend applications (web + iOS + Android) this is an attractive capability because we can have a single codebase for a ton of core functionality, and have each frontend import a library which is native to its own platform.
Of course many technologies with this promise have come and gone over the years, but this is the first one with a strong backing that has allowed us to _natively_ interoperate with each target platform.
I believe these are all driving factors that have been pushing well known companies, that were previously Java shops, toward Kotlin. If you speak to a broad range of people in the industry you'll find many more orgs moving from Java to Kotlin than from Kotlin back to Java. We can simply get more work done with less code and ship to all our frontend platforms, and unless Java can do the same, I don't see the industry moving in that direction.
It’s like marriage. Yes there are tools that can give you most of the same rights and privileges as marriage, or you could just get married and get all of that for the cost of writing one check and signing some papers. No lawyers. Done. Move on with your life.
The question of optional things is always laid out as if the choices of my coworkers do not exist. Are you expecting me to work solo, or to be a bloody tyrant who pushes through all of my own ideas over the objections of an entire team? These are some of the most antisocial people in any debate on software. No I don’t get to just chose to use or ignore a tool in an ecosystem. That’s why picking a system with batteries included is a simpler transaction. I can go to a new company and have expectations, not arguments.
You forgot that you need to have a whole infra team that is going to maintain all the shitty Gradle configuration KMP requires.
> What makes Kotlin such a strong choice for many orgs today is its batteries-included multiplatform capability. We are able to write data models, validation logic, business logic, etc just once and compile to JVM, WASM, and native targets.
Not familiar with Kotlin but how does that work? Does it come included with a PAL? Because it you want to be platform agnostic, you can't for instance use a Java RegularExpression in your platform agnostic code.
The PAL (Platform Abstraction Layer I assume) is just the stdlib that is provided. The stdlib is not the same for all platforms, as can be seen in the documentation. A regex implementation is provided for all platforms, but is not quite the same on all platforms: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/-r...
In shared code you can define interfaces that have to be implemented by any platform you use.
As long as they're already writing new code in Kotlin, translating the existing code makes a ton of sense, if they can do it cost effectively (which is sounds like they did).
One of the huge problems with a language migration is that you're left with old and new and all the parallel tooling, context switching, and impedance mismatches. There's often a claim that the existing code will be migrated eventually, but IME that doesn't actually happen with large code bases. Then if there's ever a third migration, you're left with three languages in play.
It's much better to aim for 100% migration, as close to 100% automated as possible. Then when you're done, you're really done. Maintenance, training, and the next migration will be easier.
There are a lot of nice things I enjoyed when I was still working with Kotlin extensively. Null safety, data class, sealed class, exhaustive when, top level functions, object class, higher order function, extension functions etc. They fundamentally change the way dev think, producing safer, lighter and more maintainable code.
Contrary to the popular notion of dismissing certain syntax differences as sugar, I consider it one of the most important factors simply because we spend more time reading than writing code. To me Java has always been verbose and dreadful to read, there's something fundamental wrong if you need your IDE generate so much then force to train your eyes to skip over most of them while reading.
I find Kotlin to be more elegant and fluent especially with native syntax support of the above features. I can read it at least 25% faster than Java. Perhaps which one is better is personal taste, but I'd maintain syntax is very important, just like natural languages.
>I think Kotlin helped push Java in the right direction, but at this point it seems like a weaker choice.
Nobody wants to use a mountain of tools just to achieve basic feature parity with other languages. Also java syntax is awful.
Personally, I love Java syntax, or did before Java 8. It's still better than Kotlin though. At least it gets the type and name in the correct order.
What makes it the "correct" order?
The newer version of Java has much better syntax.
Especially because outside Android, Kotlin Virtual Machine will never happen, it will always be a guest language on the Java ecosystem.
Nothing on Kotlin will ever matter on the decisions taken by Oracle, IBM, Amazon, Azul, PTC, Aicas, microEJ, Microsoft, Red-Hat,.... on their JVM/JDK implementations and related tooling.
> I’m skeptical of the value in doing this.
Its Meta. They have enough money to spare not to need to worry too much about whether there is a good RoI on rewriting some mobile apps.
I very much doubt FB's mobile apps are written for any sort of efficiency, (either in engineering or financial terms) - 18,000 classes in the apple one! https://quellish.tumblr.com/post/126712999812/how-on-earth-t...
Yeah Facebook was (in)famous for autogenerated code in their apps and frameworks. In fact early on they had to do some hack on Android since they had more classes in their FB app than was supported by the operating system lol.
So super inefficient binaries, but I guess more efficient to develop (or I assume that was the idea)?
You are getting the details wrong (I was there). This was the single DEX limit, and Google would just bump it in AOSP every time their own apps hit it (as their apps didn't support older OS versions). FB at the time was supporting back to froyo, which had a much lower limit than "modern" apps needed. See this note for more info: https://www.facebook.com/notes/10158791578712200/
Thanks for the clarification! Yeah I only vaguely remember the story from reading about it when it was new, so it’s been a while haha
> was supported by the operating system
By the Dalvik Virtual Machine (DVM). 65k method limit is what Facebook hit. tbf, DVM was engineered to run on embedded systems with 64MiB RAM (as a replacement for J2ME).
Most important reason for the rebirth of golang (after it's initial dip in early '10s) and wide spread adoption of Kotlin is the ownership of Java by Oracle. It doesn't matter what fine print exists, but everyone knows what kind of a*holes Oracle and Larry Ellison are, they will find a way to squeeze you. Everyone is going to sprint away from Java as soon and as much as possible. For someone like FB, Kotlin being a nicer language is just cherry on the cake.
But doesn't Kotlin compile to Java?
No, it compiles to Java bytecode that runs directly on the JVM.
I would argue it was Scala, not Kotlin, that has contributed to the push to make Java “better”.
I would argue that Scala drove Kotlin. And over the last 10 years at least, it's been Kotlin driving Java.
What I saw happen was Kotlin taking over the mantle of "better java" from Scala, and aiming for an 80/20 type language compared to Scala. And for the most part, it's those 80% that are finding their way into Java.
Many of Kotlin's profound improvements will never find their way into Java because it's 25 years too late to do so. Java improves where it still can, which is the right thing to do. Sometimes Java even manages to improve on Kotlin because now it can learn from Kotlin and other languages. Nevertheless, it's impossible for Java to catch up to Kotlin, which was designed 15 years later with the explicit goal of fixing as many of Java's design mistakes as feasible for a JVM language.
Scala and Groovy were big pushes for Java 7/8. Their hypes died down after that release.
Where do you see pattern matching in Kotlin?
In its pattern matching feature
Kotlin has no pattern matching. Their bootleg ‘when’ with guards is no match (no pun intended) for true pattern matching.
There are, at least in my opinion, many more reasons to use Kotlin than null-safety and data-classes/records, especially on Android (Jetpack Compose).
Say thanks to Google who basically stopped developing stuff with Java in mind.
How do they compare tooling wise? Kotlin's K2 compiler targets more platforms than the JVM which seems like an advantage.
Is Oracle a factor?
Does a Kotlin codebase have more safety from a legal perspective?
The legal issues around Java between Alphabet and Oracle are settled at this point and no longer a risk for third-party software vendors. But it's pretty clear that Android is moving away from Java, so anyone with a strategic commitment to that platform has to plan around that reality.
It's safe to say that Android is definitely moving away from Java in terms of new language features. I mean, if you want to support older Android versions and use modern Java features or newer parts of standard libraries, you'll usually have to rely on desugaring or making sure you're using classes that are supported in Android.
IMO, Android is moving away from modern versions of Java. Java and its underlying standard library will always play a big role in Android development.
The way I see it, Kotlin makes a lot of sense for Android development because Kotlin can introduce new things in its standard library that make older versions of Java look ancient. It's not like you can use new Java features in Android, so using Kotlin makes people not care as much about new features of Java that are only available in modern versions of Java.
Android folks were forced to acknowledge modern Java, otherwise they would lose access to Maven Central ecosystem, as everyone adopts newer Java versions.
Hence ART is updatable via Play Store since Android 12, and is currently on Java 17 LTS.
Is it even full Java 17?
Of course not, it is Android we're talking about.
It is good enough Java for relevant libraries to work on Android.
It is hard to move away from Java, when the whole JetBrains tooling, Gradle and Maven Central depend on Java ecosystem.
Including the main sales pitch of Kotlin, its Interoperability with Java.
unless you're not using the jvm (which is owned by oracle, despite being opensource), you won't have any difference between kotlin and java from a legal perspective tbh.
> unless you're not using the jvm (which is owned by oracle, despite being opensource)
It's not "the JVM"; JVM is a spec that has many implementations, you are probably referring to Oracle JRE/JDK.
Meta operates at such a large scale that the engineering management decision process becomes qualitatively different from smaller organizations. They can justify enormous investments in keeping their code base fresh for small improvements in productivity and quality.
It isn't fresh though. Java has improved a lot and the benefit of Kotlin is dubious.
Maybe it has a more PHP-like feel to it. No accounting for taste at that company.
> benefit of Kotlin is dubious
What's dubious about it. Kotlin has a more comprehensive dx, and has features like extension functions that aren't on the java road map.
The nice thing about null-safety in Kotlin is that it is built-in and it requires no extra annotations or added tooling. Not having @ProxySingletonAbstractJunkFactoryFactoryBean sprinkled all over your code does it ever so slightly more readable.
But if built-in null safety and lower verbosity was all that Kotlin had to offer I doubt it would have won. Kotlin offers a lot more features that Java does and most probably will not offer in the next 10 years:
- Extension methods: No more terrible FooUtil or BarHelper classes.
- Context parameters (preview feature)
- Coroutines (they are not just about concurrency - you can use them to write generators as well[1]).
- Keyword arguments: This makes using constructing data classes possible without generating builders, and in general lets you deal with multi-argument methods in a safer and more readable way. It also cuts down the boilerplate of defining 20 different overloads that call each other.
- Interface Implementation by delegation: This feature makes it easy and painless to follow the motto "composition over inheritance". Java makes implementation inheritance 50 orders of magnitude easier than composition with manual delegation, so it's pretty natural that the vast majority of Java code- bases overuse inheritance in very bad ways.
- Properties: This reduces the need to worry about direct field access causing API breakage in the future, and removes the need for the absolutely massive boilerplate associated with getters and setters in Java. It's true that records remove the need for that, but records only work for purely immutable data classes (and right now they are only practically usable with a small number of fields).
- Delegated Properties: Not something I use every day, but it's very useful when I do use it.
- Unsigned integers (specifically bytes): Writing any byte-wrangling code in Java is a downright nightmare.
- Reified Generics: Java may get something equivalent to that with Project Valhalla, but I'm not sure how comprehensive it would be.
- If expression: Java made switch an expression with JEP 361, but if is still a statement. This leads to a lot of bloated and somewhat unsafe code where you have to define a variable before the if and then assign to he variable in each branch.
- Expression Bodies for function: This is a very small feature, but it's pretty nice to cut down boilerplate and make functions more readable when you need it.
DSL Features ------------
Kotlin is perfect for DSLs thanks to features like:
- Closure blocks: Kotlin lets you pass the last closure argument to a function as a block of code inside curly braces that follows the function call. This features lends itself very well to readable DSL. In Java you would need to embed the closure inside the function argument list and add an extra parentheses. This gives you some of (see the next couple of points) the DSL capabilities of Kotlin, but the DSL becomes very hard to read and use when a lot of blocks are involved. There is a good reason why Kotlin DSLs are based on blocks, while Java DSLs are based on the less-flexible fluent interface pattern: block-based DSLs are just too hard to read and write in Java.
- Inline functions: Inline functions allow the compiler to perform some optimizations in advance that may be harder for the JIT to do (namely inlining the provided block closures), but the real kicker is that you can use flow control statements (break, continue and return) that affect the calling scope.
- Multiple "this" in context. You can have multiple values for this inside a nested scope. The compiler manages to find the right reference for the "this" alias based on your usage, but in case of ambiguity (where the deepest "this" wins), you can disambiguate your choice. This feature sounds overly complex and unnecessary at first, but it allows a lot of power for the Kotlin DSL system.
- Closures with this receivers: Using the multiple this values from above, you can have closures that receive their own "this" to introduce DSL methods. Being able to introduce these methods without shadowing the "this" from the parent context is crucial for powerful DSLs.
- Infix functions and operator overloading: Needless to say, these features make DSLs even nicer.
I've probably skipped a couple of features I can't recall right now, but I hope that it shows that Kotlin is a lot more than just "Java with null-safety and data classes" as some people think.
[1] https://kotlinlang.org/docs/sequences.html#from-chunks
> Interface Implementation by delegation: This feature makes it easy and painless to follow the motto "composition over inheritance". Java makes implementation inheritance 50 orders of magnitude easier than composition with manual delegation, so it's pretty natural that the vast majority of Java code- bases overuse inheritance in very bad ways.
While we're on this subject, I'd like to rant for a bit. Why is this not a standard feature in every OO language today? The adage that composition should be preferred to (implementation) inheritance is itself older than Java, and was already fairly common wisdom 20 years ago - yet PL design has seemingly not reflected this.
At my last job, the management greenlighted a full rewrite in Kotlin in order to attract/retain developers bored with Java and Python. The actual business project was boring since all the important design work was already finished by the architect. No language rewrite, no interested devs. So management made a quiet trade with ICs where everyone got what they wanted at the cost of future maintenance.
I learned that social whims (developer fun, preferences , dopamin) are weighted as much as technical rationales (performance, maintenance)
How would you say it panned out? Were you able to hire people easily and did retention go up?
Initially, Kotlin attracted mid-level folks to transfer. I recall a roaming staff engineer who lead the project for a bit, and then wisely rotated outside of the org.
Long-term, the folks who remain are stuck and unhappy. The business got what it needed and moved on, leaving a system half in maintenance-mode, half feature-complete. Any Kotlin-only changes are mostly just bug-fixes or glue work with other team's SDKs. Any new features carry a lot of risk because the few people motivated to drum up new business (and write up requirements) left or quiet-quit.
In a weird way, the project naturally selected for people who would get stuck maintaining the artefacts of the project. It's a paradox that I can't fully comprehend.
Sounds to me that the situation was caused by problems entirely unrelated to the programming language that got chosen.
On the contrary. The fact that there are two languages that “coexist” means that now we can have politics. The same would not be true if the choices were Java and, say Go, because then it’s a complete rewrite. There would still be the possibility of politics but the engineering decision would be so one sided as to make it difficult to leverage. With Java vs Kotlin, disingenuous folks pretend they are the same thing.
yup, a company I worked at allowed Scala and Clojure to make different developers happy
I view this as great news. It will attract those would rather code in Kotlin for whatever reason, to FB, leaving more Java opportunities for the rest of us who like and prefer Java, even after 29 years with it.
Crazy project.
Personally I find that it's an interesting indicator of the capability of the programming languages. Moving from language A to B can be extremely easy if B is as powerful or more powerful in terms of expressiveness than A. It can be an absolute horror if it is less powerful.
Being not null-safe in fact brings additional expressiveness. I guess most would argue that it's not a good type expressiveness. Nonetheless it is a type of expressiveness that can cause trouble during such a transition.
In general it feels like Java is making faster progress than Kotlin nowadays. I wonder where Kotlin would be if it weren't for Android. I hope those folks don't have to migrate back before they even finished.
Without Android, Kotlin would just be Cool Alternative Language #4721. Java has been a safe, non-controversial pick for decades. Who is going to endorse writing their hot new project in it just because some IDE uses it? When Google says they support a technology going forward, that gives the project some cache it otherwise never would have received.
Not even support, now they’ve completely migrated all Android libraries to be Kotlin-first/Java-never.
I have a genuine side question... Why does Meta have 10M lines of Java for their Android code base? What's in it?
(Android dev at Meta here, though not involved with the Kotlin migration. Opinion below, completely my own)
We have: public and internal apps, tests for those, tons of wrappers around Android APIs, autogenerated infra code, did I mention the API wrappers?, custom layout engines, duplicate code that people wrote because they couldn't be bothered to search the monorepo, code that was copied without much thought because people searched the monorepo and found something, app architecture tools like dependency injectors, annotation processors, custom build tooling, etc
If you can think it, someone has probably committed Java/Kotlin or native code for it already.
> duplicate code that people wrote because they couldn't be bothered to search the monorepo, code that was copied without much thought because people searched the monorepo and found something
This is also a very common way in Object-Oriented code bases to ensure you're not messing anything up by re-using classes. Concatenating yet another class to the code base is the safest and laziest way to ensure you're not possibly changing the behavior of anything else when you inevitably need to add/change something in the class.
Why is this a OOP problem and not a problem in large code-bases in general? I can easily see it happening in functional or imperative languages where one creates an almost identical copy of a function instead of reusing and modifying an existing one.
We move fast and break things ;)
Wouldn't it make more sense to do a massive refactoring given the situation is as you described? And, if needed, rewrite to Kotlin later. This would just cause the same problems to be reproduced in a different language.
A rewrite, too, would take less than years, would have a better end result. Honestly the FB application is not exactly rocket science, and the massive amount of code they have points more on the direction of broken internal process and a total lack of simplicity in the design and choices.
No, the translation is mostly automated.
That makes sense. Thanks for the answer.
I don't think people realize that "Android" at Meta is not just the "Facebook" app, but also: WhatsApp, WhatsApp for business, Instagram, the whole Oculus layer on top of AOSP, whatever they use for Ray Bans, etc.
As a consumer you're only ever seeing the tip of the iceberg of Meta apps. There are at least 3 major user groups: The consumers, the producers, and the advertisers, and each of them is at least as complex as the others. Then you have to consider the global audiences, and that e.g. ads are handled very differently in the EU than in North America, and that needs to be accounted for.
There's a lot of subtlety in what exactly a "line" is, especially for Java and especially for a legacy enterprise codebase: hard to say that Meta's 10M is actually twice as big as someone else's 5M.
I have no clue what's in their code, but I would expect a lot of "almost redundant" stuff around specific Android versions, manufacturers, etc, which can really pile up.
Java's how you wrote Android apps before Kotlin came out. I expect they have __all their existing Android code__ in Java. 10MM lines doesn't seem out of line for a very, very established company with 100k developers across several products. It's one of the 3 main platforms that people interact with Facebook on and so they'd want it to be as good and as fast as possible, especially on older phones for the time when Android phones were new.
Meta doesn’t have close to 100k developers.
Why they wouldn't use React Native?
[1] https://en.wikipedia.org/wiki/React_Native
So, even if they did use React Native, they still have 4+ years of code in the original language; and, React Native doesn't stop the use of Java
I'm pretty sure wg0 was being facetious.
Most importantly, why was Threads not written in React Native?
Because Instagram wasn't, and Threads is pretty much a copy pasted Instagram codebase (for good reasons).
I'm well aware of the history of Android, Kotlin and Java. Still my question stand...
All those AbstractContextFizzProviderMetaFactoryBuilderInjectorStreamManager quickly add up.
That is mostly a meme. What really adds up is putting every little thing into its own file and having an interface and a (usually trivial) implementation for everything.
Really the only question I have on this post. So many of their non ai updates make me roll my eyes
Why does fb.com always erase your history so the back button no longer works once you click on the link to go there?
For the same reason that casinos don't have windows.
One of the things that surprised me in the article was their usage of J2K. They’ve been using it as part of IntelliJ, alright, but why did they have to run it headless? They’ve even mentioned that it was open sourced. And later they’ve said that they were not able to do much improvements because it was on maintenance mode at Jet Brains.
I mean, with the ressources meta has I’m sure they could have rewritten the tool, made a fork or done any other thing to incorporate their changes (they talk about overrides) or transformed the tool into something better fitting their approach. Maybe it has been done, just not clear from the article
What are the benefits of Kotlin over Java? Something I wish they went into!
I commented here: https://news.ycombinator.com/item?id=42483538
Mostly, I find it far less verbose with a couple huge convenience features that I'm constantly using and would feel very lost without; and, they all come as part of the language, rather than bytecode hacks / pre-processors, like lombak
My biggest pet peeve with Kotlin is that they got rid of checked exceptions and replaced them with nothing at all. All error code flow is now just invisible unchecked exceptions, the single worst kind of error handling imaginable.
I know they added the Result type later on, but there's zero convenience to it, so you have to manually handle every error, even if you just want to bubble it.
Checked Exceptions were better, though Rust still has the best error handling in any non-functional language.
Depends on what you're writing I guess (as always). I write backend services and if there is an exception thrown, generally the whole request is forfeit. So a top-level exception handler is all that's needed.. no checked exceptions ruining my lambdas or streams like in Java.
Lombok is absolutely painless.
I admit I have no direct experience with it. For my perspective on it, I rely on one of the best developers I've ever worked with - especially when it comes to debugging and deep investigation and she *hates* it. If I had to assume (we don't work together anymore), it's because it did some freaky stuff that made debugging the weird bugs really, really hard.
Lombok does provide a lot but it doesn't make types non nullable by default or reduce verbosity or give us extension functions.
Lombok is absolute garbage. Either use proper Java 2.0 like Scala/Kotlin or stay in Java.
Steve Yegge has a fun article on why someone might prefer kotlin over Java: http://steve-yegge.blogspot.com/2017/05/why-kotlin-is-better...
Do note that Java has quite a few features now that it didn’t at the time of writing.
“More specifically, from JetBrains, the makers of the world-famous IntelliJ IDEA IDE, whose primary claim to fame is its lovely orange, green, purple and black 'Darcula' theme”
This must be a bad attempt at a joke, right? Darcula is a nice theme (I personally prefer high contrast), but surely IntelliJ’s code inspection and automatic refactoring have always been its claim to fame.
Yegge’s writing has a pretty sardonic sense of humor. I like it but I’ve seen it put off some folk.
Themes are the cup holders of the IDE world. You can't sell one without them.
> maximize our gains in developer productivity and null safety
Java is to too verbose. Kotlin have features like null safety, better functional programming, better coroutines, advanced sealed classe. Java virtual threads is still not ready and development was very slow. Blame oracle for being too complacent.
it has been getting better. Records and lambdas cut down on a lot.
still seems too verbose though even though it is my preferred language
I'm a dunce
Kotlin's null safety is a huge win, but the language has it's own set of flaws such that I try to avoid using it.
The language's "declarative style lambda syntax" makes code overly-complex. Every library is basically it's own DSL and every lambda context has it's own set of specialized keywords. If I write `test { somthing(value) }`, depending on the context, `somthing` could be invoking a higher-order function, calling a method on an object that the lambda is extending, or calling a static method. The muddling of data/declarations and procedures, makes jumping into new codebases confusing and time-consuming. Rich Hickey has pointed out in numerous talks that syntax itself is complex because rules and order are complex, but in languages we generally trade a little complexity for terseness. Kotlin encourages this trade-off far too much in the direction of complexity, and the language would be almost unusable if not for it's IDE support.
Getting to the root of the previous problem is that method extensions in general feel like an anti-pattern. Without introspecting a method call, it's not possible to tell where that functionality is coming from. It's a huge amount of added cognitive strain to have a classes functionality split into various files, possibly across various code bases.
Another problem is coroutines (oh joy, another new DSL to learn). By now, it should be known, from languages like Go and Erlang, that preemptive lightweight threading is almost always going to be better than cooperative. It avoids function coloring, keeps throughput of applications more consistent, and is generally easier to reason about. Java also now has preemptive lightweight threading via virtual threads, but because much of the Kotlin ecosystem is reliant on the coroutine API, it don't get any of the ergonomic benefits.
Interesting that they didn't use one of their AI models to assist them...
The process is mostly automated...
This seems like a huge waste of time unless they expect Google to deprecate Java on Android - which isn't impossible.
You can’t use Jetpack Compose with Java.
Some of the newer material libraries are kotlin only. It does seem to be happening.
Kotlin can't be called from Java land?
Jetpack Compose cannot be written in Java because it relies on a Kotlin compiler plugin to transform composable functions.
With some limitations. Mostly writing Kotlin code that is a used friendly enough for Java developers requires a bit of extra effort. It's not something that a lot of Kotlin developers waste time on.
It can.
Why? Oh God why?
Ah, here we go again with HHVM and Hack ...
The only reason fb is able to do this, is the billions of $ behind it... For everyone else this is just pure idiocy
Sure if you like Kotlin, use it for new software, but rewriting milliona loc for some marginal gains... that how businesses fail more often than not
I don't have any numbers, but we know that the Meta family of apps has ~3B users, and that most of them are on mobile. Let's assume half of them are on Android, and you're easily looking at ~1B users on Android. If you have a nullpointer exception in a core framework that somehow made it through testing, and it takes 5-6 hours to push an emergency update, then Meta stands to lose millions of dollars in ad revenue. Arguably even one of these makes it worth to move to a null-safe language! I know your point is that you need to have that sort of crazy scale to make it worth it and that's true, I'm just annoyed at the comments suggesting that the move to Kotlin is just to pad resumes or because Meta let a bunch of bored devs run amok.
I think its great for recruiting. This signals to the world their investment in making Devs happier (one of top two reasons mentioned was "devs were happier with Kotlin")
>Developers prefer Kotlin over Java
Why? Bit of substance here would be nice. Otherwise it's just another "we migrated to $coolLanguage" post.
Does it matter why? They asked their devs and got an answer. Kotlin is not just another cool language, it's now the main Android language. Staying with Java if you're on Android definitely should feel like staying behind... I only write backend Kotlin code, lots of it, and we have freedom to choose Java or Kotlin, the code base is mixed. Nearly everyone uses exclusively Kotlin for new code. Sometimes they go to the trouble of even converting some existing Java code to Kotlin before working on it. If you need to ask why devs prefer Kotlin, you probably don't have any experience with the languages.
> we decided that the only way to leverage the full value of Kotlin was to go all in on conversion
Could someone expand on this please.
In addition to what @phyrex already pointed out, without any Java in the code base, they probably hope to hire from a different cohort of job seekers.
Younger developers are far less likely to know Java today than Kotlin, since Kotlin has been the lingua franca for Android development for quite some time. Mobile developers skew younger, and younger developers skew cheaper.
With Java out of the code base they can hire "Kotlin developers" wanting to work in a "modern code base".
I'm not suggesting there's something malevolent about this, but I'd be surprised if it wasn't a goal.
You're already expected to learn a number of exotic (Hack) or old (C++) languages at Meta, so I'm pretty sure that's not the reason.
To quote from another comment I made:
> I don't have any numbers, but we know that the Meta family of apps has ~3B users, and that most of them are on mobile. Let's assume half of them are on Android, and you're easily looking at ~1B users on Android. If you have a nullpointer exception in a core framework that somehow made it through testing, and it takes 5-6 hours to push an emergency update, then Meta stands to lose millions of dollars in ad revenue. Arguably even one of these makes it worth to move to a null-safe language!
An NPE means an incomplete feature was originally pushed to production. It would still be incomplete or incorrect, in Kotlin and would still need a fix pushed to production.
It's even worse with Kotlin, without the NPE to provide the warning something is wrong, the bug could persist in PROD much longer potentially impacting the lives of 1 Billion users much longer than it would have if the code remained in the sane Java world.
I think you're on to something here. When recruiters contact me about Java jobs, I tell them my level of interest in a Java job is about as high as RPG or COBOL, and that I'm wary of spending time on a "legacy" technology. Most of them are fairly understanding of that sentiment, too.
If I had someone call me about Kotlin, I would assume the people hiring are more interested in the future than being stuck in the past.
From the article:
> The short answer is that any remaining Java code can be an agent of nullability chaos, especially if it’s not null safe and even more so if it’s central to the dependency graph. (For a more detailed explanation, see the section below on null safety.)
One of my biggest gripes with an otherwise strictly typed language like Java was deciding to allow nulls. It is particularly annoying since implementing something like NullableTypes would have been quite trivial in Java.
Would it have been trivial and obvious for Java (and would Java still have been "not scary") back in the 90s when it came out?
It wouldn't have been particularly hard from a language, standard library, and virtual machine perspective. It would have made converting legacy C++ programmers harder (scarier). Back then the average developer had a higher tolerance for defects because the consequences seemed less severe. It was common to intentionally use null variables to indicate failures or other special meanings. It seemed like a good idea at the time
> It would have made converting legacy C++ programmers harder (scarier).
And that, right there, is all the reason they needed back then. Sun wanted C++ developers (and C developers, to some extent) to switch to Java.
Wait a minute, this isn't an AI article...
[dead]
This article absolutely reeks of ChatGPT to me. For example:
>With this in mind, we set out to automate the conversion process and minimize interference with our developers’ daily work. The result was a tool we call the Kotlinator that we built around J2K. It’s now comprised of six phases:
followed by a list of descriptions of the "phases" which only sort of make sense for the name given to them, and are utterly incoherent as actual phases in a process (and grammatically inconsistent). For example, one of the cited "phases" is... "headless J2K". In other words: they have one piece of software that wraps another, and it - gasp - doesn't use the wrapped software's GUI. Aside from being entirely unremarkable, that's neither a phase in a process nor a component of a tool. It's a fact about the component.
LLMs write like this all the time - and it's clear evidence that they do not, in fact, do anything like reasoning, even if they can sometimes be manipulated into generating a second piece of text that resembles an analysis of the first one. The resulting description is so weird that I question whether the authors actually checked the LLM's output for accuracy.
Any human writer who gives a damn about good writing and has any skill, would not allow "it" to refer to "the conversion process" two sentences back when "a tool called the Kotlinator" has been introduced in the interim (or, if that were the intended referent, would notice that tools are not "comprised of phases"). Such a writer would also not come up with abominations like "the conversion process is now comprised of six phases" where "we now use a six-phase conversion process" would be much clearer. Certainly, a six-point bullet list produced by a competent writer would label them in a grammatically consistent way (https://en.wikipedia.org/wiki/Parallelism_(grammar)) - not with an abstract noun describing an action, two participles, two concrete (well, as concrete as software ever is) nouns and a command (who, exactly, is being told to "build" the "error-based fixes" - whatever that means - here?).
I'm starting to feel like Mark Twain.
----
On the other hand, I was cynically expecting some mention of using AI for the actual task, and that doesn't seem to be the case.
(Also, the "reactive" web design is broken. The page overflows horizontally for some range of window widths, without causing a horizontal scrollbar to be added.)
Calling it a 'phase' doesn't seem that weird to me? For each file, they go through a number of steps to translate it, and one of those steps is to run the J2K tool on it. The next section is just describing how they implemented that step: the J2K tool is normally enmeshed into the rest of the IDE, but they managed to jerry-rig a solution to run the relevant code by itself, without running the rest of the IDE with it every time.
It could possibly make sense if the phases were described as e.g. "performing a deep build, preprocessing, running J2K, postprocessing, linting and applying error-based fixes" (i.e., all participles). But then the descriptions should be about how those phases are implemented (i.e., this is the place to mention the work of setting up a headless version of J2K so that it could be run in an automated pipeline) and how they contribute to getting the desired result. The description "The J2K we know and love, but server-friendly!" is utterly useless here, and also practically the archetype of what ChatGPT writes in "promotional material" mode.
You are tilting at windmills. The article exhibits the kind of English I'd expect from someone sharing a technical development in an acceptably-perfunctory style for a company blog.
It's as if chatgpt got its style from somewhere... For a hyper sanitized LLM like chatgpt, it's not surprising that it will sound like a sanitized corporate blog (that I still personally liked).
> cynically expecting some mention of using AI for the actual task
Well, you could combine AI with correctness preserving transformations, so you get the best of both worlds (i.e., correctness AND a translation that keeps the resulting code close to what a human would write).
What's not a "phase" about running J2K?
The phase is, as you say, (the act of) running J2K - not a property (being headless) of the J2K implementation used.
I am surprised they did not use LLMs like Claude or maybe even train their own Llama version to do this. In my experience LLMs have been very reliable in translating code.
It seems pretty obvious to me why deterministic code translation is preferable for something like this.
There is zero chance they’d end up with a functional code base after attempting to do this.
Wait, no AI is used ? Amazon claims big about their code conversion from Java 8 to 17 using Q developer (GitHub Copilot equivalent). Why not use Llama3 models here? Can't they help doing such?
Why use something that works 90 times out 100, but blows up ten times, versus an automated process that can be provably correct all the time?
This is an application where LLMs should be the obvious choice, it is Machine translation the thing they are supposed to excel at. Why not use them? Likely a lack of data. You would need lots of Kotlin data (I’m sure lots of Java data exists), and the data would need to overlap so the LLM could understand the mapping.
"Android development at Meta has been Kotlin-first since 2020, and developers have been saying they prefer Kotlin as a language for even longer."
Not one link to an opinion piece or two regarding: "kotlin vs java". The nearest thing I found was "What makes Kotlin different".
This sounds somewhat like a debate about which Germanic language is best. German, Dutch and English are all "Germanic" but which is "best"?
Obviously: That's the wrong question to ask and so an answer is doomed to failure.
In the end, does your generated machine code implore the CPU and associated hardware to do what you want it to more efficiently in some way that is not an abstraction?
Pissing contests rarely excite me. Why did you do this?
> Why did you do this?
You literally quoted the "why": their developers prefer writing Kotlin over Java for Android development. That's it. They don't need further justification. They didn't need a "Kotlin vs. Java" comparison, and they're not really evangelizing Kotlin all that much. They're simply stating a fact for their organization: Kotlin is a better fit for their developers than Java is.
> In the end, does your generated machine code implore the CPU and associated hardware to do what you want it to more efficiently in some way that is not an abstraction?
Most shops don't care about this too much. The most important thing is developer productivity.
(And yes, this is why we have bloated garbage like Electron these days; sometimes some people value developer productivity to unhealthy extremes.)
So why bother posting on HN if it was inevitable?
Seems to me the article focused on how they did this big job, rather than why.
I think a pissing contest is precisely what they were hoping to avoid by not linking to some "Kotlin v Java" blog post. That their developers prefer writing in Kotlin is a basic premise for the rest of the article, not its thesis.
The target audience for this kind of article - Android engineers - do not need any more convincing on why Kotlin is superior to Java. This is a debate that has been settled in the Android community since a long time ago. I'd be willing to bet money on there only being a rounding error of Android engineers advocating the use of Java over Kotlin these days.
Blimey, DV'd within seconds!