The term monad comes straight from Category Theory, a branch of mathematics. There are some rules, and anything that obeys those rules is a monad.
From the point of view of programming languages, any set of values/objects for which a particular set of functions exists is a monad. See wikipedia for what they are, exactly.
Monads exist on both dynamic and statically typed languages, but it is more difficult to explain them on dynamic languages, where you don't have types to explain the properties.
So many things can be monads. For example, collections can easily be monads, for it is easy to define "join" and "fmap" for them (one of the two possible ways of defining a monad):
* "join", aka "flatten", means that if we have a collection of a collection (a list of lists of x, a set of sets of y, etc), we can "flatten" it one level (that is, get a list of x or set of y);
* "fmap" means that, if we have a function that changes the objects inside that collection, we can create a function that changes the collection so that the objects inside it are modified. For example, if we have a function from strings to numbers, we can create a function that converts a list of strings into a list of numbers.
A list comprehension in Python is an example of these rules put in practice, though when using "if" you are going beyond these rules.
There are other monads besides collection and the usefulness of monads is that they work like an interface or API that is shared by all monads.
Going to Python again, consider the syntax:
listC = [f(x, y) for x in listA for y in listB]
The function f doesn't care that x and y came from listA and listB, and if Python supported any kind of monad, then you could write something like this:
monadC = [f(x, y) for x in monadA for y in monadB]
And that would work for any monad you passed. You would have abstracted away that you had lists, making your code more generic and, therefore, more reusable and less repetitive.
To go with an example you use elsewhere, you have a monad containing a file descriptor, a read line function and a convert string to number function, then you'd get back a monad of a number:
monadN = [int(readline(fd)) for fd in monadFD]
Note that monads don't define a way to extract that number from inside them, but that really isn't necessary, as you can continue to apply functions:
monadVoid = [print(n) for n in monadN]
If your original monad was a list of file descriptors, you'd print a list of numbers, one for each file. A Maybe monad (aka Option monad) would either contain a file descriptor or not, and you'd either print a number or not accordingly.
Another type of monad, the Future monad (also known as Promise), executes the function they are given asynchronously. So the monad containing the file descriptor was a future monad, then the code above would be executed and finish, but the number could be printed well afterward. I'll come back to this example at the end.
The usefulness or particular monads depends on the context, but the ones I'm familiar with mostly help avoiding side effects in functional programming. If you don't avoid side effects, you have no use for them. If you do, you'll inevitably come up with something equivalent to monads like the IO or State.
And the usefulness of the fact that they are monads comes from the fact that you can abstract away the specific type of monad they are, and work generically.
So, coming back to our number-printing program, let's say we are using a Future monad so that the number gets printed asynchronously, and we wish to test the program. If we use the Future monad, we'll probably have to sleep for a while and then see if the number was printed or not -- a test that may fail if the program takes too long to execute (say, the file is on a remote computer and has to be downloaded first).
One alternative is that we replace the Future monad with the Identity monad. The identity monad just holds a value, so any operation on it is executed synchronously. We can test our program using the Identity monad, so we do not have to way, and run it in production using the Future monad, so our program runs asynchronously.
One of the goals of Scala was having good interoperability with Java. This might not look important today, where considering different languages is ok, but it was certainly the case years ago, when programming environments were monolingual and proud of it.
There are bad consequences arising from that, but it was a trade off that was made taking into account the failure of Scala's predecessor, Pizza.
Among these things there's asInstanceOf, which you should generally not be using in first place. That said, you can use asInstanceOf with structural types:
scala> object X {
| def f(a: Int, b: Int) = a + b
| }
defined object X
scala> val y: Any = X
y: Any = X$@aebbca6
scala> val z = y.asInstanceOf[Any { def f(x: Int, y: Int): Int }]
z: Any{def f(x: Int,y: Int): Int} = X$@aebbca6
scala> z.f(2, 3)
res6: Int = 5
Your example definitely doesn't show structural types being broken: it only shows that type refinements cannot be recursive. This might be limiting, but it is not broken, and it was a necessary trade off to integrate structural types into a nominal type system -- something that was thought to be impossible.
Inheritance and type classes: type class is not a Scala feature, it is a design pattern that uses other Scala features together, so there's no "clash" here, because there's no feature.
The eleven ways in which underscore can be used is not something "at odd" either -- it's just the same symbol used in eleven different contexts to mean eleven different things. It saves on reserved keywords/characters, at the cost of making the language more difficult to learn, but there's no conflict between their uses.
There's nothing bizarre about currying syntax -- you may not like how it looks, but it's perfectly functional. Maybe you like point free programming, but that is NOT a style favored by Scala, intentionally so.
The rules for omitting parenthesis are actually rather simple: you can omit parenthesis around the parameter of a single-parameter method when that parameter is enclosed in braces itself; you can also omit parenthesis around a parameter of a single-parameter method whose parameter type is a tuple.
Now, even if I had not an answer for each case you raised, you have failed to show that they don't fit well with each other. You have criticized each one on their own, in isolation, and, in fact, failed to appreciate how they actually show their strength when combined.
Some examples of that:
* Omitting parenthesis combined with by-name method parameter passing allows for DSLs that look like native language constructs;
* Implicit parameters, type inference and nominal typing can be used together to produce the type class pattern;
* Abstract data types and inheritance can be used together to create a module system.
There's a lot that could be criticized about Scala, but that article is a criticism of the author, for me who actually knows something about Scala.
It starts with a criticism of a build system that is most likely SBT. Now, for all its strengths, you could not pick an easier target than SBT if you wanted to bad mouth a piece of Scala software, but what we get is:
* It is slow to recompile the build definition (true, not the most complex build files I ever saw ever took anywhere near that time);
* I didn't bother to learn the syntax (or what was going on).
Is that it? If all you have to complain about SBT is this, then you never actually made even a passing attempt to learn to use it. That doesn't make me much confident about the author...
Next, the author has been writing Scala code for the past two weeks. Scala is not, I argue, as complex as people make it to be, but it has enough different concepts than what most other languages offer that you simply don't pick it up in two weeks, just like you didn't learn your first programming language in two weeks.
If you come from Java, two weeks of Scala let you use Scala like Java: you'll produce working software, though not in any idiomatic style. Well, we pick later on that the team is using Spray, and Spray is a very idiomatic library. You do have to unlearn many habits to pick it up, and the author doesn't seem interested in unlearning anything he "knows".
He picks Scala because, hey, there were doing XSL and doing XML processing, so Scala must be good at it, right? Well, actually, no, but I can see why one would think that, and also why one would be disappointed that it actually isn't, but picking the wrong tool doesn't mean the tool is bad: it's just not the tool for that.
So we go to TDD, where he comes up on Scala awful compile times (which troubled him with SBT before). That is a problem that Scala community has faced, and it has produced solutions to that problem. Specifically, it has adopted SBT, a build tool which can compile incrementally (and automatically on change), and even execute just the tests that are affected by a change (automatically as well).
Of course, the author, having chosen Scala, decides to ignore the existing solutions and picks up Gradle, which supports none of this. Now, Gradle is a very nice build system, but, again, the author picks the wrong tool and proceeds to blame the wrong thing from it.
Next, the author proceeds to have problems with libraries -- problems somehow not shared with the rest of the community (see comments by other Scala users). He mentioned stability problems, and then mentions two pieces of software with excellent documentation, extremely stable, and supported across a broad range of versions of their dependencies.
Now, choosing Spray when you are not familiar with Scala was a terrible decision, and I can well see where the author had trouble using it -- while not agreeing there's any problem with Spray itself. Having trouble with Specs2, however, makes me seriously doubt his abilities -- if he truly had trouble with it.
Next he proceeds to some discussion of simplicity, and I'd grant that Scala is not simple. However, he ties that to a lack of regularity, which is interesting in that Scala is highly regular, much more so than Ruby. It just allow different things than the languages he is used to, so it looks different.
I can buy "looks different" -- yes, it does. But the author's inability to realize how regular it is just goes to show that he has a very, very poor knowledge of Scala. That's not a problem with Scala, it's just that he didn't learn Scala, and it keeps showing.
So, having not learned Scala, he tries to accomplish something that required a specific Scala knowledge he lacked and fails. And somehow that's Scala's fault and Spray's fault, not his.
Well, I admit it: if you don't learn Scala, it is very difficult to program in Scala. That's certainly not the case with PHP, for example, but I have certainly struggled with rather simple tasks in Ruby when trying to write code in Ruby without learning it. I just never blamed Ruby for it.
Next, "everything is a type". I disagree that this is in any way a problem -- if you are going to complain about that, how about just writing "Scala is a statically typed language" and leave it at that? We all knew that, didn't we?
So, having chosen a statically typed language, he dislikes the fact that it actually tries to use types? Weird, but at least he doesn't make it a problem of Scala itself.
On the other hand, it took me thirty seconds to find RawHeader, which allows arbitrary headers, which indeed would be a weird thing to be missing. It's right there, among the other headers. Among, say, Origin and either Access-Control-* headers, which happen to be the CORS headers, all of which appear in the API docs, so what gives?
Maybe they are new headers? Well, the CORS headers are new, if not that new, but maybe the article refers to something that happened months ago. The raw header is there for quite a while, as you can see for yourself here: https://github.com/spray/spray/blame/master/spray-http/src/m...
At this point, I don't even know where the author is coming from, that he failed to spot this. It is certainly not Spray's fault, much less Scala's.
Next comes a rant on lack of H-M type inference. Well, we all would have liked to have more type inference (without making the compiler even slower!), but separate compilation, subtyping and full type inference is not a solved problem. Which doesn't mean we can't gripe about it.
But there's a damning comment among it: the error message he describes in his "workflow". Hey, Scala infers the return type, so he doesn't need to add it, unless he's doing recursion of calling overloaded methods. If he were doing that, however, the error message would be different.
What the error message says, however, is that he is using procedure syntax -- that is, a method returning void in C/Java parlance -- when he actually intended the method to return something. He doesn't need to add the type, he just needs to use the correct declaration:
def method() { body } // procedure (unit-returning method)
def method() = expr // function
He spent two weeks programming in Scala, then went on to write a blog about it, but he still hasn't figured out a simple detail like this, despite the compiler telling him what the problem is?
Yes, beginners might well struggle with this oddity, but for two weeks??? And despite having fixed it throughtout the program? Really?
Now, detour briefly through long vs int, which I actually agree with, but saying he was "reminded" of it shows a complete lack of familiarity with statically typed languages.
Again, fine that you don't like statically typed languages, though I think one ought to first learn one of them before criticizing, but you could start and end your post with "I don't like statically typed languages".
Then there are other issues which he enumerates but don't describe, and which I can only disagree with. Horrible repetition required by case classes? Err, what repetition?
So, in the end, it seems that what the author really wanted to say is: I don't like statically typed languages, and I tried to use one for the wrong reasons, in the wrong way, without learning it, and failed. No need to blame Scala for his bad decisions.
Scala has dependent type support, so, yes, it is quite more powerful than most languages in widespread use. You can look at the Shapeless library to see that at work.
However, that is not really what's going on... though Scala type system, even without dependent types, is strictly more powerful that Java's, it really comes down to the ease of use: Scala programmers tend to use types liberally (which was one of the criticisms of the article), and that is part of the "magic".
Part of this is how easy it is to define a type: it can be as short as a single line. So you'll see way less String types in Scala, for instance, or "one type to do it all" like the article wanted for HTTP. Instead, you get many types, in which invalid states are not possible and, therefore, getting the types right is enough to ensure there are no invalid states.
Likewise, the partial type inference offered by Scala makes it possible to not worry about all these types: the compiler knows what type you got, and knows whether it can be used the way you are using it, so you don't have to keep looking up types to get things accomplished, but still get to have the types.
Another part is that Scala support higher order types, something not supported by Java, which makes them more useful. That's what allows monads & companions come in and make an appearance -- it's not that you can't write a monad in Java, it's just that you can't abstract over them.
The final part is emphasis on immutability. When you stick to immutable objects representing valid states, the space for errors not caught by the type system is greatly reduced.
Let's get back to the headers, then. First, yes, you are allowed to add arbitrary headers (which is supported by Spray through RawHeader, something it took me 30 seconds to find out, even though I have never used Spray and have little familiarity with it), but section 14 of RFC 2616 has a nice list of standard headers and the rules they are expected to abide by, so let's have them.
And if you have a header, and it is a X-Forwarded-For, isn't it reasonable to expect the value to be a valid remote address? It is supposed to be a valid remote address, after all. So, if you ask for this header, should you simply get a string that may or may not be actually valid, or should the type system give you an Option that may contain Some valid header, or None, therefore ensuring that you don't just assume you are getting the right thing? And if you get something and retrieve the value, is it preferable to just get a string, that might lead to some ad hoc parsing of the address, or some actual type where you can easily retrieve information in a safe manner?
The Scala community will usually think that yes, that's a good idea, and design the APIs accordingly.
I see some objective problems with your analysis: it looks more like you expect to be true than assertion of facts, and it is filled with misconceptions. You expect that mixing oo and fp aspects cannot work, and deduce problems from that expectation.
Here's a different premise: Scala combines fp and oo while keeping things simple because it is coherent -- more so than languages such as Java.
So, for instance, when you complain about Scala's algebraic data types, you completely miss the fact that Scala doesn't have them. There's no type in Scala that isn't a class.
Instead, various Scala features are combined to support ADT patterns through the OO type system, and these features are designed to work in concert to produce various desirable effects, instead of having stand alone features serving a single purpose, without coherent integration with the rest of the language.
Take Java, for example, and some things it has that Scala doesn't: static declarations, primitive types and arrays. Each of them are completely at odds with the rest of the language: they serve a single purpose, they have their own rules, and they cannot be combined. That is bad design.
Let's consider one of the features you mention that Scala actually has, and see how it fits: structural typing. Scala doesn't have structural typing because all the cool kids are doing it or some other non-sense: it serves a purpose in the language, and solves a number of problems in a coherent manner, with a single set of rules and in a way that combines together with other parts of the language.
Specifically, it generalizes Java's own duck typing, which, apparently, you never realized it was there (because Java is such an ad hoc language): anonymous classes extra methods.
There's no explanation in Java's type system for why anonymous classes may have extra methods and use them. They are simply allowed for anonymous classes, and that's that.
Scala, on the other hand, introduced "type refinements". It is exactly the same thing: extra methods. However, it is not a standalone thing dissociated from the rest of the language: one can call these methods because they are part of the type refinement. And because they are part of the type refinement, it also means they can be visible from outside the anonymous class, and even specified as the type for parameters.
And that's where the "duck typing" comes in: it is simply the type of an anonymous class that extends Object whose extra methods are visible from outside the anonymous class itself. Not an extra feature, compared to Java, but an existing feature of Java that got integrated.
The pattern continues: while Scala has just classes with refinements, Java has classes and primitives and arrays. While Scala has just methods and assignment, Java has methods, static methods, operators, fields, assignment (of which multiple kinds exist!), and array index access and assignment.
It's not that Scala has many disparate features: it has a core of well designed features, and they are well designed because they allow use in many different ways.
Now, if one thinks a language should constrain its user, then Scala is not a language for that person. But, please, don't invent problems that don't exist because you expect them to be there: if you want to criticize it for anything other than not being to your taste, at least get to know it first.
The term monad comes straight from Category Theory, a branch of mathematics. There are some rules, and anything that obeys those rules is a monad.
From the point of view of programming languages, any set of values/objects for which a particular set of functions exists is a monad. See wikipedia for what they are, exactly.
Monads exist on both dynamic and statically typed languages, but it is more difficult to explain them on dynamic languages, where you don't have types to explain the properties.
So many things can be monads. For example, collections can easily be monads, for it is easy to define "join" and "fmap" for them (one of the two possible ways of defining a monad):
* "join", aka "flatten", means that if we have a collection of a collection (a list of lists of x, a set of sets of y, etc), we can "flatten" it one level (that is, get a list of x or set of y);
* "fmap" means that, if we have a function that changes the objects inside that collection, we can create a function that changes the collection so that the objects inside it are modified. For example, if we have a function from strings to numbers, we can create a function that converts a list of strings into a list of numbers.
A list comprehension in Python is an example of these rules put in practice, though when using "if" you are going beyond these rules.
There are other monads besides collection and the usefulness of monads is that they work like an interface or API that is shared by all monads.
Going to Python again, consider the syntax:
The function f doesn't care that x and y came from listA and listB, and if Python supported any kind of monad, then you could write something like this:
And that would work for any monad you passed. You would have abstracted away that you had lists, making your code more generic and, therefore, more reusable and less repetitive.
To go with an example you use elsewhere, you have a monad containing a file descriptor, a read line function and a convert string to number function, then you'd get back a monad of a number:
Note that monads don't define a way to extract that number from inside them, but that really isn't necessary, as you can continue to apply functions:
If your original monad was a list of file descriptors, you'd print a list of numbers, one for each file. A Maybe monad (aka Option monad) would either contain a file descriptor or not, and you'd either print a number or not accordingly.
Another type of monad, the Future monad (also known as Promise), executes the function they are given asynchronously. So the monad containing the file descriptor was a future monad, then the code above would be executed and finish, but the number could be printed well afterward. I'll come back to this example at the end.
The usefulness or particular monads depends on the context, but the ones I'm familiar with mostly help avoiding side effects in functional programming. If you don't avoid side effects, you have no use for them. If you do, you'll inevitably come up with something equivalent to monads like the IO or State.
And the usefulness of the fact that they are monads comes from the fact that you can abstract away the specific type of monad they are, and work generically.
So, coming back to our number-printing program, let's say we are using a Future monad so that the number gets printed asynchronously, and we wish to test the program. If we use the Future monad, we'll probably have to sleep for a while and then see if the number was printed or not -- a test that may fail if the program takes too long to execute (say, the file is on a remote computer and has to be downloaded first).
One alternative is that we replace the Future monad with the Identity monad. The identity monad just holds a value, so any operation on it is executed synchronously. We can test our program using the Identity monad, so we do not have to way, and run it in production using the Future monad, so our program runs asynchronously.
We can do that because they are both just monads.