etc. In practice you use a linter to enforce a style and it's not a problem.
For the curious. The combinations follow out of fairly simple rules:
() and {} are interchangeable for expressions - altough {} can contain multiple statements and () only an expression.
x op y is equivalent to x.op(y)
Type abscriptions - x : Type - are optional and will be inferred if possible
{ case ... } is the pattern match construct and works similarly to a function with 1 parameter.
How is that a bad thing? It's the same in other languages and it is nice to have some flexibility.
E.g. lisp: use whichever type of parentheses you want
Or Kotlin: use short syntax lambdas fruits.filter(it == apple) or long syntax fruits.filter(fruit -> fruit == apple) or with annotated types: fruits.filter{fruit: Fruit -> fruit == apple}
Sometimes brevity is good for the reader, sometimes more details are good for the reader. Not everything is a nail.
The flexibility is perfectly fine when you're programming something on your own. But when you're in a team, or worse, in a large company, it starts to be a problem that everyone can use their own style. It's much easier to read someone else's code when you have a common code style.
And in Scala there are not only many different ways to use the standard libraries or to structure your code, there is a pure FP vs OOP-style FP schism as well. Because of this, you can qualify as a senior Scala developer for one job but only a junior in another.
I've been writing Scala for the better part of 8 years now and I have my list of gripes about the language, but this is one I have never understood.
Yes, there are different code styles you can use with Scala (standard vs infix notation, parens vs braces, etc) but all of that can be standardized with code formatting tools.
In terms of FP vs OO style I don't think that is different for any other language. No matter which programming language you choose you have to make decisions about what sort of patterns you want to use in which scenario and enforce that across the team. I have seen many Java projects where a relatively small codebase has approximately every GoF pattern implemented somewhere (and a few novel patterns just for good measure).
The opposite. If I write code on my own just for myself I use the highest level of brevity.
But if I write code for a team, then in some places I will use explicit type annotations and variable names to aid people unfamiliar with the code to understand what's going on.
What you are saying is pretty much "it's easier when everyone only uses nails, because then all I have to bring is a hammer". I think it's good to use screws sometimes.
However, your second point I agree with. OOP vs. FP is a different story. This is about paradigms not about mere syntax. So here, a team must be aligned, which can be a challange when using Scala. It's not a language for corporate drones.
If it was so simple as just using explicit types :)
And this sort of flexibility is exactly why Scala is hard to read. Perl is another example of a great language that allows you a lot of flexibility. In fact, the ability to use just any symbol for a method is the worst thing about Scala. You quickly realize it once you start using Scala libraries some of which basically introduce you to a new Scala based DSL. This quickly becomes a nightmare to work with if several such libraries are used in the project.
Have you used Scala before? I used both perl and Scala and I had to laugh when I read what you said.
Yeah, Scala makes it possible to write cryptic DSLs. I just don't use libraries that do that, but there are not many such libraries anymore anyways, that was mostly abused in the early days of Scala. Now, 15 years later, that's almost non-existing anymore.
But not having symbolic characters in method names is just horrible. Here is Java code that calculates some datetime:
LocalDateTime started = LocalDateTime.parse("2018-03-22T19:00:00")
LocalDateTime finished = start.plus(Duration.ofMinutes(30)).plus(Duration.ofMinutes(15).multipliedBy(4)).plus(Duration.ofSeconds(45)).plus(Duration.ofMinutes(4).multipliedBy(3)).plus(Duration.ofSeconds(30))
Here is the exact same calculation, but with Scala's "symbolic" characters:
val started = LocalDateTime.parse("2018-03-22T19:00:00")
val finished = start + 30.minutes + 4 * 15.minutes + 45.seconds + 3 * 4.minutes + 30.seconds
I don't need very long to know what I find far more readable ;)
> But when you're in a team, or worse, in a large company, it starts to be a problem that everyone can use their own style
I was in many failed or semi-failed projects and code style was never the problem. Yes, people do use slightly different styles, but style is really easy to enforce - there are tools to do it automatically. And even if somebody misplaces a brace or writes `map(_.length)` instead of `map(x => x.length)` this doesn't impact the readability as long people in your team know the language. If a project fails to meet the deadline because of the code style it is not because of people using different styles but because of developers bikeshedding about the code style in code reviews instead of doing real work.
The Scala ecosystem has tooling to enforce code style across teams.
Your second argument is true, Scala is a big language, and there are three main paradigms: better Java, OOP+FP, and pure FP. It's similar to how C++ is used, for some people is just like C with some improvements, but there are people using lots of C++ features.
This doesn't quite answer your question, but, the Python community takes seriously the idea of There's Only One Way To Do It, as part of their philosophy on complexity.
1. just put everything ad-hoc into a dict or a list (the "PHP" way :D)
2. use a namedtuple
3. define a class
4. define a dataclass (preferred) but works only if your Python version is recent enough
In Scala? Just use case classes and this is the only recommended way (#1 is very impractical so noone does that, #2 doesn't exist, #3 possible, but impractical when you have #4 in all versions).
How to map a collection in Python?
- Start from an empty one and add mapped items in a loop
- Use map + lambda
- Use list comprehension
- Update all items in place with a loop
Scala goes quite far in the flexibility, maybe too far. Agreed.
But is python really so good? I'm not a heavy python user, but there are loops and list comprehensions and map. There are also optional type annotations now, should you always use them, or not? How about "a is b" or "a != b"? How about environments and build tools...
I think that go might have been a better model student.
This is like saying, airplanes are too complex. There are so many knobs and buttons. I'd rather just walk or ride a bike everywhere.
Scala is a very powerful, very expressive language. There are some features which you can just choose to leave out. If you do, you end up with a very clean, concise, and powerful language that makes you really productive.
I've literally had moments where I made my algorithm 5x faster just by adding 4 letters: '.par' in front of an operation. Instantly it got parallelized without my having to do anything, and the processing time got cut 5x.
Its more like saying: I need to cut some paper ( i need to program): Swiss army knifes (scala) are too complex, I just need a pair of pair of scissors (lisp / other languages).
Having switched from Scala to Python recently, I must say Python has probably 5x more ways to do things than Scala and I can't see people complaining on Python's complexity.
This is something that really surprised me, because "one obvious way " was announced to be a part of Zen of Python.
IIRC one of Odersky's stated goal for Scala 3 was to address exactly this issue. That being said, it is developed in a research setting so I guess even if they restrict these features now, there will be new ones added over time...
Scala has always been an opinionated language. The main difference is that this time is holding opinions against itself. Previously, opinions were held against Java, that's why there are features such as case classes, traits, and immutability by default. They answer to Java problems.
Scala is now mature enough that some issues have emerged. Typeclasses are cool, but programming typeclasses in Scala is a bit clunky. The type system is very powerful, but it could be even more powerful and that would actually help people write simpler code. And there are a bunch of unused features that could be removed. This is what Scala 3 is about.
Scala 3 tries to be more opinionated and goes quite some miles (kilometers?) to make the language more regular and simple.
The implicits are gone, for example. Kinda crazy, since it was Scala's banner feature for so long. For the primary use-cases of implicits (extensions, type classes, conversion, ...) there are now language level concepts to make these use-cases accessible.
275 pages: https://kotlinlang.org/spec/pdf/kotlin-spec.pdf