The problem
When I'm working with libraries that support type-level programming, I often find myself writing comments like the following (from an example presented by Paul Snively at Strange Loop 2012):
// But these invalid sequences don't compile:
// isValid(_3 :: _1 :: _5 :: _8 :: _8 :: _2 :: _8 :: _6 :: _5 :: HNil)
// isValid(_3 :: _4 :: _5 :: _8 :: _8 :: _2 :: _8 :: _6 :: HNil)
Or this, from an example in the Shapeless repository:
/**
* If we wanted to confirm that the list uniquely contains `Foo` or any
* subtype of `Foo`, we could first use `unifySubtypes` to upcast any
* subtypes of `Foo` in the list to `Foo`.
*
* The following would not compile, for example:
*/
//stuff.unifySubtypes[Foo].unique[Foo]
This is a very rough way of indicating some fact about the behavior of these methods, and we could imagine wanting to make these assertions more formal—for unit or regression testing, etc.
To give a concrete example of why this might be useful in the context of a library like Shapeless, a few days ago I wrote the following as a quick first attempt at an answer to this question:
import shapeless._
implicit class Uniqueable[L <: HList](l: L) {
def unique[A](implicit ev: FilterAux[L, A, A :: HNil]) = ev(l).head
}
Where the intention is that this will compile:
('a' :: 'b :: HNil).unique[Char]
While this will not:
('a' :: 'b' :: HNil).unique[Char]
I was surprised to find that this implementation of a type-level unique for HList didn't work, because Shapeless would happily find a FilterAux instance in the latter case. In other words, the following would compile, even though you'd probably expect it not to:
implicitly[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
In this case, what I was seeing was a bug—or at least something bug-ish—and it has since been fixed.
More generally, we can imagine wanting to check the kind of invariant that was implicit in my expectations about how FilterAux should work with something like a unit test—as weird as it may sound to be talking about testing type-level code like this, with all the recent debates about the relative merit of types vs. tests.
My question
The problem is that I don't know of any kind of testing framework (for any platform) that allows the programmer to assert that something must not compile.
One approach that I can imagine for the FilterAux case would be to use the old implicit-argument-with-null-default trick:
def assertNoInstanceOf[T](implicit instance: T = null) = assert(instance == null)
Which would let you write the following in your unit test:
assertNoInstanceOf[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
The following would be a heck of a lot more convenient and expressive, though:
assertDoesntCompile(('a' :: 'b' :: HNil).unique[Char])
I want this. My question is whether anyone knows of any testing library or framework that supports anything remotely like it—ideally for Scala, but I'll settle for anything.
Not a framework, but Jorge Ortiz (#JorgeO) mentioned some utilities he added to the tests for Foursquare's Rogue library at NEScala in 2012 which support tests for non-compilation: you can find examples here. I've been meaning to add something like this to shapeless for quite a while.
More recently, Roland Kuhn (#rolandkuhn) has added a similar mechanism, this time using Scala 2.10's runtime compilation, to the tests for Akka typed channels.
These are both dynamic tests of course: they fail at (test) runtime if something that shouldn't compile does. Untyped macros might provide a static option: ie. a macro could accept an untyped tree, type check it and throw a type error if it succeeds). This might be something to experiment with on the macro-paradise branch of shapeless. But not a solution for 2.10.0 or earlier, obviously.
Update
Since answering the question, another approach, due to Stefan Zeiger (#StefanZeiger), has surfaced. This one is interesting because, like the untyped macro one alluded to above, it is a compile time rather than (test) runtime check, however it is also compatible with Scala 2.10.x. As such I think it is preferable to Roland's approach.
I've now added implementations to shapeless for 2.9.x using Jorge's approach, for 2.10.x using Stefan's approach and for macro paradise using the untyped macro approach. Examples of the corresponding tests can be found here for 2.9.x, here for 2.10.x and here for macro paradise.
The untyped macro tests are the cleanest, but Stefan's 2.10.x compatible approach is a close second.
ScalaTest 2.1.0 has the following syntax for Assertions:
assertTypeError("val s: String = 1")
And for Matchers:
"val s: String = 1" shouldNot compile
Do you know about partest in the Scala project? E.g. CompilerTest has the following doc:
/** For testing compiler internals directly.
* Each source code string in "sources" will be compiled, and
* the check function will be called with the source code and the
* resulting CompilationUnit. The check implementation should
* test for what it wants to test and fail (via assert or other
* exception) if it is not happy.
*/
It is able to check for example whether this source https://github.com/scala/scala/blob/master/test/files/neg/divergent-implicit.scala will have this result https://github.com/scala/scala/blob/master/test/files/neg/divergent-implicit.check
It's not a perfect fit for your question (since you don't specify your test cases in terms of asserts), but may be an approach and/or give you a head start.
Based on the links provided by Miles Sabin I was able to use the akka version
import scala.tools.reflect.ToolBox
object TestUtils {
def eval(code: String, compileOptions: String = "-cp target/classes"): Any = {
val tb = mkToolbox(compileOptions)
tb.eval(tb.parse(code))
}
def mkToolbox(compileOptions: String = ""): ToolBox[_ <: scala.reflect.api.Universe] = {
val m = scala.reflect.runtime.currentMirror
m.mkToolBox(options = compileOptions)
}
}
Then in my tests I used it like this
def result = TestUtils.eval(
"""|import ee.ui.events.Event
|import ee.ui.events.ReadOnlyEvent
|
|val myObj = new {
| private val writableEvent = Event[Int]
| val event:ReadOnlyEvent[Int] = writableEvent
|}
|
|// will not compile:
|myObj.event.fire
|""".stripMargin)
result must throwA[ToolBoxError].like {
case e =>
e.getMessage must contain("value fire is not a member of ee.ui.events.ReadOnlyEvent[Int]")
}
The compileError macro in µTest does just that:
compileError("true * false")
// CompileError.Type("value * is not a member of Boolean")
compileError("(}")
// CompileError.Parse("')' expected but '}' found.")
Related
I am studying the cats library recently, and I have come across this class called NonEmptyList.
After reading the api, I couldn't help wondering what is it that made the cats authors to create a new class, instead of utilizing something that is built in (::) and use type classes to extend it. It is not even listed in the cats github page, so I have come here to ask about it. Maybe it is because cons is a subtype of List? (Though I do not know the implications of it)
What are the differences between :: and NEL? And why did cats authors have to write NEL instead of using ::?
The main reason for having NonEmptyList which doesn't extend from List is developer experience of including assumptions in the APIs.
Firstly, note that :: has all the methods List has which can be misleading and it makes it harder to design better APIs with more powerful assumptions. Additionally, List doesn't have any methods that directly return ::, which means that developer needs to maintain non-empty abstraction manually.
Let me show you an example which shows what I mean in practice:
// NonEmptyList usage is intuitive and types fit together nicely
val nonEmpty: NonEmptyList[Int] = NonEmptyList.of(1, 2, 3)
val biggerNonEmpty: NonEmptyList[Int] = 0 :: nonEmpty
val nonEmptyMapped: NonEmptyList[Int] = nonEmpty.map(_ * 2)
// :: has lots of problems
// PROBLEM: we can't easily instantiate ::
val cons: ::[Int] = 1 :: 2 :: 3 :: Nil // type mismatch; found: List[Int]; required: ::[Int]
val cons: ::[Int] = new ::[Int](1, ::(2, ::(3, Nil)))
// PROBLEM: adding new element to Cons returns List
val biggerCons: ::[Int] = 0 :: cons // type mismatch; found: List[Int]; required: ::[Int]
// PROBLEM: ::.map returns List
val consMapped : ::[Int] = cons.map(_ * 2) // type mismatch; found: List[Int]; required: ::[Int]
Note that NonEmptyList has methods that return List, namely filter, filterNot and collect. Why? Because filtering through NonEmptyList may mean that you filter out all elements and the list can become an empty one.
This is what makes the whole non-empty abstraction so powerful. By properly using function input and output types, you can encode assumptions about the API. :: doesn't provide this abstraction.
I have a List of items (in this case Spray Marshallers) which I want to make implicitly available in the current context. Just to clarify: I don't want the List itself to be implicitly available, but each element of the list. Does anyone have trick to do this? Or is this actually not possible since the exact implicits have to be available at compile time?
In this particular case, if I understood correctly, you need to have all your specific marshallers available in a context, hence you need to provide each of them implicitly.
You should put them into an object and define them as implicit values.
object Protocol {
implicit val marshaller1: ...
implicit val marshaller2: ...
}
Then, by importing the Protocol object, you would have all your marshallers implicitly available.
And to answer to your question, yes, implicits are resolved at compile time. Hence, whenever you need to use a specific marshaller, the implicit resolution algorithm will look for a value of the specific marshaller type. This is why you cannot get it from a list, let's say, and you shouldn't want this either way.
You can certainly achieve this with shapeless:
import shapeless._
import shapeless.ops.hlist.Selector
case class A()
case class B()
case class C()
val list : A :: B :: C :: HNil = A() :: B() :: C() :: HNil
def screamIfAccessible[T](implicit selector: Selector[A :: B :: C :: HNil ,T]) = {
selector(list) // Actually picks the T type element
// from the HList to work with
println("YAY!")
}
screamIfAccessible[A]
screamIfAccessible[B]
Shapeless - Generic programming with scala
Any method in your code that needs access to an element of type A from the list(or in this case HList) needs to have the Selector implicit argument
I have a function that expects a variable number of parameters of the same type, which sounds like the textbook use case for varargs:
def myFunc[A](as: A*) = ???
The problem I have is that myFunc cannot accept empty parameter lists. There's a trivial way of enforcing that at runtime:
def myFunc[A](as: A*) = {
require(as.nonEmpty)
???
}
The problem with that is that it happens at runtime, as opposed to compile time. I would like the compiler to reject myFunc().
One possible solution would be:
def myFunc[A](head: A, tail: A*) = ???
And this works when myFunc is called with inline arguments, but I'd like users of my library to be able to pass in a List[A], which this syntax makes very awkward.
I could try to have both:
def myFunc[A](head: A, tail: A*) = myFunc(head +: tail)
def myFunc[A](as: A*) = ???
But we're right back where we started: there's now a way of calling myFunc with an empty parameter list.
I'm aware of scalaz's NonEmptyList, but in as much as possible, I'd like to stay with stlib types.
Is there a way to achieve what I have in mind with just the standard library, or do I need to accept some runtime error handling for something that really feels like the compiler should be able to deal with?
What about something like this?
scala> :paste
// Entering paste mode (ctrl-D to finish)
def myFunc()(implicit ev: Nothing) = ???
def myFunc[A](as: A*) = println(as)
// Exiting paste mode, now interpreting.
myFunc: ()(implicit ev: Nothing)Nothing <and> [A](as: A*)Unit
myFunc: ()(implicit ev: Nothing)Nothing <and> [A](as: A*)Unit
scala> myFunc(3)
WrappedArray(3)
scala> myFunc(List(3): _*)
List(3)
scala> myFunc()
<console>:13: error: could not find implicit value for parameter ev: Nothing
myFunc()
^
scala>
Replacing Nothing with a class that has an appropriate implicitNotFound annotation should allow for a sensible error message.
Let's start out with what I think is your base requirement: the ability to define myFunc in some way such that the following occurs at the Scala console when a user provides literals. Then maybe if we can achieve that, we can try to go for varargs.
myFunc(List(1)) // no problem
myFunc(List[Int]()) // compile error!
Moreover, we don't want to have to force users either to split a list into a head and tail or have them convert to a ::.
Well when we're given literals, since we have access to the syntax used to construct the value, we can use macros to verify that a list is non-empty. Moreover, there's already a library that'll do it for us, namely refined!
scala> refineMV[NonEmpty]("Hello")
res2: String Refined NonEmpty = Hello
scala> refineMV[NonEmpty]("")
<console>:39: error: Predicate isEmpty() did not fail.
refineMV[NonEmpty]("")
^
Unfortunately this is still problematic in your case, because you'll need to put refineMV into the body of your function at which point the literal syntactically disappears and macro magic fails.
Okay what about the general case that doesn't rely on syntax?
// Can we do this?
val xs = getListOfIntsFromStdin() // Pretend this function exists
myFunc(xs) // compile error if xs is empty
Well now we're up against a wall; there's no way a compile time error can happen here since the code has already been compiled and yet clearly xs could be empty. We'll have to deal with this case at runtime, either in a type-safe manner with Option and the like or with something like runtime exceptions. But maybe we can do a little better than just throw our hands up in the air. There's two possible paths of improvement.
Somehow provide implicit evidence that xs is nonempty. If the compiler can find that evidence, then great! If not, it's on the user to provide it somehow at runtime.
Track the provenance of xs through your program and statically prove that it must be non-empty. If this cannot be proved, either error out at compile time or somehow force the user to handle the empty case.
Once again, unfortunately this is problematic.
I strongly suspect this is not possible (but this is still only a suspicion and I would be happy to be proved wrong). The reason is that ultimately implicit resolution is type-directed which means that Scala gets the ability to do type-level computation on types, but Scala has no mechanism that I know of to do type-level computation on values (i.e. dependent typing). We require the latter here because List(1, 2, 3) and List[Int]() are indistinguishable at the type level.
Now you're in SMT solver land, which does have some efforts in other languages (hello Liquid Haskell!). Sadly I don't know of any such efforts in Scala (and I imagine it would be a harder task to do in Scala).
The bottom line is that when it comes to error checking there is no free lunch. A compiler can't magically make error handling go away (although it can tell you when you don't strictly need it), the best it can do is yell at you when you forget to handle certain classes of errors, which is itself very valuable. To underscore the no free lunch point, let's return to a language that does have dependent types (Idris) and see how it handles non-empty values of List and the prototypical function that breaks on empty lists, List.head.
First we get a compile error on empty lists
Idris> List.head []
(input):1:11:When checking argument ok to function Prelude.List.head:
Can't find a value of type
NonEmpty []
Good, what about non-empty lists, even if they're obfuscated by a couple of leaps?
Idris> :let x = 5
-- Below is equivalent to
-- val y = identity(Some(x).getOrElse(3))
Idris> :let y = maybe 3 id (Just x)
-- Idris makes a distinction between Natural numbers and Integers
-- Disregarding the Integer to Nat conversion, this is
-- val z = Stream.continually(2).take(y)
Idris> :let z = Stream.take (fromIntegerNat y) (Stream.repeat 2)
Idris> List.head z
2 : Integer
It somehow works! What if we really don't let the Idris compiler know anything about the number we pass along and instead get one at runtime from the user? We blow up with a truly gargantuan error message that starts with When checking argument ok to function Prelude.List.head: Can't find a value of type NonEmpty...
import Data.String
generateN1s : Nat -> List Int
generateN1s x = Stream.take x (Stream.repeat 1)
parseOr0 : String -> Nat
parseOr0 str = case parseInteger str of
Nothing => 0
Just x => fromIntegerNat x
z : IO Int
z = do
x <- getLine
let someNum = parseOr0 x
let firstElem = List.head $ generateN1s someNum -- Compile error here
pure firstElem
Hmmm... well what's the type signature of List.head?
Idris> :t List.head
-- {auto ...} is roughly the same as Scala's implicit
head : (l : List a) -> {auto ok : NonEmpty l} -> a
Ah so we just need to provide a NonEmpty.
data NonEmpty : (xs : List a) -> Type where
IsNonEmpty : NonEmpty (x :: xs)
Oh a ::. And we're back at square one.
Use scala.collection.immutable.::
:: is the cons of the list
defined in std lib
::[A](head: A, tail: List[A])
use :: to define myFunc
def myFunc[A](list: ::[A]): Int = 1
def myFunc[A](head: A, tail: A*): Int = myFunc(::(head, tail.toList))
Scala REPL
scala> def myFunc[A](list: ::[A]): Int = 1
myFunc: [A](list: scala.collection.immutable.::[A])Int
scala> def myFunc[A](head: A, tail: A*): Int = myFunc(::(head, tail.toList))
myFunc: [A](head: A, tail: A*)Int
In languages like SML, Erlang and in buch of others we may define functions like this:
fun reverse [] = []
| reverse x :: xs = reverse xs # [x];
I know we can write analog in Scala like this (and I know, there are many flaws in the code below):
def reverse[T](lst: List[T]): List[T] = lst match {
case Nil => Nil
case x :: xs => reverse(xs) ++ List(x)
}
But I wonder, if we could write former code in Scala, perhaps with desugaring to the latter.
Is there any fundamental limitations for such syntax being implemented in the future (I mean, really fundamental -- e.g. the way type inference works in scala, or something else, except parser obviously)?
UPD
Here is a snippet of how it could look like:
type T
def reverse(Nil: List[T]) = Nil
def reverse(x :: xs: List[T]): List[T] = reverse(xs) ++ List(x)
It really depends on what you mean by fundamental.
If you are really asking "if there is a technical showstopper that would prevent to implement this feature", then I would say the answer is no. You are talking about desugaring, and you are on the right track here. All there is to do is to basically stitch several separates cases into one single function, and this can be done as a mere preprocessing step (this only requires syntactic knowledge, no need for semantic knowledge). But for this to even make sense, I would define a few rules:
The function signature is mandatory (in Haskell by example, this would be optional, but it is always optional whether you are defining the function at once or in several parts). We could try to arrange to live without the signature and attempt to extract it from the different parts, but lack of type information would quickly come to byte us. A simpler argument is that if we are to try to infer an implicit signature, we might as well do it for all the methods. But the truth is that there are very good reasons to have explicit singatures in scala and I can't imagine to change that.
All the parts must be defined within the same scope. To start with, they must be declared in the same file because each source file is compiled separately, and thus a simple preprocessor would not be enough to implement the feature. Second, we still end up with a single method in the end, so it's only natural to have all the parts in the same scope.
Overloading is not possible for such methods (otherwise we would need to repeat the signature for each part just so the preprocessor knows which part belongs to which overload)
Parts are added (stitched) to the generated match in the order they are declared
So here is how it could look like:
def reverse[T](lst: List[T]): List[T] // Exactly like an abstract def (provides the signature)
// .... some unrelated code here...
def reverse(Nil) = Nil
// .... another bit of unrelated code here...
def reverse(x :: xs ) = reverse(xs) ++ List(x)
Which could be trivially transformed into:
def reverse[T](list: List[T]): List[T] = lst match {
case Nil => Nil
case x :: xs => reverse(xs) ++ List(x)
}
// .... some unrelated code here...
// .... another bit of unrelated code here...
It is easy to see that the above transformation is very mechanical and can be done by just manipulating a source AST (the AST produced by the slightly modified grammar that accepts this new constructs), and transforming it into the target AST (the AST produced by the standard scala grammar).
Then we can compile the result as usual.
So there you go, with a few simple rules we are able to implement a preprocessor that does all the work to implement this new feature.
If by fundamental you are asking "is there anything that would make this feature out of place" then it can be argued that this does not feel very scala. But more to the point, it does not bring that much to the table. Scala author(s) actually tend toward making the language simpler (as in less built-in features, trying to move some built-in features into libraries) and adding a new syntax that is not really more readable goes against the goal of simplification.
In SML, your code snippet is literally just syntactic sugar (a "derived form" in the terminology of the language spec) for
val rec reverse = fn x =>
case x of [] => []
| x::xs = reverse xs # [x]
which is very close to the Scala code you show. So, no there is no "fundamental" reason that Scala couldn't provide the same kind of syntax. The main problem is Scala's need for more type annotations, which makes this shorthand syntax far less attractive in general, and probably not worth the while.
Note also that the specific syntax you suggest would not fly well, because there is no way to distinguish one case-by-case function definition from two overloaded functions syntactically. You probably would need some alternative syntax, similar to SML using "|".
I don't know SML or Erlang, but I know Haskell. It is a language without method overloading. Method overloading combined with such pattern matching could lead to ambiguities. Imagine following code:
def f(x: String) = "String "+x
def f(x: List[_]) = "List "+x
What should it mean? It can mean method overloading, i.e. the method is determined in compile time. It can also mean pattern matching. There would be just a f(x: AnyRef) method that would do the matching.
Scala also has named parameters, which would be probably also broken.
I don't think that Scala is able to offer more simple syntax than you have shown in general. A simpler syntax may IMHO work in some special cases only.
There are at least two problems:
[ and ] are reserved characters because they are used for type arguments. The compiler allows spaces around them, so that would not be an option.
The other problem is that = returns Unit. So the expression after the | would not return any result
The closest I could come up with is this (note that is very specialized towards your example):
// Define a class to hold the values left and right of the | sign
class |[T, S](val left: T, val right: PartialFunction[T, T])
// Create a class that contains the | operator
class OrAssoc[T](left: T) {
def |(right: PartialFunction[T, T]): T | T = new |(left, right)
}
// Add the | to any potential target
implicit def anyToOrAssoc[S](left: S): OrAssoc[S] = new OrAssoc(left)
object fun {
// Use the magic of the update method
def update[T, S](choice: T | S): T => T = { arg =>
if (choice.right.isDefinedAt(arg)) choice.right(arg)
else choice.left
}
}
// Use the above construction to define a new method
val reverse: List[Int] => List[Int] =
fun() = List.empty[Int] | {
case x :: xs => reverse(xs) ++ List(x)
}
// Call the method
reverse(List(3, 2, 1))
I've written a rather large program in Scala 2.7.5, and now I'm looking forward to version 2.8. But I'm curious about how this big leap in the evolution of Scala will affect me.
What will be the biggest differences between these two versions of Scala? And perhaps most importantly:
Will I need to rewrite anything?
Do I want to rewrite anything just to take advantage of some cool new feature?
What exactly are the new features of Scala 2.8 in general?
Taking the Leap
When you migrate, the compiler can provide you with some safety nets.
Compile your old code against 2.7.7
with -deprecation, and follow the
recommendations from all deprecation
warnings.
Update your code to use
unnnested packages. This can be done
mechanically by repeatedly running
this regular expression search
replace.
s/^(package com.example.project.*)\.(\w+)/$1\npackage $2/g
Compile with 2.8.0 compiler, using paranoid command line options -deprecation -Xmigration -Xcheckinit -Xstrict-warnings -Xwarninit
If you receive errors the error could not find implicit value for evidence parameter of type scala.reflect.ClassManifest[T], you need to add an implicit parameter (or equivalently, a context bound), on a type parameter.
Before:
scala> def listToArray[T](ls: List[T]): Array[T] = ls.toArray
<console>:5: error: could not find implicit value for evidence parameter of type scala.reflect.ClassManifest[T]
def listToArray[T](ls: List[T]): Array[T] = ls.toArray ^
After:
scala> def listToArray[T: Manifest](ls: List[T]): Array[T] = ls.toArray
listToArray: [T](ls: List[T])(implicit evidence$1: Manifest[T])Array[T]
scala> def listToArray[T](ls: List[T])(implicit m: Manifest[T]): Array[T] = ls.toArray
listToArray: [T](ls: List[T])(implicit m: Manifest[T])Array[T]
Any method that calls listToArray, and itself takes T as a type parameter, must also accept the Manifest as an implicit parameter. See the Arrays SID for details.
Before too long, you'll encounter an error like this:
scala> collection.Map(1 -> 2): Map[Int, Int]
<console>:6: error: type mismatch;
found : scala.collection.Map[Int,Int]
required: Map[Int,Int]
collection.Map(1 -> 2): Map[Int, Int]
^
You need to understand that the type Map is an alias in Predef for collection.immutable.Map.
object Predef {
type Map[A, B] = collection.immutable.Map[A, B]
val Map = collection.immutable.Map
}
There are three types named Map -- a read-only interface: collection.Map, an immutable implementation: collection.immutable.Map, and a mutable implementation: collection.mutable.Map. Furthermore, the library defines the behaviour in a parallel set of traits MapLike, but this is really an implementation detail.
Reaping the Benefits
Replace some method overloading with named and default parameters.
Use the generated copy method of case classes.
scala> case class Foo(a: Int, b: String)
defined class Foo
scala> Foo(1, "a").copy(b = "b")
res1: Foo = Foo(1,b)
Generalize your method signatures from List to Seq or Iterable or Traversable. Because collection classes are in a clean hierarchy, can you accept a more general type.
Integrate with Java libraries using Annotations. You can now specify nested annotations, and have fine-grained control over whether annotations are targeted to fields or methods. This helps to use Spring or JPA with Scala code.
There are many other new features that can be safely ignored as you start migrating, for example #specialized and Continuations.
You can find here a preview of new feature in Scala2.8 (April 2009), completed with recent this article (June 2009)
Named and Default Arguments
Nested Annotations
Package Objects
#specialized
Improved Collections (some rewrite might be needed here)
REPL will have command completion (more on that and other tricks in this article)
New Control Abstractions (continuation or break)
Enhancements (Swing wrapper, performances, ...)
"Rewriting code" is not an obligation (except for using some of the improved Collections), but some features like continuation (Wikipedia: an abstract representation of the control state, or the "rest of computation" or "rest of code to be executed") can give you some new ideas. A good introduction is found here, written by Daniel (who has also posted a much more detailed and specific answer in this thread).
Note: Scala on Netbeans seems to work with some 2.8 nightly-build (vs. the official page for 2.7.x)
VonC's answer is hard to improve on, so I won't even try to. I'll cover some other stuff not mentioned by him.
First, some deprecated stuff will go. If you have deprecation warnings in your code, it's likely it won't compile anymore.
Next, Scala's library is being expanded. Mostly, common little patterns such as catching exceptions into Either or Option, or converting an AnyRef into an Option with null mapped into None. These things can mostly pass unnoticed, but I'm getting tired of posting something on the blog and later having someone tell me it's already on Scala 2.8. Well, actually, I'm not getting tired of it, but, rather, and happily, used to it. And I'm not talking here about the Collections, which are getting a major revision.
Now, it would be nice if people posted actual examples of such library improvements as answers. I'd happily upvote all such answers.
REPL is not getting just command-completion. It's getting a lot of stuff, including the ability to examine the AST for an object, or the ability to insert break points into code that fall into REPL.
Also, Scala's compiler is being modified to be able to provide fast partial compilation to IDEs, which means we can expect them to become much more "knowledgable" about Scala -- by querying the Scala compiler itself about the code.
One big change is likely to pass unnoticed by many, though it will decrease problems for library writers and users alike. Right now, if you write the following:
package com.mystuff.java.wrappers
import java.net._
You are importing not Java's net library, but com.mystuff.java's net library, as com, com.mystuff, com.mystuff.java and com.mystuff.java.wrappers all got within scope, and java can be found inside com.mystuff. With Scala 2.8, only wrappers gets scoped. Since, sometimes, you want some of the rest to be in Scope, an alternative package syntax is now allowed:
package com.mystuff.factories
package ligthbulbs
which is equivalent to:
package com.mystuff.factories {
package lightbulbs {
...
}
}
And happens to get both factories and lightbulbs into scope.
Will I need to rewrite anything?
def takesArray(arr: Array[AnyRef]) {…}
def usesVarArgs(obs: AnyRef*) {
takesArray(obs)
}
needs to become
def usesVarArgs(obs: AnyRef*) {
takesArray(obs.toArray)
}
I had to visit the IRC channel for that one, but then realized I should have started here.
Here's a checklist from Eric Willigers, who has been using Scala since 2.2. Some of this stuff will seem dated to more recent users.
* Explicitly import from outer packages *
Suppose we have
package a
class B
Change
package a.c
class D extends B
to
package a.c
import a.B
class D extends B
or
package a
package c
class D extends B
* Use fully qualified package name when importing from outer package *
Suppose we have
package a.b
object O { val x = 1 }
Change
package a.b.c
import b.O.x
to
package a.b.c
import a.b.O.x
* When explicitly specifying type parameters in container method calls, add new type parameters *
Change
list.map[Int](f)
to
list.map[Int, List[Int]](f)
Change
map.transform[Value](g)
to
map.transform[Value, Map[Key, Value]](g)
* Create sorted map using Ordering instead of conversion to Ordered *
[scalac] found : (String) => Ordered[String]
[scalac] required: Ordering[String]
[scalac] TreeMap[String, Any](map.toList: _*)(stringToCaseInsensitiveOrdered _)
* Import the implicit conversions that replace scala.collection.jcl *
* Immutable Map .update becomes .updated *
*** Migrate from newly deprecated List methods --
* elements
* remove
* sort
* List.flatten(someList)
* List.fromString(someList, sep)
* List.make
*** Use List methods
* diff
* iterator
* filterNot
* sortWith
* someList.flatten
* someList.split(sep)
* List.fill
* classpath when using scala.tools.nsc.Settings *
http://thread.gmane.org/gmane.comp.lang.scala/18245/focus=18247
settings.classpath.value = System.getProperty("java.class.path")
* Avoid error: _ must follow method; cannot follow (Any) => Boolean *
Replace
list.filter(that.f _)
with
list.filter(that f _)
or
list.filter(that.f(_))
>
>
>
* Migrate from deprecated Enumeration methods iterator map *
Use Enumeration methods values.iterator values.map
* Migrate from deprecated Iterator.fromValues(a, b, c, d) *
Use Iterator(a, b, c, d)
* Avoid deprecated type Collection *
Use Iterable instead
* Change initialisation order *
Suppose we have
trait T {
val v
val w = v + v
}
Replace
class C extends T {
val v = "v"
}
with
class C extends {
val v = "v"
} with T
* Avoid unneeded val in for (val x <- ...) *
* Avoid trailing commas *