I like this "problem" because it encourages small programs. Also, Scala's type system is just very complex.
If you have a 2000-file program with cyclical dependencies you are screwed. If you use Scala-IDE (which has an embedded sbt doing incremental compiles, which become non-incremental if your dependency structure is fail and you have a huge SCC) on such a project you are doubly screwed. That's not a problem with Scala. Bigass programs aren't a good idea in any language. The programmer-to-program relationship should be one-to-many (for engagement, productivity, modularity, code quality) and not the "enterprise" reverse.
I'm sorry but this is possibly one of the worst comments I've seen... There is a conservation of complexity that you just can't get rid of in an application. To suggest that Scala encourages small programs, you are also suggesting that Scala is only appropriate for small problems, and thus it isn't a scalable language.
Ultimately, the baseline requirements drive the minimal complexity of code. Sure a poor programmer can drive up the complexity with unnecessary constructs, but the converse is not true: a great programmer can drive to simplicity. For example, a flight control system for a 777 can never be reduced to a single file of 700 lines of code. It will likely always be 2000+ files, and have lots of inter-dependencies.
So I guess (run time safety issues of Scala aside) Scala is not an appropriate language for complex problems by your own words?
Scala's compile speed (with incremental compiles) is generally acceptable if you have reasonable dependency patterns. If you have cyclic dependencies, it's a problem. However, cyclic dependencies are signs of things that are wrong in development practices that can't be blamed on the language.
You mentioned flight control systems: those are unlikely to be written in Scala, being real-time systems, but if one were, it would not need to be compiled and recompiled on the fly.
Scala's a great language but it's not the right language for every problem. No language is.
GHC does a lot of the same things (arguably more stuff) and it's extremely fast, especially if you don't use -O*.
Anyway, type-checking is probably the fastest part of compilation so there's no reason to give up Hindley-Milner for speed reasons. (One of the best features of GHC is that you can use GHCi to typecheck)
OCaml doesn't have Haskell's type-classes or Scala's implicits. Scala's subtyping is also nominative (versus structural) and it's basically impossible to do type-inference for nominative subtyping. Whether this is good or bad, the fact remains that Scala runs on top of the JVM while Ocaml doesn't. If you ever work with Ocaml you'll notice that the type-inference system breaks down when you're working with OOP and in Ocaml you're really dealing with 2 type-systems shoved in the same language.
What Ocaml does really well, compared to both Haskell and Scala is type-inference, but it's kind of a weak language IMHO as far as functional programming is concerned.
I also don't get all this complaining, because in practice Scala compilation speed is not a problem. After working with Scala in production for a couple of months now, I'm beginning to suspect that all this is just hyperbole perpetuated by people that never worked with Scala a single day in their lives.
When I last evaluated Scala, I found the following:
* Launching the interpreter takes 10 seconds; 50 times slower than Haskell.
* Compiling a small module takes 15 seconds; 10 times slower than Haskell.
* Running 100 command-line unit tests on a small compiled module takes 1.5 minutes; 100 times slower than Haskell.
I also found Scala's type system requires many type annotations even in trivial code. For example, here Scala fails to infer the type of `optBar`, apparently because `None` is also a type:
scala> class Foo {
| def setBar(bar: Int) = optBar = Some(bar)
| def getBar = optBar.get
| var optBar: Option[Int] = None
| }
defined class Foo
Moreover, Scala's implicits can pose a significant barrier to understanding other people's code.
While OCaml's type system may not integrate objects cleanly, in everyday use type annotations are not necessary, perhaps because objects aren't commonly used.
Similarly, while it's possible to confuse Haskell's type inference by using advanced language extensions, in everyday use one tends not to run into such problems.
With SBT you're only going to do that once per work session. Plus the startup time isn't as bad as you make it sound. I've had a much more painful experience with Ruby (Rails).
> Compiling a small module takes 15 seconds
That hasn't been my experience. I just did a test for a project of mine and it took 5 seconds. Without line-counts such metrics are useless (how small is that module anyway?). Also many Scala projects are composed of multiple subprojects that are efficiently managed by SBT, so a root compile triggers compilation for all these sub-modules, but context-switching between them is what increases compilation cost.
> Running 100 command-line unit tests on a small compiled module
When in the world are you ever going to do that? Like seriously?
> Scala fails to infer the type of `optBar`
In practice it's good to annotate the signature of public functions. Haskell developers do that too, because really, when a developer looks at the definition of "optBar" are you seriously going to suggest that they should be looking at other parts of the code to infer what it returns?
IMHO, this is a feature, as what you want is even worse than what you get with a dynamic language, but YMMV.
> Scala's implicits can pose a significant barrier to understanding other people's code
I haven't met a single library or project of significant size that isn't making use of implicits in one form or another. Whether they are called global state, singletons or parameters injected through DI, we are really talking about implicit parameters.
The difference is that Scala gives you the means to document these implicits in the signature (referential transparency FTW). They are also statically type-safe, can be overridden and have the extra benefit that they make possible some powerful techniques, like type-classes, something that Ocaml isn't capable of.
I see you're attempting to disregard my results by making assumptions about my workflow. I'm not going to respond to these comments.
> I just did a test for a project of mine and it took 5 seconds. Without line-counts such metrics are useless (how small is that module anyway?).
Obviously, comparing absolute times between two unknown systems isn't going to make sense. I was comparing how long it takes to accomplish the same thing on my machine in Haskell and Scala.
> In practice it's good to annotate the signature of public functions. Haskell developers do that too, because really, when a developer looks at the definition of "optBar" are you seriously going to suggest that they should be looking at other parts of the code to infer what it returns?
I agree, annotating type signatures of top-level functions is a good practice. However, in the above example `optBar` is an internal variable in class `Foo`. The point here is I found Scala's type inference to be incapable of dealing even with trivial code.
> The difference is that Scala gives you the means to document these implicits in the signature (referential transparency FTW). They are also statically type-safe, can be overridden and have the extra benefit that they make possible some powerful techniques, like type-classes, something that Ocaml isn't capable of.
Haskell does type classes without the need for implicits. Also, true referential transparency requires control over side effects, which is something neither OCaml or Scala are capable of, but Haskell is.
> Haskell does type classes without the need for implicits
If you really think about it, type-classes are all about passing around a global vtable implicitly. If anything, Scala's implicits are a superset of Haskell's type-classes and allows for techniques that Haskell isn't capable of.
We could debate all day about what's better. If you're going to make the case that Haskell's type-classes are easier to reason about, then I can make the case that a dynamic language beats Haskell on reasoning about it any day of the week. In the end it's all about tradeoffs.
> true referential transparency requires control over side effects, which is something neither OCaml or Scala are capable of, but Haskell is
With all due respect, that's just bullshit. First of all because there is no such thing as "true referential transparency", second because most techniques that are available in Haskell for dealing with side-effects are also available in Scala. As examples, Scala's library is filled with monadic types and the Play framework makes use of Iteratees for composing HTTP responses, something which makes Comet/WebSocket a peach to deal with.
> With all due respect, that's just bullshit. First of all because there is no such thing as "true referential transparency", second because most techniques that are available in Haskell for dealing with side-effects are also available in Scala.
A referentially transparent expression is guaranteed not to have side effects. A Haskell function which doesn't advertise in its type that it may have side effects, by being in the IO monad, can be trusted to be referentially transparent. This cannot be said about OCaml or Scala functions.
Pardon me...are you saying you want something in the type signature of a scala function that guarantees said function will not do input output to files sockets etc...and does not make a call to random number generators and does not modify global vars...so basically all it does is operate solely on params passed to it...absolutely no side effects...is that what you are asking for? That would be awesome from a testability standpoint...
Yeah I know exactly what you mean in Haskell. Its just that in scala given the java inter-op there are a whole hunch of escape hatches that will always be there. I remember in the coursera exercises when you submitted code that used mutable vars...it would flag your code and deduct points. So essentially even if someone provided a purefn annotation...to actually enforce that, the compiler would have to scan each function and disbar i/o, calls to rng, calls to global vars... tall order.
> If you really think about it, type-classes are all about passing around a global vtable implicitly. If anything, Scala's implicits are a superset of Haskell's type-classes and allows for techniques that Haskell isn't capable of.
First, I'm not a Haskell expert, but from what I understand there are no vtable passed around nor attached to "objects" in Haskell.
Type-classes are used to find an implementation given an instance. Without extensions you cannot do this:
The reason for why it hasn't been a problem for us is because we've been making heavy usage of SBT's support for sub-projects. Scala makes it easier than other languages to decouple components, so after a while you begin to spot possible sub-projects all over the place.
The project I'm working on right now is about 15kloc, but it's split in 3 sub-projects. Coupled with incremental (and continuous) compilation, this compilation speed issue hasn't been a problem for me.
Also, if you look around, many open-source projects built with Scala follow the same technique. It's quite sane too.
Incremental compilation suffers from the fragile base class problem. In a well structured app with proper encapsulation and rigid (and type annotated) interfaces, it helps a lot.
If your project isn't properly encapsulated, incremental recompilation will often come close to a full recompile, or at least recompilation of a big chunk of code.
For example, if you delete AbstractFooBase.unusedMethod, sbt will recompile every file everywhere that references a concrete Foo instance.
(In principle it should only need to recompile files which reference unusedMethod, but it's not that smart yet.)
On the other hand, if you do want a cutting-edge language with a powerful static type system, and a compiler an order of magnitude faster than Scala's, you could try Haskell.
If you have a 2000-file program with cyclical dependencies you are screwed. If you use Scala-IDE (which has an embedded sbt doing incremental compiles, which become non-incremental if your dependency structure is fail and you have a huge SCC) on such a project you are doubly screwed. That's not a problem with Scala. Bigass programs aren't a good idea in any language. The programmer-to-program relationship should be one-to-many (for engagement, productivity, modularity, code quality) and not the "enterprise" reverse.