Disclaimer: I'm not a Scala guru.
Scala does two things extremely well which Java (currently) does not.
Solve functional problems
At the most basic level, Scala has fully fledged closures with collections support. This means you no longer have to write boiler plate code such as (shamelessly ripped off a DZone post)
public List<Item> bought(User user)
{
List<Item> result = new ArrayList();
for (Item item : currentItems)
{
if (user.bought(item))
{
result.add(item);
}
}
return result;
}
But instead write something like:
def bought(user: User) = items.filter(user bought _)
- There's more functional love, but I'm not qualified to talk about it since I currently still suck at functional programming :)
Solve concurrency in a safer way
- Scala has an actors model (+ some other goodness) which is inheritly safer than Java's mutable data + locks on Thread model (no matter how good the libs are getting, Java is still hindered by the language).
I can't honestly think of too much else that makes Scala stand head and shoulders above Java. Lots of small gains and improvements yes, but also far more rope to hang yourself with. YMMV
HTH a little
Scala doesn't require an explicit return type on all functions, just recursive ones. The reason for that is that Scala's type inference algorithm is (something close to) a simple scan from beginning to end that is incapable of lookahead.
This means that a function like this:
def fortuneCookieJoke(message: String) = message + " in bed."
doesn't need a return type, since the Scala compiler can clearly see, without using logic variables or looking at anything other than the method's parameters, that the return type must be String
.
On the other hand, a function like this:
def mapInts(f: (Int) => Int, l: List[Int]) = l match {
case Nil => Nil
case x :: xs => f(x) :: mapInts(f, xs)
}
will cause a compile-time error, because the Scala compiler cannot see, without using either lookahead or logic variables, exactly what the type of mapInts
is. The most it could say, if it were smart enough, is that the return type is a supertype of List[Nothing]
, since Nil
is of that type. That doesn't give it anywhere near enough information to accurately determine the return type of mapInts
.
Please note that this is specific to Scala, and that there are other statically typed languages (most of the Miranda/Haskell/Clean family, most of the ML family, and a few scattered others) that use much more comprehensive and capable type inference algorithms than Scala uses. Also, be aware that this is not entirely Scala's fault; nominal subtyping and whole-module type inference are fundamentally at odds with each other, and the designers of Scala chose to favor the former over the latter for the sake of Java compatibility, while the "purer" statically typed functional languages were mostly designed with the opposite choice in mind.
Best Answer
I think you are creating a bit of a false dichotomy here.
Haskell has monad comprehensions built into the language. One reason for that is the use of monads for imperative-style I/O. Therefore, the designers of Haskell decided to make it look mostly like a code block in a generic C-style language, complete with curly braces, semicolons and even
return
. The assignments look a bit non-C, but left-arrow for assignment is typical imperative pseudo-code.So, monad comprehensions in Haskell were designed to look like imperative sequential side-effecting code blocks in some generic C/pseudo-code like language, because that's an important use case for them. This even permeates the API design with the naming of the aforementioned
return
function. Note that the name of this function doesn't really make much sense if you use it outside ofdo
notation.C# has monad comprehensions built into the language. One reason for that is the use of monads for querying of datasets. Therefore, the designers of C# decided to make it look mostly like SQL or XQuery, complete with
FROM
,SELECT
,WHERE
,GROUPBY
,ORDERBY
, etc. The order looks a bit non-SQL (FROM
beforeSELECT
), but it's the same one as used by XQuery, actually.So, monad comprehensions in C# were designed to look like SQL queries, because that's an important use case for them. This even permeates the API design with the naming of e.g.
Select
for the transformation function (instead ofmap
),SelectMany
(instead offlatMap
),Where
(instead ofselect
/filter
), etc. Note that the names of these operations don't really make much sense if you use them outside of a query comprehension, and in fact, can even be actively confusing (Select
is what is usually calledmap
, whereas what is usually calledselect
isWhere
).Scala has monad comprehensions built into the language. One reason for that is the use of monads for collection operations. Therefore, the designers of Scala decided to make it look mostly like a
for
loop in a generic C-style language. The assignments look a bit non-C, but left-arrow for assignment is typical imperative pseudo-code.So, monad comprehensions in Scala were designed to look like imperative
for
loops in some generic C/pseudo-code like language, because that's an important use case for them.Note that both in the case of C# and Scala, the comprehension syntax can actually do more and/or less than just perform the two monadic operations
join
andbind
(ormap
andflatMap
). In Scala, afor
comprehension withoutyield
translates intoforeach
, i.e. an imperative side-effecting iteration, which really doesn't have much to do with monads at all. Ayield
can have a guard (yield foo if bar
), which translates into a call to a call towithFilter
, i.e. filtering elements. C# can do the same withWhere
. C# can also do aggregation (group by
) and sorting (order by
).They are actually more of a generalization and/or fusion of monad comprehensions and list comprehensions generalized to arbitrary collections and monads. Note that Haskell also has both, but they are different things, in C# and Scala, they are fused together.
Haskell has a lot of history, and has pioneered a lot of concepts. As is typical with pioneers, often the people who come after them, discover better, shorter, safer routes. Maybe if Haskell had been designed with hindsight instead of innovation, they also would have generalized list comprehensions to work with more collections, and would have fused monad comprehensions and generalized collection comprehensions together, who knows? I know that there are proposed variants and extensions to Haskell, which add Arrow Comprehensions, for example.
The first one is intentional. It is supposed to look like a
for
loop.The second one is unfortunate. The word
yield
has two (actually related but not obviously so) meanings. One is the meaning used in concurrency, coroutines, fibres, threads, and for theyield
keyword and theFibre#yield
method in Ruby, where it means that a piece of code yields control of execution to another piece of code. The other is the meaning of a computation yielding a result. That's the meaning that is used in Python generators, C# generators, and interestingly also in Ruby, in theEnumerator::Yielder#yield
method, and is the interpretation that is meant for theyield
keyword in Scalafor
comprehensions.