4 ms·
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 ex
by dancapo 13y ago
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.
- frowaway001 13y agoThanks a lot for your comment. This is probably one of the best explanations I have read since a long time.
- laureny 13y ago> 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. I'm surprised you're pointing out bad design decisions in Java (that I agree with) and point structural type as a good Scala design decision. Structural typing is terribly designed and avoided overall for all these reasons. For example, asInstanceOf doesn't work with structural types but works fine with traits and classes. That's bad design. Here is an example showing how broken structural types are: scala> type Mappable[A] = AnyRef { def map[B](f: A => B): Mappable[B] } <console>:7: error: recursive method map needs result type type Mappable[A] = AnyRef { def map[B](f: A => B): Mappable[B] } I have about three other mystifying compiler errors due to the fact that structural types are very badly integrated in the language overall. > 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. I think you are not being very objective in the way you see Scala. A lot of its features are terribly at odds with each other, such as mixing inheritance and type classes (which one should I use and when?), the eleven ways in which the underscore character can be used, currying and its bizarre syntax, the odd rules when you can omit parentheses and when you can't, when to use parentheses and when you should use braces, etc... Scala has tons of disparate features that don't fit very well with each other, and it's very similar to C++ in that respect.
- dancapo 13y agoOne 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.