Scala on the JVM !
Okay, cool – just wanted to make sure the ecosystem comments above were correct.
Another strength I would highlight is Scala’s very strong type system: it is easier to express precisely what you mean, in a compiler-checkable way, than it is in most languages. This tends to make code more maintainable, and means that more bugs can be caught before you even run it for the first time.
In terms of the flexibility mentioned above, it provides both one of the most powerful object-oriented programming environments, along with a solidly good functional-programming one.
As a Scala beginner coming from Java background, biggest advantage is that it pulls you out of your comfort zone , and makes you approach programming problems in a better way.
I am programming in Scala for few years and in Rust for a few months. Rust feels very limited compared to Scala. Rust’s primary tool for code reuse is trait which is basically Haskell’s typeclass, i.e. it doesn’t work like Scala traits. You can implement Rust trait for a type only once and the implementation must be either in the file with enriched type or in the file with trait definition. Rust doesn’t support typical OOP features like virtual method overriding so you don’t have a choice - you need to always go the typeclass route.
The choice of garbage collection by reference counting in Rust IMO prohibits efficient multithreaded functional programming. If you use atomic reference counting pervasively in persistent data structures (so multiple threads can share data instead of cloning it or transferring ownership of it) then you get multiple times slower performance than with tracing garbage collection. Overhead of atomic reference counting over plain reference counting is big so Rust has both types of references and warns about the performance of the atomic one.
Overall, you can emulate Rust i.e. write in Rust-style in Scala pretty easily, but the reverse is not true. Emulating Scala constructs is often infeasible in Rust.
I don’t at the moment have time to read what the others have said. My apologies if I’m repeating anyone here. But, I doubt that happening because I’m in the extreme minority here with C++ being the mother (programming) tongue.
For me, the ONLY thing in Scala that I really bow before is its brevity. In C++, we suffer from syntax noise all the time. Scala’s support for Functional Programming is also way better than C++. But, that’s not as better as its brevity.
I, however, do not use Scala for any of that. It turns out that I am an academic. Personally, I am also a multiparadigm guy. Scala is my best shot for Programming Languages publication – which as a part of my job I need to do.
I’ve used Scala for a long time, used C++ for years and years before that, and dabbled in Rust for as long as it was there to dabble in. So all this is my opinion, but it’s at least opinion honed after a good deal of experience.
The advantages of using Scala over both C++ and Rust are:
- You can use the JVM. There’s a huge ecosystem of libraries and a huge amount of knowledge in how to deploy things on the JVM, keep them running robustly, and so on. It is much easier to achieve these things on the JVM (for now) than in any other context.
- You have really powerful abstraction mechanisms, an amazing collections library, and a very DSL-friendly language, allowing you to write code at a higher level and offload (safely!) many problems to the compiler once you figure out how to express them.
C++ and Rust will, if used moderately carefully, run considerably faster than Scala for performance-critical tasks, most especially when using generic code that handles primitive values (integers, etc.). Generally in carefully-crafted areas you can often get 2-3x faster in C++ or Rust than Scala or Java. A lot of it comes down to you caring enough about performance to design for performance ahead of time, and the profile and microbenchmark afterwards. You have to put in a pretty serious effort before the language choice matters more than your will to make things fast in the language you’ve got.
The earlier comment about garbage collection being an advantage is wrong. If you don’t know how to use C++ and Rust properly, maybe you’ll end up in a situation where a GC actually helps you. You really have no excuse whatsoever with C++ to lose to a GC system, though, if you care. With Rust you have a small excuse–memory arenas require placement new which you get in nightly only for now. But really, the extra work that the GC has to do in checking, pointlessly, to make sure you still haven’t lost your reference to something is quite considerable. If you’re going to use C++ or Rust and not think at all about how you create or use memory–well, true, it’s not going to be super-fast, and it won’t be all that fast in Scala either, so are you sure you really meant to go fast? Anyway, Rust excels at efficient multithreaded programming; if you insist on “functional” instead of “correct and safe”, you’re doing it wrong. Rust will help you get it correct and safe. C++ won’t, but should you happen to get it right anyway, it, like Rust, will outperform Scala or anything else on the JVM.
Rust is very good for writing code that will not go wrong, even though you are forced to think about ownership of variables all the time. The compiler will check you. I feel much more comfortable that my Rust programs are free of problems that could be detected at compile-time than I do with Scala. With C++ and lots of templates, it’s kind of scary. (Python, for comparison, is a terrifying free-for-all.)
Although Rust is not quite as good at Scala at allowing you to write high-level code, it is pretty good at writing safe time-saving abstractions. I choose it over C++ whenever I can (which isn’t very often, since I have a lot of C++ code and use things like SIMD a lot which aren’t stabilized in Rust yet).
If you want to program in a pure functional style, Scala is also the best, due to the advanced type system. Rust’s type system isn’t yet quite up to the task of talking about complex type constructs (technically, it doesn’t have higher kinded types yet and there isn’t even a plan for traditional HKTs; it will be getting associated type constructors which can handle some of the complexity). Note that Rust’s safety guarantees pretty much eliminate the need to use immutability for safety; you can still get yourself into trouble by mutating things without realizing it, but you can also use functions to return new values and then forget you’ve done a type-preserving transformation. Do not use C++ to program in a functional style; closures have awful syntax, and the interaction with templates is weird, and the error messages you get when something goes wrong are exceedingly long.
If you want it to be really easy to pick up and use external packages, Rust and Scala are so far ahead of C++ that the difference between them hardly matters. Rust’s cargo build tool excels at making things simple. Scala’s build tools are all more complicated, but JVM deployments are often more complicated, and the tools will help you with that.
If you like object-oriented programming, Rust doesn’t really have that; it uses a trait system instead that has some but not all of the code reuse and abstraction capabilities of object-oriented programming. C++ and Scala both have first-class OOP capabilities. Neither really is a clear winner there; they both have their wrinkles. Scala’s is geared more towards compile-time safety with generic code working properly at runtime, while C++ is geared more towards code generation via templates. C++ usually performs better as a consequence, but Scala has more comprehensible error messages (and more comprehensible complex types, at least to my eye).
If you need to access hardware, stay away from Scala and the JVM. The whole point is to abstract that stuff away. The JVM makes FFI relatively awkward, so you can’t use it to add functionality to other platforms either. If you want to write a module for something else (Python, let’s say), it’s got to be C++ or Rust. You could try Scala-Native, but it’s still early in development.
If you don’t like having to think about memory, use Scala! Rust makes you think about it all the time, and helps you think about it. With C++, you can think about it or not, and it will help you a bit if ask it to, and crash at runtime if you didn’t think about it when you needed to. Overall, for “don’t worry about memory”, Scala is clearly in the lead.
Scala has a REPL. Trying to do this in C++ or Rust is awkward at best. If you like programming and testing things live, this is a huge benefit.
Anyway, to reiterate: Scala can take advantage of a HUGE ecosystem of libraries for the JVM and gives you the build tools to consume them easily (C++ has a HUGE ecosystem that is hard to use, and Rust’s ecosystem is still quite small and using non-Rust libraries gives a poor experience); has a highly advanced type system that includes both functional and object-oriented capabilities giving you a larger and more potent set of tools for effective abstraction and code reuse than in either C++ or Rust; and has a powerful straightforward set of collections that help you turn a ton of really common tasks from half-page functions into one- or two-liners (more effectively than C++ or Rust can).
Not in comparison to C++. Scala is a JVM language with erasure also in place. And, erasure is so nasty when it comes to type juggling. By design, the Scala type system has no independent existence at compile-time. This often bites me for my frequent need for lightweight family polymorphism.
Erasure is a mixed blessing. It does amazing things for parametricity. On balance, I certainly would always choose to have erasure.
There certainly is a matter of choice here. But, erasure is just one way to provide type parametricity – a very bad way though.
Qualifying something as “A very bad way” doesn’t seem to indicate it’s a matter of choice at all.
In my opinion it’s the best way - but I certainly won’t be saying that it’s the way that you want it to be.
Maybe a less judgmental style of discussing peoples preferences would yield more interesting discussion.
If you want to compile a parametrized method without knowing with what parameter it is called, the only ways to do this that come to my mind are erasure or capturing the type dynamically as a value. The first is the default in Scala and the second you can implement using TypeTags or the like. Am I missing something?
AFAIK, in C++, parametrized methods are only compiled once the call site is known, and then you get an instance for every type it is called with. So, there is no separate compilation of method and call site. Would any one want such a thing in Scala?
Template specialization is pretty neat, and I could entirely imagine people wanting that.
Indeed. This is another plus for Scala over C++, which I missed.
The C++ type system is way safer than that of Scala. The latter type system has no real distinction between compile-time and runtime. (See my former posts.) In C++, static entities are categorically different from runtime ones.
Well, when I say “a very bad way”, that’s because it comes with tons of serious drawbacks. One loses important information too early. And, of course, that’s still one way to provide type polymorphism…
Why would you want to discard that information? That’s loss of useful information.
Why would you want that?
Making that information unavailable, either by discarding it with erasure, or hiding it in some other way, is what parametricity is
See for example Failed Experiments: Fake Theorems for Free
You either erase it (apparently a very bad way), or you hide it in other ways (which is apparently a lot less bad).
I just recalled another plus for Scala over C++. Scala has a neat module system. C++ still lacks that.
Parametricity? Parametricity by what? Do you mean type parametricity? If so, then, I’m afraid, no… That’s not the definition. Type parametricity is making the type a parameter of the code.
I’m not sure how C++'s type system is “safer” when your templates can do completely not what you mean as long as the method names match up as used, and you can specify nonsense like
myThing<double, Vector<int>> where the former is supposed to be the argument to the latter. (Note: you might say: okay, write
myThing such that you call it as
myThing<double, Vector>, but that doesn’t work if you want to allow non-parameterized classes as the “container”.)
Since templates are code generation, the type system catches any type errors in the generated code, but it has very little safety in what you can ask for. In Scala you can’t even ask without an error.
Also, when you have a huge number of types in play, the code generation method creates enormous binaries; generics keep the binaries small by handling the abstraction earlier. This is something I think Rust does really well: it will specialize if you parameterize over a trait, but it will compile a generic interface if you just use the trait bare. So you effectively have both type erasure or not, controlled by the declaration site. In Scala, erasure is pervasive and you have to use type witnesses to effectively smuggle the compile-time information in.
I’m not sure why you think the Scala type system is unsafe, though. If you’re casting stuff with
asInstanceOf, then you should be thinking of that as equivalent to
(MyWhatever*) casting in C++, or at least dynamic_cast. If you avoid that, you’re on pretty solid ground.
The two systems are each quite elaborate and quite capable, and they are both quite different from each other.