4 ms·
Kotlin was originally designed for running on the JVM, so it is generally object-oriented, but the language and the standard library allow for and encourage a f
by moritzruth 2y ago
Kotlin was originally designed for running on the JVM, so it is generally object-oriented, but the language and the standard library allow for and encourage a functional coding style.
Kotlin is well-suited for DSLs, especially declarative ones (see kotlinx.html[0]).
[0] https://github.com/Kotlin/kotlinx.html https://github.com/Kotlin/kotlinx.html
- graypegg 2y agoHuh, I've never actually looked too hard at Kotlin. That is a lot more syntactically flexible than I thought it was! That HTML builder reminds me of Ruby DSLs a lot.
- dtech 2y agoIt's not really syntactically flexible, but made explicit syntax for the use cases other languages used flexibility for, like DSL-builders and extension methods. This avoid the problem that you have in more flexible languages where everyone does a pattern in a slightly different way.
- lolinder 2y agoThis understates how functional Kotlin is—you wouldn't say that Scala is generally OOP with some functional support just because it was built on the JVM. Scala's clearly a functional language with some OOP support. Kotlin, in turn, is very balanced. The standard collection library uses OOP syntax (chains of method calls), but is extremely functional in its philosophy towards how we think about manipulating collections.
- ragnese 2y agoI think a comparison to Scala is apt. Working with both languages makes it quite clear to me that Scala is most certainly much more functional than Kotlin. Kotlin is really not very functional in practice, in that it really doesn't encourage a functional style, nor is it optimized for the patterns that are common in functional programming. Scala has `Try` and `Either` for modeling domain failures as values as opposed to throwing (unchecked) exceptions, which are side-effecting. Scala also has for-comprehension syntax built in to make it more convenient to compose and chain fallible operations that use `Try`, `Either, `Option`, etc. Kotlin's `Result` type is not designed for modeling fallible operations (not a sealed class, no type parameter on the error variant, error must be `Throwable`, etc), and Kotlin does not offer convenient syntax like for-comprehensions despite being more than able to (given that many of us have implemented near-perfect analogues to Scala's `Try` and for-comprehension syntax in Kotlin). Similarly, no official Kotlin libraries or APIs ever return errors as values and always opt for throwing exceptions instead. Scala's collection types are implemented as persistent collections, which are optimized for cheap updates. If you want to avoid direct mutation in Kotlin, you have to make a full copy of a collection. Scala's mutable and immutable collection types are actually distinct from each other and cannot be used interchangeably. In Kotlin, List<T> is a supertype of MutableList<T>, which means I can pass a MutableList into a function that expects a List. That means that the list can be changed in another thread while my function is running, so I can't even assume that checking `list.size` at two different points in my function will return the same value. Scala has actual type classes. Kotlin has extension functions which are not nearly as useful (and they have very surprising semantics when it comes to static vs dynamic dispatch). Type classes are certainly not required for functional programming with a statically typed language, but it definitely helps when it comes to modeling things without needing to lean on writing more classes and/or utilizing inheritance. Also, Kotlin's "functional" APIs on collections are much more janky than Scala's. For example, if I have a `Set<T>` and I call `.map((T) -> R)` on it, Kotlin will give me a `List<R>` while Scala will give me a `Set<R>`, which makes way more sense. Kotlin is cool, and it would be dishonest for me to say that it's not at all functional, but after having worked in other many other languages, I'm very comfortable saying that Kotlin is an OOP/imperative language first with some functional stuff added in (sealed classes, convenient lambda syntax, top-level functions, and some of the typical collection combinator APIs). Whereas Scala is quite clearly designed to be actually GOOD for functional programming without taking an extra-hard performance hit.
- vips7L 2y agoExceptions are great. Whats not great is not having them expressed in the type system via checking. I think Kotlin's greatest mistake is not improving upon checked exception handling. Though it looks like they're going to be moving forward in the future with errors as values via union types [0]. Scala also has some experimental work around putting exceptions into the type system via capabilities [1]. I really like Scala's solution because it lets checked exceptions work across higher order functions and Scala has enough syntax sugar to make handling exceptions pain free. [0] https://youtrack.jetbrains.com/issue/KT-68296 https://youtrack.jetbrains.com/issue/KT-68296 [1] https://docs.scala-lang.org/scala3/reference/experimental/canthrow.html https://docs.scala-lang.org/scala3/reference/experimental/ca...
- ragnese 2y agoI have LOTS of opinions about this topic, but I'll try not to ramble. I always found checked exceptions (Java) to be mostly fine/good. I sincerely believe that a lot of the hate for them in the last decade is just cargo culting. I get a small dose of schadenfreude when I see someone doing mental contortions to simultaneously explain why Rust's Result type and handling (and similar features in other langs) is awesome, and Java's checked exceptions are terrible and definitely not 95% similar in DX and semantics... The one point against checked exceptions for me is that if I'm trying to model a domain failure, it really doesn't make sense to collect a stack trace. For example, if I'm writing a logIn function that takes a username and password, then it's totally normal for the username and password to be invalid. Why would I want to spend the CPU time collecting a stack trace when someone simply typed in an incorrect password? Do we want to collect a stack trace when the password is correct and the user gets logged in? So, in that sense, I do have a small preference toward expected failure modeling to be somehow different from "true" "exceptions". I do also agree with you that Kotlin's biggest original sin was to throw away checked exceptions without replacing the concept with ANYTHING. Of course, as you've pointed out, they're backtracking on that somewhat by trying to add this concept of errors as a kind of ad-hoc union type. (aside: they also backtracked on their choice to not have type classes because "extension functions are good enough" by trying to do context receivers, which is ending up being really hard and probably more complex than just doing damned type classes in the first place...) I do kind of like the direction that Swift is moving with finally adding specifically typed throw signatures (essentially Swift now has checked exceptions, but they're aren't actually traditional exceptions because they don't collect stack traces and unwind the stack- they're just syntax sugar around a Result/Try type). But, my prediction is that the pendulum is starting to swing back in favor of checked exceptions. In the next decade we'll continue seeing languages adopt mechanisms that are essentially checked exceptions. But, they will be slightly different and definitely called something else so that we don't have to admit that we were wrong to shit on the idea for 15 years. EDIT: Also, I do follow Kotlin developments closely, but I haven't actually worked in Scala for several years, so I had no idea about this capabilities idea. Thanks for the link.
- School-Cotton 2y agoThe JVM doesn't necessarily imply object-orientation. Clojure (a JVM language) is not really object-oriented at all; I'm not an expert but I think the only time objects are used in idiomatic Clojure is when doing interop with Java libraries.