Why I need a new primitive? - scala

I am learning functional programming in Scala and reading the book FPiS.
One the page 131 about API design(algebra), the author mentioned following:
As we discussed in chapter 7, we’re interested in understanding what
operations are primitive and what operations are derived, and in
finding a small yet expressive set of primitives. A good way to
explore what is expressible with a given set of primitives is to pick
some concrete examples you’d like to express, and see if you can
assemble the functionality you want. As you do so, look for patterns,
try factoring out these patterns into combinators, and refine your set
of primitives. We encourage you to stop reading here and simply play
with the primitives and combinators we’ve written so far. If you want
some concrete examples to inspire you, here are some ideas:
What does he mean with expressive primitives? What is a primitive?
(The chapter 7 does not explain about primitive at all.)
I am imaging, a primitive is a smallest thing of something that can be build with combinator to get a higher level thing.
The question from the book:
If we can generate a single Int in some range, do we need a new primitive to
generate an (Int,Int) pair in some range?
What is the answer of the question?
Is Int a primitive?
What is a primitive function?

What is a primitive?
I am imaging, a primitive is a smallest thing of something that can be build with combinator to get a higher level thing.
Right. So in this case: you are developing an API which has type Gen[A] denoting "a generator of values of type A". Combinators in this API will be methods which operate on Gens and produce Gens. However, you also need something to apply those combinators to: methods which produce Gens without starting with one.
One of these may be range(min: Int, max: Int): Gen[Int]. If you now need a pairInRange(min: Int, max: Int): Gen[(Int, Int)], do you need to implement it in the same way range is implemented, or can you build it from range using combinators? And if you can, what combinators do you need? In this case, you should conclude that
def pairInRange(min: Int, max: Int) = pair(range(min, max), range(min, max))
is a reasonable implementation, assuming there is a pair combinator (think what its type should be), so pairInRange doesn't need to be a primitive: it's a derived operation. Primitive operations are those which aren't derived.
You could equally have pairInRange as primitive and def pair(min: Int, max: Int) = pairInRange(min, max).map(_._1) as derived; this is just an unnatural way to design it!
(The chapter 7 does not explain about primitive at all.)
Now that I've got home and have access to the book: yes, it does. If you search for "primitive" in the book (assuming you have the electronic version), you'll find this quote in 7.1.3 Explicit forking:
The function lazyUnit is a simple example of a derived combinator, as opposed to a
primitive combinator like unit. We were able to define lazyUnit just in terms of other
operations. Later, when we pick a representation for Par, lazyUnit won’t need to
know anything about this representation—its only knowledge of Par is through the
operations fork and unit that are defined on Par.
Note in particular the last sentence, which makes a point I didn't emphasize above. You should also read what follows: there are more explanations in 7.3.
What does he mean with expressive primitives?
A primitive is not expressive, it's a set of primitives (including combinators) that is expressive. That is, it should allow you to express not just range and pairInRange but all other generators you want.
Is Int a primitive?
No. This is a meaning of "primitive" unrelated to "primitive types" (Int, Double, etc.)

Related

Why didn't scala design around Integer Overflow?

I am a former Java developer and I have recently watched the insightful and entertaining introduction to Scala for Java developers by professor Venkat Subramaniam (https://www.youtube.com/watch?v=LH75sJAR0hc).
A major point introduced is the elimination of declared types in lieu of "type inference". Presumably, this means the higher-order compiler recognizes the type I intend to use, by the context.
Being an application security expert by trade, the first thing I tried to do is break this type inference... Example:
// declare a function that returns the square of an input Int. The return type is to be inferred.
scala> val square = (x:Int) => x*x
square: Int => Int = <function1>
// I can see the compiler inferred an Int for the output value, which I do not agree with.
scala> square(2147483647)
res1: Int = 1
// integer overflow
My question is why did the compiler not see that "*" is an operator with a threat of overflow, and wrap the inputs in something a little more protective like a BigInteger?
According to the professor, I am supposed to forget about the internal implementation and just get on with my business logic. But after my quick demonstration I'm not so sure that Scala is safe for a programmer who doesn't understand what the compiler is doing with my methods.
I think #rightføld somewhat overstates how often overflows do or don't happen (particularly when considering an attacker who is actively trying to overflow you). But I agree with his basic point. Converting all math to BigInteger would almost certainly have created a massive performance impact over Java. For developers to choose such a language, they'd have to get something visible for that cost.
String objects have a much smaller performance overhead over cstrings for many operations. They also provide very visible benefits to the developer, which is why people use them, not security per se. There are many common things that string objects make easy to do over cstrings. BigInteger provides none of that. It requires exactly the same code at a fraction of the speed, but just won't overflow (a bug few developers see day to day, even if security guys see it more often).
The equivalent would have been a cstring (with strcmp, strcpy, strcat, etc.) that ran at a fraction of the speed, but just didn't require a null terminator. I don't think many people would have jumped to use that, either, no matter how much that would help security over null-terminated strings. And if the language required it, I don't see a lot of people anxious to use the language.
And as #rightføld suggests in the comments, interoperability with Java would be trashed, since most if not all numbers would wind up being BigInteger. You'd constantly be converting, which raises the same dangers of overflows while adding a lot of code complexity (and more performance impacts).
A from-scratch language might get away with ubiquitous BigInteger (like python) if the language had a lot of other compelling features, but it's a very hard thing to retrofit into a language that wants to be a natural transition from (and with) Java.
In addition to the above answers, I think this question misunderstands the purpose of type inference in a statically typed language. Type inference does not make the choices that you are referring to - promoting a Int to a BigInt. It is restricted to simply "inferring" the type of an expression based the the known types of subexpressions at compile time.
The * function in Int returns an Int when supplied with an Int input parameter
def *(x: Int): Int
In this case, since x is declared to be an Int, then x*x must be an Int based on the signature of *.
If we really wanted this behavior, we could define a function that promotes Int to BigInt when multiplying.
implicit class SafeInt(x: Int) {
def safeMult(a: Int): scala.math.BigInt = scala.math.BigInt(x)*a
}
Then when we can define a square with the desired property:
scala> val square = (x: Int) => x safeMult x
square: Int => scala.math.BigInt = <function1>
The compiler infers based on the methods available. Int has a method *(Int): Int that is, as far as the compiler knows, perfectly well defined; 2147483647*2147483647 is a perfectly good method call with the result 1, it doesn't throw ClassCastException or anything like that.
Why is the Int type written this way? Largely for Java/JVM compatibility; many parts of Scala have design compromises for the sake of Java compatibility. If you don't need that functionality, you might prefer to use Haskell or a similar language. (I suspect that even without the requirement for JVM compatibility, Scala would have wanted to expose the machine-native integer types so that users could make that performance/correctness tradeoff where desired. They might not have been the default though)
If you're doing numeric computation in Scala you probably want to use the Spire library, which makes it easy to abstract over numeric types, and provides several high-performance numeric types with particular properties. In particular it has a SafeLong type that handles arbitrary-precision integers but with much better performance than BigInt for values which fall within the Long range, similar to Python's integer type.
Because overflow occurs almost never in practice, and BigInteger is slow as a dog compared to Int. It is also most inconvenient to have all * operations on Ints return BigIntegers.
"Recognizes the type I intend to use" is not an accurate description of what scala tries to do. It infers the most generic type possible given the constraints imposed by the context. Hence if you write List(Nil, "1"), you'll get List[Serializable], because Serializable is an interface that List and String share - disregarding that Serializable was probably not on your mind at all.
The question you're asking could be asked more precisely as "why is Int the type of numeric literals instead of BigInteger?" - inference doesn't have much to do with it.
And we can opine all we want on that topic, but there's one most accurate answer describing why Scala is what it is: "because Java".
If you wanted the type of safety that you seem to want, then one approach is to define via a partial function which guards against numeric overflow and then returns either an Option[Int] or even perhaps an Either[Int, BigInteger].
The type inference for your square function is correct - given that it's inferred from the input types you've specified and the type of the * function...it's not really broken in my opinion.

Is it possible to implement F#'s infrastructure for Units of Measurement in Scala?

F# ships with special support for a unit of measurement system, which provides static type safety while compiling down to the numeric types instead of burdening the runtime with wrapping/unwrapping operations.
Is it possible to use some of Scala's type system magic to implement something comparable to that?
The answer is no.
Now, someone is bound to point me to Scalar, but that gives runtime checking. Perhaps, then, point to the efforts of Jesper Nordenberg's type-safe units or Jim McBeath's take on it, but these are cumbersome and awkward.
I'll point, instead to the Units compiler plugin. It gave Scala, back in 2008/2009, a pretty good system of units, as can be seen in this post. It did so, however, by extending the compiler, which would not be necessary if the type system was enough. Alas, it has not been maintained and it doesn't work anymore.
I don't know anything about it, but I just stumbled accross this talk at Scala Days: https://wiki.scala-lang.org/display/SW/ScalaDays+2011+Resources#ScalaDays2011Resources-ScalaUImplementingaScalalibraryforUnitsofMeasure
Kind of. You can encode the SI units quite easily using a type representation of integers in a tuple of exponents. See http://svn.assembla.com/svn/metascala/src/metascala/Units.scala for an example implementation.
It should also be possible to support an extensible units system if the units are encoded as a TList of pairs of a unit type and an integer (for example, ((M, _1), (S, _2)) where M <: Unit and S <: Unit). Calculating the types for quantity operations becomes a bit more complicated in this encoding.
Regarding performance there will always be a memory overhead for wrapping the value in a type containing the unit information. However there is probably no performance overhead in the actual operations as all unit checking is done at compile time.
Have a look at Units of Measure - A Scala Macro System. It seems to satisfy your requirements.

Does it make sense to define a class for Complex numbers, where real/imaginary parts use Numeric[T] instead of a concrete type?

Would something like
class Complex[T: Numeric](real: T, imag: T)
make sense, instead of writing a Complex class using Doubles, one using Longs, one using BigInts, so that everyone can choose the number type he needs?
How would performance compare to the non-generic approach?
For the moment, Numeric is not #specialized. So the generic version using it will suffer from boxing and unboxing and the performances will be greatly reduced. Here is a nice blog post with performance measurments:
http://www.azavea.com/blogs/labs/2011/06/scalas-numeric-type-class-pt-2/
However, you could directly write a #specialized version of your Complex number class without using Numeric and get all the benefits.
On a strictly pragmatic point of view, I am not sure to understand what's the usage of a complex number with integer parts...

Scala versus F# question: how do they unify OO and FP paradigms?

What are the key differences between the approaches taken by Scala and F# to unify OO and FP paradigms?
EDIT
What are the relative merits and demerits of each approach? If, in spite of the support for subtyping, F# can infer the types of function arguments then why can't Scala?
I have looked at F#, doing low level tutorials, so my knowledge of it is very limited. However, it was apparent to me that its style was essentially functional, with OO being more like an add on -- much more of an ADT + module system than true OO. The feeling I get can be best described as if all methods in it were static (as in Java static).
See, for instance, any code using the pipe operator (|>). Take this snippet from the wikipedia entry on F#:
[1 .. 10]
|> List.map fib
(* equivalent without the pipe operator *)
List.map fib [1 .. 10]
The function map is not a method of the list instance. Instead, it works like a static method on a List module which takes a list instance as one of its parameters.
Scala, on the other hand, is fully OO. Let's start, first, with the Scala equivalent of that code:
List(1 to 10) map fib
// Without operator notation or implicits:
List.apply(Predef.intWrapper(1).to(10)).map(fib)
Here, map is a method on the instance of List. Static-like methods, such as intWrapper on Predef or apply on List, are much more uncommon. Then there are functions, such as fib above. Here, fib is not a method on int, but neither it is a static method. Instead, it is an object -- the second main difference I see between F# and Scala.
Let's consider the F# implementation from the Wikipedia, and an equivalent Scala implementation:
// F#, from the wiki
let rec fib n =
match n with
| 0 | 1 -> n
| _ -> fib (n - 1) + fib (n - 2)
// Scala equivalent
def fib(n: Int): Int = n match {
case 0 | 1 => n
case _ => fib(n - 1) + fib(n - 2)
}
The above Scala implementation is a method, but Scala converts that into a function to be able to pass it to map. I'll modify it below so that it becomes a method that returns a function instead, to show how functions work in Scala.
// F#, returning a lambda, as suggested in the comments
let rec fib = function
| 0 | 1 as n -> n
| n -> fib (n - 1) + fib (n - 2)
// Scala method returning a function
def fib: Int => Int = {
case n # (0 | 1) => n
case n => fib(n - 1) + fib(n - 2)
}
// Same thing without syntactic sugar:
def fib = new Function1[Int, Int] {
def apply(param0: Int): Int = param0 match {
case n # (0 | 1) => n
case n => fib.apply(n - 1) + fib.apply(n - 2)
}
}
So, in Scala, all functions are objects implementing the trait FunctionX, which defines a method called apply. As shown here and in the list creation above, .apply can be omitted, which makes function calls look just like method calls.
In the end, everything in Scala is an object -- and instance of a class -- and every such object does belong to a class, and all code belong to a method, which gets executed somehow. Even match in the example above used to be a method, but has been converted into a keyword to avoid some problems quite a while ago.
So, how about the functional part of it? F# belongs to one of the most traditional families of functional languages. While it doesn't have some features some people think are important for functional languages, the fact is that F# is function by default, so to speak.
Scala, on the other hand, was created with the intent of unifying functional and OO models, instead of just providing them as separate parts of the language. The extent to which it was succesful depends on what you deem to be functional programming. Here are some of the things that were focused on by Martin Odersky:
Functions are values. They are objects too -- because all values are objects in Scala -- but the concept that a function is a value that can be manipulated is an important one, with its roots all the way back to the original Lisp implementation.
Strong support for immutable data types. Functional programming has always been concerned with decreasing the side effects on a program, that functions can be analysed as true mathematical functions. So Scala made it easy to make things immutable, but it did not do two things which FP purists criticize it for:
It did not make mutability harder.
It does not provide an effect system, by which mutability can be statically tracked.
Support for Algebraic Data Types. Algebraic data types (called ADT, which confusingly also stands for Abstract Data Type, a different thing) are very common in functional programming, and are most useful in situations where one commonly use the visitor pattern in OO languages.
As with everything else, ADTs in Scala are implemented as classes and methods, with some syntactic sugars to make them painless to use. However, Scala is much more verbose than F# (or other functional languages, for that matter) in supporting them. For example, instead of F#'s | for case statements, it uses case.
Support for non-strictness. Non-strictness means only computing stuff on demand. It is an essential aspect of Haskell, where it is tightly integrated with the side effect system. In Scala, however, non-strictness support is quite timid and incipient. It is available and used, but in a restricted manner.
For instance, Scala's non-strict list, the Stream, does not support a truly non-strict foldRight, such as Haskell does. Furthermore, some benefits of non-strictness are only gained when it is the default in the language, instead of an option.
Support for list comprehension. Actually, Scala calls it for-comprehension, as the way it is implemented is completely divorced from lists. In its simplest terms, list comprehensions can be thought of as the map function/method shown in the example, though nesting of map statements (supports with flatMap in Scala) as well as filtering (filter or withFilter in Scala, depending on strictness requirements) are usually expected.
This is a very common operation in functional languages, and often light in syntax -- like in Python's in operator. Again, Scala is somewhat more verbose than usual.
In my opinion, Scala is unparalled in combining FP and OO. It comes from the OO side of the spectrum towards the FP side, which is unusual. Mostly, I see FP languages with OO tackled on it -- and it feels tackled on it to me. I guess FP on Scala probably feels the same way for functional languages programmers.
EDIT
Reading some other answers I realized there was another important topic: type inference. Lisp was a dynamically typed language, and that pretty much set the expectations for functional languages. The modern statically typed functional languages all have strong type inference systems, most often the Hindley-Milner1 algorithm, which makes type declarations essentially optional.
Scala can't use the Hindley-Milner algorithm because of Scala's support for inheritance2. So Scala has to adopt a much less powerful type inference algorithm -- in fact, type inference in Scala is intentionally undefined in the specification, and subject of on-going improvements (it's improvement is one of the biggest features of the upcoming 2.8 version of Scala, for instance).
In the end, however, Scala requires all parameters to have their types declared when defining methods. In some situations, such as recursion, return types for methods also have to be declared.
Functions in Scala can often have their types inferred instead of declared, though. For instance, no type declaration is necessary here: List(1, 2, 3) reduceLeft (_ + _), where _ + _ is actually an anonymous function of type Function2[Int, Int, Int].
Likewise, type declaration of variables is often unnecessary, but inheritance may require it. For instance, Some(2) and None have a common superclass Option, but actually belong to different subclases. So one would usually declare var o: Option[Int] = None to make sure the correct type is assigned.
This limited form of type inference is much better than statically typed OO languages usually offer, which gives Scala a sense of lightness, and much worse than statically typed FP languages usually offer, which gives Scala a sense of heavyness. :-)
Notes:
Actually, the algorithm originates from Damas and Milner, who called it "Algorithm W", according to the wikipedia.
Martin Odersky mentioned in a comment here that:
The reason Scala does not have Hindley/Milner type inference is
that it is very difficult to combine with features such as
overloading (the ad-hoc variant, not type classes), record
selection, and subtyping
He goes on to state that it may not be actually impossible, and it came down to a trade-off. Please do go to that link for more information, and, if you do come up with a clearer statement or, better yet, some paper one way or another, I'd be grateful for the reference.
Let me thank Jon Harrop for looking this up, as I was assuming it was impossible. Well, maybe it is, and I couldn't find a proper link. Note, however, that it is not inheritance alone causing the problem.
F# is functional - It allows OO pretty well, but the design and philosophy is functional nevertheless. Examples:
Haskell-style functions
Automatic currying
Automatic generics
Type inference for arguments
It feels relatively clumsy to use F# in a mainly object-oriented way, so one could describe the main goal as to integrate OO into functional programming.
Scala is multi-paradigm with focus on flexibility. You can choose between authentic FP, OOP and procedural style depending on what currently fits best. It's really about unifying OO and functional programming.
There are quite a few points that you can use for comparing the two (or three). First, here are some notable points that I can think of:
Syntax
Syntactically, F# and OCaml are based on the functional programming tradition (space separated and more lightweight), while Scala is based on the object-oriented style (although Scala makes it more lightweight).
Integrating OO and FP
Both F# and Scala very smoothly integrate OO with FP (because there is no contradiction between these two!!) You can declare classes to hold immutable data (functional aspect) and provide members related to working with the data, you can also use interfaces for abstraction (object-oriented aspects). I'm not as familiar with OCaml, but I would think that it puts more emphasis on the OO side (compared to F#)
Programming style in F#
I think that the usual programming style used in F# (if you don't need to write .NET library and don't have other limitations) is probably more functional and you'd use OO features only when you need to. This means that you group functionality using functions, modules and algebraic data types.
Programming style in Scala
In Scala, the default programming style is more object-oriented (in the organization), however you still (probably) write functional programs, because the "standard" approach is to write code that avoids mutation.
What are the key differences between the approaches taken by Scala and F# to unify OO and FP paradigms?
The key difference is that Scala tries to blend the paradigms by making sacrifices (usually on the FP side) whereas F# (and OCaml) generally draw a line between the paradigms and let the programmer choose between them for each task.
Scala had to make sacrifices in order to unify the paradigms. For example:
First-class functions are an essential feature of any functional language (ML, Scheme and Haskell). All functions are first-class in F#. Member functions are second-class in Scala.
Overloading and subtypes impede type inference. F# provides a large sublanguage that sacrifices these OO features in order to provide powerful type inference when these features are not used (requiring type annotations when they are used). Scala pushes these features everywhere in order to maintain consistent OO at the cost of poor type inference everywhere.
Another consequence of this is that F# is based upon tried and tested ideas whereas Scala is pioneering in this respect. This is ideal for the motivations behind the projects: F# is a commercial product and Scala is programming language research.
As an aside, Scala also sacrificed other core features of FP such as tail-call optimization for pragmatic reasons due to limitations of their VM of choice (the JVM). This also makes Scala much more OOP than FP. Note that there is a project to bring Scala to .NET that will use the CLR to do genuine TCO.
What are the relative merits and demerits of each approach? If, in spite of the support for subtyping, F# can infer the types of function arguments then why can't Scala?
Type inference is at odds with OO-centric features like overloading and subtypes. F# chose type inference over consistency with respect to overloading. Scala chose ubiquitous overloading and subtypes over type inference. This makes F# more like OCaml and Scala more like C#. In particular, Scala is no more a functional programming language than C# is.
Which is better is entirely subjective, of course, but I personally much prefer the tremendous brevity and clarity that comes from powerful type inference in the general case. OCaml is a wonderful language but one pain point was the lack of operator overloading that required programmers to use + for ints, +. for floats, +/ for rationals and so on. Once again, F# chooses pragmatism over obsession by sacrificing type inference for overloading specifically in the context of numerics, not only on arithmetic operators but also on arithmetic functions such as sin. Every corner of the F# language is the result of carefully chosen pragmatic trade-offs like this. Despite the resulting inconsistencies, I believe this makes F# far more useful.
From this article on Programming Languages:
Scala is a rugged, expressive,
strictly superior replacement for
Java. Scala is the programming
language I would use for a task like
writing a web server or an IRC client.
In contrast to OCaml [or F#], which was a
functional language with an
object-oriented system grafted to it,
Scala feels more like an true hybrid
of object-oriented and functional
programming. (That is, object-oriented
programmers should be able to start
using Scala immediately, picking up
the functional parts only as they
choose to.)
I first learned about Scala at POPL
2006 when Martin Odersky gave an
invited talk on it. At the time I saw
functional programming as strictly
superior to object-oriented
programming, so I didn't see a need
for a language that fused functional
and object-oriented programming. (That
was probably because all I wrote back
then were compilers, interpreters and
static analyzers.)
The need for Scala didn't become
apparent to me until I wrote a
concurrent HTTPD from scratch to
support long-polled AJAX for yaplet.
In order to get good multicore
support, I wrote the first version in
Java. As a language, I don't think
Java is all that bad, and I can enjoy
well-done object-oriented programming.
As a functional programmer, however,
the lack of (or needlessly verbose)
support of functional programming
features (like higher-order functions)
grates on me when I program in Java.
So, I gave Scala a chance.
Scala runs on the JVM, so I could
gradually port my existing project
into Scala. It also means that Scala,
in addition to its own rather large
library, has access to the entire Java
library as well. This means you can
get real work done in Scala.
As I started using Scala, I became
impressed by how cleverly the
functional and object-oriented worlds
blended together. In particular, Scala
has a powerful case
class/pattern-matching system that
addressed pet peeves lingering from my
experiences with Standard ML, OCaml
and Haskell: the programmer can decide
which fields of an object should be
matchable (as opposed to being forced
to match on all of them), and
variable-arity arguments are
permitted. In fact, Scala even allows
programmer-defined patterns. I write a
lot of functions that operate on
abstract syntax nodes, and it's nice
to be able to match on only the
syntactic children, but still have
fields for things such as annotations
or lines in the original program. The
case class system lets one split the
definition of an algebraic data type
across multiple files or across
multiple parts of the same file, which
is remarkably handy.
Scala also
supports well-defined multiple
inheritance through class-like devices
called traits.
Scala also allows a
considerable degree of overloading;
even function application and array
update can be overloaded. In my
experience, this tends to make my
Scala programs more intuitive and
concise.
One feature that turns out to save a
lot of code, in the same way that type
classes save code in Haskell, is
implicits. You can imagine implicits
as an API for the error-recovery phase
of the type-checker. In short, when
the type checker needs an X but got a
Y, it will check to see if there's an
implicit function in scope that
converts Y into X; if it finds one, it
"casts" using the implicit. This makes
it possible to look like you're
extending just about any type in
Scala, and it allows for tighter
embeddings of DSLs.
From the above excerpt it is clear that Scala's approach to unify OO and FP paradigms is far more superior to that of OCaml or F#.
HTH.
Regards,
Eric.
The syntax of F# was taken from OCaml but the object model of F# was taken from .NET. This gives F# a light and terse syntax that is characteristic of functional programming languages and at the same time allows F# to interoperate with the existing .NET languages and .NET libraries very smoothly through its object model.
Scala does a similar job on the JVM that F# does on the CLR. However Scala has chosen to adopt a more Java-like syntax. This may assist in its adoption by object-oriented programmers but to a functional programmer it can feel a bit heavy. Its object model is similar to Java's allowing for seamless interoperation with Java but has some interesting differences such as support for traits.
If functional programming means programming with functions, then Scala bends that a bit. In Scala, if I understand correctly, you're programming with methods instead of functions.
When the class (and the object of that class) behind the method don't matter, Scala will let you pretend it's just a function. Perhaps a Scala language lawyer can elaborate on this distinction (if it even is a distinction), and any consequences.

Is the Scala 2.8 collections library a case of "the longest suicide note in history"? [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
I have just started to look at the Scala collections library re-implementation which is coming in the imminent 2.8 release. Those familiar with the library from 2.7 will notice that the library, from a usage perspective, has changed little. For example...
> List("Paris", "London").map(_.length)
res0: List[Int] List(5, 6)
...would work in either versions. The library is eminently useable: in fact it's fantastic. However, those previously unfamiliar with Scala and poking around to get a feel for the language now have to make sense of method signatures like:
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
For such simple functionality, this is a daunting signature and one which I find myself struggling to understand. Not that I think Scala was ever likely to be the next Java (or /C/C++/C#) - I don't believe its creators were aiming it at that market - but I think it is/was certainly feasible for Scala to become the next Ruby or Python (i.e. to gain a significant commercial user-base)
Is this going to put people off coming to Scala?
Is this going to give Scala a bad name in the commercial world as an academic plaything that only dedicated PhD students can understand? Are CTOs and heads of software going to get scared off?
Was the library re-design a sensible idea?
If you're using Scala commercially, are you worried about this? Are you planning to adopt 2.8 immediately or wait to see what happens?
Steve Yegge once attacked Scala (mistakenly in my opinion) for what he saw as its overcomplicated type-system. I worry that someone is going to have a field day spreading FUD with this API (similarly to how Josh Bloch scared the JCP out of adding closures to Java).
Note - I should be clear that, whilst I believe that Joshua Bloch was influential in the rejection of the BGGA closures proposal, I don't ascribe this to anything other than his honestly-held beliefs that the proposal represented a mistake.
Despite whatever my wife and coworkers keep telling me, I don't think I'm an idiot: I have a good degree in mathematics from the University of Oxford, and I've been programming commercially for almost 12 years and in Scala for about a year (also commercially).
Note the inflammatory subject title is a quotation made about the manifesto of a UK political party in the early 1980s. This question is subjective but it is a genuine question, I've made it CW and I'd like some opinions on the matter.
I hope it's not a "suicide note", but I can see your point. You hit on what is at the same time both a strength and a problem of Scala: its extensibility. This lets us implement most major functionality in libraries. In some other languages, sequences with something like map or collect would be built in, and nobody has to see all the hoops the compiler has to go through to make them work smoothly. In Scala, it's all in a library, and therefore out in the open.
In fact the functionality of map that's supported by its complicated type is pretty advanced. Consider this:
scala> import collection.immutable.BitSet
import collection.immutable.BitSet
scala> val bits = BitSet(1, 2, 3)
bits: scala.collection.immutable.BitSet = BitSet(1, 2, 3)
scala> val shifted = bits map { _ + 1 }
shifted: scala.collection.immutable.BitSet = BitSet(2, 3, 4)
scala> val displayed = bits map { _.toString + "!" }
displayed: scala.collection.immutable.Set[java.lang.String] = Set(1!, 2!, 3!)
See how you always get the best possible type? If you map Ints to Ints you get again a BitSet, but if you map Ints to Strings, you get a general Set. Both the static type and the runtime representation of map's result depend on the result type of the function that's passed to it. And this works even if the set is empty, so the function is never applied! As far as I know there is no other collection framework with an equivalent functionality. Yet from a user perspective this is how things are supposed to work.
The problem we have is that all the clever technology that makes this happen leaks into the type signatures which become large and scary. But maybe a user should not be shown by default the full type signature of map? How about if she looked up map in BitSet she got:
map(f: Int => Int): BitSet (click here for more general type)
The docs would not lie in that case, because from a user perspective indeed map has the type (Int => Int) => BitSet. But map also has a more general type which can be inspected by clicking on another link.
We have not yet implemented functionality like this in our tools. But I believe we need to do this, to avoid scaring people off and to give more useful info. With tools like that, hopefully smart frameworks and libraries will not become suicide notes.
I do not have a PhD, nor any other kind of degree neither in CS nor math nor indeed any other field. I have no prior experience with Scala nor any other similar language. I have no experience with even remotely comparable type systems. In fact, the only language that I have more than just a superficial knowledge of which even has a type system is Pascal, not exactly known for its sophisticated type system. (Although it does have range types, which AFAIK pretty much no other language has, but that isn't really relevant here.) The other three languages I know are BASIC, Smalltalk and Ruby, none of which even have a type system.
And yet, I have no trouble at all understanding the signature of the map function you posted. It looks to me like pretty much the same signature that map has in every other language I have ever seen. The difference is that this version is more generic. It looks more like a C++ STL thing than, say, Haskell. In particular, it abstracts away from the concrete collection type by only requiring that the argument is IterableLike, and also abstracts away from the concrete return type by only requiring that an implicit conversion function exists which can build something out of that collection of result values. Yes, that is quite complex, but it really is only an expression of the general paradigm of generic programming: do not assume anything that you don't actually have to.
In this case, map does not actually need the collection to be a list, or being ordered or being sortable or anything like that. The only thing that map cares about is that it can get access to all elements of the collection, one after the other, but in no particular order. And it does not need to know what the resulting collection is, it only needs to know how to build it. So, that is what its type signature requires.
So, instead of
map :: (a → b) → [a] → [b]
which is the traditional type signature for map, it is generalized to not require a concrete List but rather just an IterableLike data structure
map :: (IterableLike i, IterableLike j) ⇒ (a → b) → i → j
which is then further generalized by only requiring that a function exists that can convert the result to whatever data structure the user wants:
map :: IterableLike i ⇒ (a → b) → i → ([b] → c) → c
I admit that the syntax is a bit clunkier, but the semantics are the same. Basically, it starts from
def map[B](f: (A) ⇒ B): List[B]
which is the traditional signature for map. (Note how due to the object-oriented nature of Scala, the input list parameter vanishes, because it is now the implicit receiver parameter that every method in a single-dispatch OO system has.) Then it generalized from a concrete List to a more general IterableLike
def map[B](f: (A) ⇒ B): IterableLike[B]
Now, it replaces the IterableLike result collection with a function that produces, well, really just about anything.
def map[B, That](f: A ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
Which I really believe is not that hard to understand. There's really only a couple of intellectual tools you need:
You need to know (roughly) what map is. If you gave only the type signature without the name of the method, I admit, it would be a lot harder to figure out what is going on. But since you already know what map is supposed to do, and you know what its type signature is supposed to be, you can quickly scan the signature and focus on the anomalies, like "why does this map take two functions as arguments, not one?"
You need to be able to actually read the type signature. But even if you have never seen Scala before, this should be quite easy, since it really is just a mixture of type syntaxes you already know from other languages: VB.NET uses square brackets for parametric polymorphism, and using an arrow to denote the return type and a colon to separate name and type, is actually the norm.
You need to know roughly what generic programming is about. (Which isn't that hard to figure out, since it's basically all spelled out in the name: it's literally just programming in a generic fashion).
None of these three should give any professional or even hobbyist programmer a serious headache. map has been a standard function in pretty much every language designed in the last 50 years, the fact that different languages have different syntax should be obvious to anyone who has designed a website with HTML and CSS and you can't subscribe to an even remotely programming related mailinglist without some annoying C++ fanboy from the church of St. Stepanov explaining the virtues of generic programming.
Yes, Scala is complex. Yes, Scala has one of the most sophisticated type systems known to man, rivaling and even surpassing languages like Haskell, Miranda, Clean or Cyclone. But if complexity were an argument against success of a programming language, C++ would have died long ago and we would all be writing Scheme. There are lots of reasons why Scala will very likely not be successful, but the fact that programmers can't be bothered to turn on their brains before sitting down in front of the keyboard is probably not going to be the main one.
Same thing in C++:
template <template <class, class> class C,
class T,
class A,
class T_return,
class T_arg
>
C<T_return, typename A::rebind<T_return>::other>
map(C<T, A> &c,T_return(*func)(T_arg) )
{
C<T_return, typename A::rebind<T_return>::other> res;
for ( C<T,A>::iterator it=c.begin() ; it != c.end(); it++ ){
res.push_back(func(*it));
}
return res;
}
Well, I can understand your pain, but, quite frankly, people like you and I -- or pretty much any regular Stack Overflow user -- are not the rule.
What I mean by that is that... most programmers won't care about that type signature, because they'll never see them! They don't read documentation.
As long as they saw some example of how the code works, and the code doesn't fail them in producing the result they expect, they won't ever look at the documentation. When that fails, they'll look at the documentation and expect to see usage examples at the top.
With these things in mind, I think that:
Anyone (as in, most people) who ever comes across that type signature will mock Scala to no end if they are pre-disposed against it, and will consider it a symbol of Scala's power if they like Scala.
If the documentation isn't enhanced to provide usage examples and explain clearly what a method is for and how to use it, it can detract from Scala adoption a bit.
In the long run, it won't matter. That Scala can do stuff like that will make libraries written for Scala much more powerful and safer to use. These libraries and frameworks will attract programmers atracted to powerful tools.
Programmers who like simplicity and directness will continue to use PHP, or similar languages.
Alas, Java programmers are much into power tools, so, in answering that, I have just revised my expectation of mainstream Scala adoption. I have no doubt at all that Scala will become a mainstream language. Not C-mainstream, but perhaps Perl-mainstream or PHP-mainstream.
Speaking of Java, did you ever replace the class loader? Have you ever looked into what that involves? Java can be scary, if you look at the places framework writers do. It's just that most people don't. The same thing applies to Scala, IMHO, but early adopters have a tendency to look under each rock they encounter, to see if there's something hiding there.
Is this going to put people off coming to Scala?
Yes, but it will also prevent people from being put off. I've considered the lack of collections that use higher-kinded types to be a major weakness ever since Scala gained support for higher-kinded types. It make the API docs more complicated, but it really makes usage more natural.
Is this going to give scala a bad name in the commercial world as an academic plaything that only dedicated PhD students can understand? Are CTOs and heads of software going to get scared off?
Some probably will. I don't think Scala is accessible to many "professional" developers, partially due to the complexity of Scala and partly due to the unwillingness of many developers to learn. The CTOs who employ such developers will rightly be scared off.
Was the library re-design a sensible idea?
Absolutely. It makes collections fit much better with the rest of the language and the type system, even if it still has some rough edges.
If you're using scala commercially, are you worried about this? Are you planning to adopt 2.8 immediately or wait to see what happens?
I'm not using it commercially. I'll probably wait until at least a couple revs into the 2.8.x series before even trying to introduce it so that the bugs can be flushed out. I'll also wait to see how much success EPFL has in improving its development a release processes. What I'm seeing looks hopeful, but I work for a conservative company.
One the more general topic of "is Scala too complicated for mainstream developers?"...
Most developers, mainstream or otherwise, are maintaining or extending existing systems. This means that most of what they use is dictated by decisions made long ago. There are still plenty of people writing COBOL.
Tomorrow's mainstream developer will work maintaining and extending the applications that are being built today. Many of these applications are not being built by mainstream developers. Tomorrow's mainstream developers will use the language that is being used by today's most successful developers of new applications.
One way that the Scala community can help ease the fear of programmers new to Scala is to focus on practice and to teach by example--a lot of examples that start small and grow gradually larger. Here are a few sites that take this approach:
Daily Scala
Learning Scala in small bites
Simply Scala
After spending some time on these sites, one quickly realizes that Scala and its libraries, though perhaps difficult to design and implement, are not so difficult to use, especially in the common cases.
I have an undergraduate degree from a cheap "mass market" US university, so I'd say I fall into the middle of the user intelligence (or at least education) scale :) I've been dabbling with Scala for just a few months and have worked on two or three non-trivial apps.
Especially now that IntelliJ has released their fine IDE with what IMHO is currently the best Scala plugin, Scala development is relatively painless:
I find I can use Scala as a "Java without semicolons," i.e. I write similar-looking code to what I'd do in Java, and benefit a little from syntactic brevity such as that gained by type inference. Exception handling, when I do it at all, is more convenient. Class definition is much less verbose without the getter/setter boilerplate.
Once in a while I manage to write a single line to accomplish the equivalent of multiple lines of Java. Where applicable, chains of functional methods like map, fold, collect, filter etc. are fun to compose and elegant to behold.
Only rarely do I find myself benefitting from Scala's more high-powered features: Closures and partial (or curried) functions, pattern matching... that kinda thing.
As a newbie, I continue to struggle with the terse and idiomatic syntax. Method calls without parameters don't need parentheses except where they do; cases in the match statement need a fat arrow ( => ), but there are also places where you need a thin arrow ( -> ). Many methods have short but rather cryptic names like /: or \: - I can get my stuff done if I flip enough manual pages, but some of my code ends up looking like Perl or line noise. Ironically, one of the most popular bits of syntactic shorthand is missing in action: I keep getting bitten by the fact that Int doesn't define a ++ method.
This is just my opinion: I feel like Scala has the power of C++ combined with the complexity and readability of C++. The syntactic complexity of the language also makes the API documentation hard to read.
Scala is very well thought out and brilliant in many respects. I suspect many an academic would love to program in it. However, it's also full of cleverness and gotchas, it has a much higher learning curve than Java and is harder to read. If I scan the fora and see how many developers are still struggling with the finer points of Java, I cannot conceive of Scala ever becoming a mainstream language. No company will be able to justify sending its developers on a 3 week Scala course when formerly they only needed a 1 week Java course.
I think primary problem with that method is that the (implicit bf : CanBuildFrom[Repr, B, That]) goes without any explanation. Even though I know what implicit arguments are there's nothing indicating how this affects the call. Chasing through the scaladoc only leaves me more confused (few of the classes related to CanBuildFrom even have documentation).
I think a simple "there must be an implicit object in scope for bf that provides a builder for objects of type B into the return type That" would help somewhat, but it's kind of a heady concept when all you really want to do is map A's to B's. In fact, I'm not sure that's right, because I don't know what the type Repr means, and the documentation for Traversable certainly gives no clue at all.
So, I'm left with two options, neither of them pleasant:
Assume it will just work how the old map works and how map works in most other languages
Dig into the source code some more
I get that Scala is essentially exposing the guts of how these things work and that ultimately this is provide a way to do what oxbow_lakes is describing. But it's a distraction in the signature.
I'm a Scala beginner and I honestly don't see a problem with that type signature. The parameter is the function to map and the implicit parameter the builder to return the correct collection. Clear and readable.
The whole thing's quite elegant, actually. The builder type parameters let the compiler choose the correct return type while the implicit parameter mechanism hides this extra parameter from the class user. I tried this:
Map(1 -> "a", 2 -> "b").map((t) => (t._2) -> (t._1)) // returns Map("a" -> 1, "b" -> 2)
Map(1 -> "a", 2 -> "b").map((t) => t._2) // returns List("a", "b")
That's polymorphism done right.
Now, granted, it's not a mainstream paradigm and it will scare away many. But, it will also attract many who value its expressiveness and elegance.
Unfortunately the signature for map that you gave is an incorrect one for map and there is indeed legitimate criticism.
The first criticism is that by subverting the signature for map, we have something that is more general. It is a common error to believe that this is a virtue by default. It isn't. The map function is very well defined as a covariant functor Fx -> (x -> y) -> Fy with adherence to the two laws of composition and identity. Anything else attributed to "map" is a travesty.
The given signature is something else, but it is not map. What I suspect it is trying to be is a specialised and slightly altered version of the "traverse" signature from the paper, The Essence of the Iterator Pattern. Here is its signature:
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
I shall convert it to Scala:
def traverse[A, B](f: A => F[B], a: T[A])(implicit t: Traversable[T], ap: Applicative[F]): F[T[B]
Of course it fails -- it is not general enough! Also, it is slightly different (note that you can get map by running traverse through the Identity functor). However, I suspect that if the library writers were more aware of library generalisations that are well documented (Applicative Programming with Effects precedes the aforementioned), then we wouldn't see this error.
Second, the map function is a special-case in Scala because of its use in for-comprehensions. This unfortunately means that a library designer who is better equipped cannot ignore this error without also sacrificing the syntactic sugar of comprehensions. In other words, if the Scala library designers were to destroy a method, then this is easily ignored, but please not map!
I hope someone speaks up about it, because as it is, it will become harder to workaround the errors that Scala insists on making, apparently for reasons that I have strong objections to. That is, the solution to "the irresponsible objections from the average programmer (i.e. too hard!)" is not "appease them to make it easier for them" but instead, provide pointers and assistance to become better programmers. Myself and Scala's objectives are in contention on this issue, but back to your point.
You were probably making your point, predicting specific responses from "the average programmer." That is, the people who will claim "but it is too complicated!" or some such. These are the Yegges or Blochs that you refer to. My response to these people of the anti-intellectualism/pragmatism movement is quite harsh and I'm already anticipating a barrage of responses, so I will omit it.
I truly hope the Scala libraries improve, or at least, the errors can be safely tucked away in a corner. Java is a language where "trying to do anything useful" is so incredibly costly, that it is often not worth it because the overwhelming amount of errors simply cannot be avoided. I implore Scala to not go down the same path.
I totally agree with both the question and Martin's answer :). Even in Java, reading javadoc with generics is much harder than it should be due to the extra noise. This is compounded in Scala where implicit parameters are used as in the questions's example code (while the implicits do very useful collection-morphing stuff).
I don't think its a problem with the language per se - I think its more a tooling issue. And while I agree with what Jörg W Mittag says, I think looking at scaladoc (or the documentation of a type in your IDE) - it should require as little brain power as possible to grok what a method is, what it takes and returns. There shouldn't be a need to hack up a bit of algebra on a bit of paper to get it :)
For sure IDEs need a nice way to show all the methods for any variable/expression/type (which as with Martin's example can have all the generics inlined so its nice and easy to grok). I like Martin's idea of hiding the implicits by default too.
To take the example in scaladoc...
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
When looking at this in scaladoc I'd like the generic block [B, That] to be hidden by default as well as the implicit parameter (maybe they show if you hover a little icon with the mouse) - as its extra stuff to grok reading it which usually isn't that relevant. e.g. imagine if this looked like...
def map(f: A => B): That
nice and clear and obvious what it does. You might wonder what 'That' is, if you mouse over or click it it could expand the [B, That] text highlighting the 'That' for example.
Maybe a little icon could be used for the [] declaration and (implicit...) block so its clear there are little bits of the statement collapsed? Its hard to use a token for it, but I'll use a . for now...
def map.(f: A => B).: That
So by default the 'noise' of the type system is hidden from the main 80% of what folks need to look at - the method name, its parameter types and its return type in nice simple concise way - with little expandable links to the detail if you really care that much.
Mostly folks are reading scaladoc to find out what methods they can call on a type and what parameters they can pass. We're kinda overloading users with way too much detail right how IMHO.
Here's another example...
def orElse[A1 <: A, B1 >: B](that: PartialFunction[A1, B1]): PartialFunction[A1, B1]
Now if we hid the generics declaration its easier to read
def orElse(that: PartialFunction[A1, B1]): PartialFunction[A1, B1]
Then if folks hover over, say, A1 we could show the declaration of A1 being A1 <: A. Covariant and contravariant types in generics add lots of noise too which can be rendered in a much easier to grok way to users I think.
I don't know how to break it to you, but I have a PhD from Cambridge, and I'm using 2.8 just fine.
More seriously, I hardly spent any time with 2.7 (it won't inter-op with a Java library I am using) and started using Scala just over a month ago. I have some experience with Haskell (not much), but just ignored the stuff you're worried about and looked for methods that matched my experience with Java (which I use for a living).
So: I am a "new user" and I wasn't put off - the fact that it works like Java gave me enough confidence to ignore the bits I didn't understand.
(However, the reason I was looking at Scala was partly to see whether to push it at work, and I am not going to do so yet. Making the documentation less intimidating would certainly help, but what surprised me is how much it is still changing and being developed (to be fair what surprised me most was how awesome it is, but the changes came a close second). So I guess what I am saying is that I'd rather prefer the limited resources were put into getting it into a final state - I don't think they were expecting to be this popular this soon.)
Don't know Scala at all, however a few weeks ago I could not read Clojure. Now I can read most of it, but can not write anything yet beyond the most simplistic examples. I suspect Scala is no different. You need a good book or course depending on how you learn. Just reading the map declaration above, I got maybe 1/3 of it.
I believe the bigger problems are not the syntax of these languages, but adopting and internalizing the paradigms that make them usable in everyday production code. For me Java was not a huge leap from C++, which was not a huge leap from C, which was not a leap at all from Pascal, nor Basic etc... But coding in a functional language like Clojure is a huge leap (for me anyway). I guess in Scala you can code in Java style or Scala style. But in Clojure you will create quite the mess trying to keep your imperative habits from Java.
Scala has a lot of crazy features (particularly where implicit parameters are concerned) that look very complicated and academic, but are designed to make things easy to use. The most useful ones get syntactic sugar (like [A <% B] which means that an object of type A has an implicit conversion to an object of type B) and a well-documented explanation of what they do. But most of the time, as a client of these libraries you can ignore the implicit parameters and trust them to do the right thing.
Is this going to put people off coming to Scala?
I don't think it is the main factor that will affect how popular Scala will become, because Scala has a lot of power and its syntax is not as foreign to a Java/C++/PHP programmer as Haskell, OCaml, SML, Lisps, etc..
But I do think Scala's popularity will plateau at less than where Java is today, because I also think the next mainstream language must be much simplified, and the only way I see to get there is pure immutability, i.e. declarative like HTML, but Turing complete. However, I am biased because I am developing such a language, but I only did so after ruling out over a several month study that Scala could not suffice for what I needed.
Is this going to give Scala a bad name in the commercial world as an academic plaything that only dedicated PhD students can understand? Are CTOs and heads of software going to get scared off?
I don't think Scala's reputation will suffer from the Haskell complex. But I think that some will put off learning it, because for most programmers, I don't yet see a use case that forces them to use Scala, and they will procrastinate learning about it. Perhaps the highly-scalable server side is the most compelling use case.
And, for the mainstream market, first learning Scala is not a "breath of fresh air", where one is writing programs immediately, such as first using HTML or Python. Scala tends to grow on you, after one learns all the details that one stumbles on from the start. However, maybe if I had read Programming in Scala from the start, my experience and opinion of the learning curve would have been different.
Was the library re-design a sensible idea?
Definitely.
If you're using Scala commercially, are you worried about this? Are you planning to adopt 2.8 immediately or wait to see what happens?
I am using Scala as the initial platform of my new language. I probably wouldn't be building code on Scala's collection library if I was using Scala commercially otherwise. I would create my own category theory based library, since the one time I looked, I found Scalaz's type signatures even more verbose and unwieldy than Scala's collection library. Part of that problem perhaps is Scala's way of implementing type classes, and that is a minor reason I am creating my own language.
I decided to write this answer, because I wanted to force myself to research and compare Scala's collection class design to the one I am doing for my language. Might as well share my thought process.
The 2.8 Scala collections use of a builder abstraction is a sound design principle. I want to explore two design tradeoffs below.
WRITE-ONLY CODE: After writing this section, I read Carl Smotricz's comment which agrees with what I expect to be the tradeoff. James Strachan and davetron5000's comments concur that the meaning of That (it is not even That[B]) and the mechanism of the implicit is not easy to grasp intuitively. See my use of monoid in issue #2 below, which I think is much more explicit. Derek Mahar's comment is about writing Scala, but what about reading the Scala of others that is not "in the common cases".
One criticism I have read about Scala, is that it is easier to write it, than read the code that others have written. And I find this to be occasionally true for various reasons (e.g. many ways to write a function, automatic closures, Unit for DSLs, etc), but I am undecided if this is major factor. Here the use of implicit function parameters has pluses and minuses. On the plus side, it reduces verbosity and automates selection of the builder object. In Odersky's example the conversion from a BitSet, i.e. Set[Int], to a Set[String] is implicit. The unfamiliar reader of the code might not readily know what the type of collection is, unless they can reason well about the all the potential invisible implicit builder candidates which might exist in the current package scope. Of course, the experienced programmer and the writer of the code will know that BitSet is limited to Int, thus a map to String has to convert to a different collection type. But which collection type? It isn't specified explicitly.
AD-HOC COLLECTION DESIGN: After writing this section, I read Tony Morris's comment and realized I am making nearly the same point. Perhaps my more verbose exposition will make the point more clear.
In "Fighting Bit Rot with Types" Odersky & Moors, two use cases are presented. They are the restriction of BitSet to Int elements, and Map to pair tuple elements, and are provided as the reason that the general element mapping function, A => B, must be able to build alternative destination collection types. However, afaik this is flawed from a category theory perspective. To be consistent in category theory and thus avoid corner cases, these collection types are functors, in which each morphism, A => B, must map between objects in the same functor category, List[A] => List[B], BitSet[A] => BitSet[B]. For example, an Option is a functor that can be viewed as a collection of sets of one Some( object ) and the None. There is no general map from Option's None, or List's Nil, to other functors which don't have an "empty" state.
There is a tradeoff design choice made here. In the design for collections library of my new language, I chose to make everything a functor, which means if I implement a BitSet, it needs to support all element types, by using a non-bit field internal representation when presented with a non-integer type parameter, and that functionality is already in the Set which it inherits from in Scala. And Map in my design needs to map only its values, and it can provide a separate non-functor method for mapping its (key,value) pair tuples. One advantage is that each functor is then usually also an applicative and perhaps a monad too. Thus all functions between element types, e.g. A => B => C => D => ..., are automatically lifted to the functions between lifted applicative types, e.g. List[A] => List[B] => List[C] => List[D] => .... For mapping from a functor to another collection class, I offer a map overload which takes a monoid, e.g. Nil, None, 0, "", Array(), etc.. So the builder abstraction function is the append method of a monoid and is supplied explicitly as a necessary input parameter, thus with no invisible implicit conversions. (Tangent: this input parameter also enables appending to non-empty monoids, which Scala's map design can't do.) Such conversions are a map and a fold in the same iteration pass. Also I provide a traversable, in the category sense, "Applicative programming with effects" McBride & Patterson, which also enables map + fold in a single iteration pass from any traversable to any applicative, where most every collection class is both. Also the state monad is an applicative and thus is a fully generalized builder abstraction from any traversable.
So afaics the Scala collections is "ad-hoc" in the sense that it is not grounded in category theory, and category theory is the essense of higher-level denotational semantics. Although Scala's implicit builders are at first appearance "more generalized" than a functor model + monoid builder + traversable -> applicative, they are afaik not proven to be consistent with any category, and thus we don't know what rules they follow in the most general sense and what the corner cases will be given they may not obey any category model. It is simply not true that adding more variables makes something more general, and this was one of huge benefits of category theory is it provides rules by which to maintain generality while lifting to higher-level semantics. A collection is a category.
I read somewhere, I think it was Odersky, as another justification for the library design, is that programming in a pure functional style has the cost of limited recursion and speed where tail recursion isn't used. I haven't found it difficult to employ tail recursion in every case that I have encountered so far.
Additionally I am carrying in my mind an incomplete idea that some of Scala's tradeoffs are due to trying to be both an mutable and immutable language, unlike for example Haskell or the language I am developing. This concurs with Tony Morris's comment about for comprehensions. In my language, there are no loops and no mutable constructs. My language will sit on top of Scala (for now) and owes much to it, and this wouldn't be possible if Scala didn't have the general type system and mutability. That might not be true though, because I think Odersky & Moors ("Fighting Bit Rot with Types") are incorrect to state that Scala is the only OOP language with higher-kinds, because I verified (myself and via Bob Harper) that Standard ML has them. Also appears SML's type system may be equivalently flexible (since 1980s), which may not be readily appreciated because the syntax is not so much similar to Java (and C++/PHP) as Scala. In any case, this isn't a criticism of Scala, but rather an attempt to present an incomplete analysis of tradeoffs, which is I hope germane to the question. Scala and SML don't suffer from Haskell's inability to do diamond multiple inheritance, which is critical and I understand is why so many functions in the Haskell Prelude are repeated for different types.
It seems necessary to state ones degree here: B.A. in Political Science and B.ed in Computer Science.
To the point:
Is this going to put people off coming to Scala?
Scala is difficult, because its underlying programming paradigm is difficult. Functional programming scares a lot of people. It is possible to build closures in PHP but people rarely do. So no, not this signature but all the rest will put people off, if they do not have the specific education to make them value the power of the underlying paradigm.
If this education is available, everyone can do it. Last year I build a chess computer with a bunch of school kids in SCALA! They had their problems but they did fine in the end.
If you're using Scala commercially, are you worried about this? Are you planning to adopt 2.8 immediately or wait to see what happens?
I would not be worried.
I have a maths degree from Oxford too! It took me a while to 'get' the new collections stuff. But I like it a lot now that I do. In fact, the typing of 'map' was one of the first big things that bugged me in 2.7 (perhaps since the first thing I did was subclass one of the collection classes).
Reading Martin's paper on the new 2.8 collections really helped explain the use of implicits, but yes the documentation itself definitely needs to do a better job of explaining the role of different kind of implicits within method signatures of core APIs.
My main concern is more this: when is 2.8 going to be released? When will the bug reports stop coming in for it? have scala team bitten off more than they can chew with 2.8 / tried to change too much at once?
I'd really like to see 2.8 stabilised for release as a priority before adding anything else new at all, and wonder (while watching from the sidelines) if some improvements could be made to the way the development roadmap for the scala compiler is managed.
What about error messages in use site?
And what about when comes the use case one needs to integrate existing types with a custom one that fits a DSL. One have to be well educated on matters of association, precedence, implicit conversions, implicit parameters, higher kinds, and maybe existential types.
It's very good to know that mostly it's simple but it's not necessarily enough. At least there must be one guy who knows this stuff if widespread library is to be designed.