Dean Wampler
(as of EOM works at training company)
Functional support
OO improvement
Statically typed (unlike a lot of new languages)
Author: Martin Odersky; combines academia and real-world well
When you see departures from java syntax, there's always a good reason -- e.g., 'var: type = value'
Type inference
Why do we need to declare types twice? (e.g., new HashMap<...>); Google collections does this now
val persons: Map[String,Person] = new HashMap
Another way:
val persons2 = new HashMap[String,Person]
Initializing with literal, no 'type annotation' required:
val name = "Ella Mae"
var count = 0
Succinct type declarations:
class Person(
var firstName : String,
var lastName : String,
var age : Int
)
Class body has curlies after the declaration
NOTE: field names and method names share the same namespace
What this means, expanded:
class Person( fn : String... ) {
// field
private var fName : String = fn;
// method
// no '()' after 'firstName'
// no explicit 'return'
// the '=' means you're returning data; optional
def firstName : String = {
fName
}
// _= allows user to do 'person.firstName = "foo"'
// 'Unit' like 'void'
def firstName_=( fn : String ) : Unit = {
fName = fn
}
}
Methods with one expression don't need '{...}'
class Person( fn : String... ) {
private var fName = fn;
def firstName = fname
def firstName_=( fn : String ) = fName = fn
Why not use javabean syntax?
If you DO need this (like Spring), you can use annotation:
class Person( fn : String... ) { @scala.reflect.BeanProperty var firstName...
Tuples: Tuple1 ... Tuple22 declared in Scala:
class MyMap[A,B] {
def firstPair : Tuple2[A,B]...
}
val pair = ( 3, "Ella Mae" )
println( "Age is: " + pair._1 + " for " + pair._2 );
// 'println' imported automatically, through a 'predef'
// object (more later)
You can do this w multi-valued assign...
val pair = ( 3, "Ella Mae" )
val ( age, name ) = pair
Using 'val' in the argument list for the class means it's a variable you pass in that you'll use to compute your fields (or whatever).
Main rules for when types are required:
You have secondary constructors as well as primary ones
def this ( fName : String, lName : String ) =
this( fName, lName, 0 ) // default age to 0
Inheritance
class Employee( fName : String, lName, String,
age : Int, val job : Job )
extends Person( fName, lName, age )
{ ... }
User defined operators
class Complex( val real : Double, val imag : Double ) {
def +( that : Complex ) =
new Complex( real + that.real, imag + that.imag )
def -( that : Complex ) =
new Complex( real - that.real, imag - that.imag )
// declare a '-' operator that
def unary_- = new Complex( -real, imag )
// 'override' is REQUIRED
override def toString() = "(" + real + "," + imag + ")";
}
Can use with '+='...
val sum += new Complex(...')
Packages and imports
'' is like '*' in Java (e.g., 'import java.io.'); can alias others:
import java.io.Reader => JReader
import java.io.{ File, Reader => JReader }
Can have multiple package declarations in a file; file and type names do not have to match. (It's still convenient, but not necessary.) Also, directory and package names don't have to match.
Type hierarchy
Other built-ins:
More on types:
+A covariant
new List[String] isa subclass of new List[AnyRef] * This is dealing with the list type *itself*, not the crap in it * Think of it as lists following the stuff in it def printClass( l : List[AnyRef] ) = { l.forEach( x => println( x.getClass ) ); } printClass( "a" :: "b" :: Nil )
'::' called 'cons' => "b" prepends
-A contravariant
trait Function2[-A1,-A2,+R] so: Function2[AnyRef,AnyRef,String] is a subclass of Function2[String,String,AnyRef] -- going in wrong direction!
Function literal syntax:
( AnyRef, AnyRef ) => String
...put off decisions about type safety to runtime?
...contravariance constraining what a function can accept
def mapper[T]( l : List[T], f: (T) => Any ) = { l.map( f(_) ); } mapper( "a" :: "b" :: Nil, (x:String) => x.toUpperCase );
Implicit conversions:
val months = Map( "Jan" -> 1, "Feb" -> 2 ... );
// -- 'implicit' tells the parser it can use this for type // conversion // -- name of method irrelevant implicit def any2ArrowAssoc[A] ( x: A ) : ArrowAssoc[A] = new ArrowAssoc(x) ... class ArrowAssoc[A]( val x: A ) { def -> [B](y:B) : Tuple2[A,B] = new Tuple2(x,y); }
trait Queue[T] { def get() : T def put( t: T ) }
trait QueueWithLogging[T] extends Queue[T] { abstract override def put( t:T ) = { println( "put(" + t + ")" ); super.put(t); } } class StandardLoggingQueue[T] extends Queue[T] with QueueWithLogging[T] { ... }
var sq = new StanardQueue[Int] with QueueLogging[Int];
var sq = new StanardQueue[Int] with QueueLogging[Int] with QueueFiltering[Int] { def veto( t: Int ) = t < 0 };
Multiple traits can be 'with'd, precendence is right-to-left ("loosely speaking", he says; spec is apparently much more complicated)
Method lookup:
Traits cannot have constructors with arguments
Functions:
trait Function1[-A,+R] { }
Companion objects:
class Complex...
// must be in same file (compiler-enforced)
object Complex...
def apply( r : Double, i : Double ) = new Complex( r, i )
The 'apply' method in the 'object' acts as a factory. * DW: typical behavior for him is to have a single constructor in the class, then variants in overridden apply() implementations in the companion
case class Complex( real : Double, imag : Double ) { .... }
Example from Map
object Map {
def apply[A,B] (elems: (A,B)*)...
}
Student: Master, does the case class create a copy constructor? Master: Yes. Student: <executes code with a copy constructor> Master, my code does not work. Master: There is no copy constructor. (the student is englightened)
A -- 1 :: 2 :: 3 :: Nil B -- Nil.::3.:: ...
Controls
Lists/functions:
DSLs
External DSLs
Actors
case s:Shape => ...