Scala is a wonderful language, with a specification of a size comparable with that of Java. Overall, the specification is simpler than C++. Why then do a lot of people feel intimidated by the expresiveness of scala?
Scala can generate complex constructs. There are some features of the language that, if not used properly, can negatively influence one's perception of the simplicity of the language and generate a waste of many an hour of a frustrated developer.
I find this to be especially true of Java developers just picking up scala. Since they do not have a full grasp of all the features of the language and their effects or benefits, they may be very surprised if others on their team use them.
One thing to have when starting or using scala is a readily available bible. I recommend "Programming in Scala", the .pdf version, an easily searcheable language reference.
The scala style guide www.codecommit.com/scala-style-guide.pdf is a must. Here we will describe the advanced features of the language that should not be abused or better, avoided by the regular Joe.
Do not abuse:
- many constructors for the same thing. These come in many kinds, from this() to apply() in a companion class to implicits
- anonymous functions everywhere
- the def f()()()() notation - it's ok only to allow DSL looking constructs
- avoid variance notation
- _ (underscorre)
- do not chain many function calls in the same line.
- operators
- implicits visible outside of a class
- complext type restrictions
- variance notation
- complex higher types
Do not re-define operators or define new ones. For instance:
// this is mean and considered sabotage - good reason for firing developers
override def + (that:String) = this - that
// coming up with operators 'cause you feel like not doing your job on a friday
def --++==--++ = 5
You could use implicits inside a class or object, to simplify the code and learn it. DO NOT make them visible outside the class
The wildchar is used in many circumstances:
- currying: f _
- ignore types: List[_]
- ignore values in pattern matching: case Student (_,a) => ...
- match anything: case _ => ...
- stand-in for parameters in lambda sugar-coated syntax: list map (_ + 2)
When defining higher order types, stay away from complex type restrictions.
// this is typical
val students : List [A <% Student]
// this is ok - although you should use Option rather than null
class NoStatic[T>:Null<:AnyRef] {
var lazyValue = null
}
//this is to be completely avoided
def combo[AA <: A >: B <% String <% Int : M : Ordering] = 0
No more than 2 map()s in the same line. Note that getOrElse is a type of map for this purpose.
//This is borderline comprehensible:
SystemProps.sPatProps.find (_.patcomp.pattern.matcher(name).matches) map (_.value) getOrElse System.getProperty(name)
// not so here - actually here an inline lambda is abused
o._2.asInstanceOf[List[scala.xml.Elem]].filter(zz => XP.stareq(zz.label, tag)).map(x => { val t = (x.asInstanceOf[T], children(x).toList.asInstanceOf[U]); println("t=" + t); t }).toList
//here the name is very useful and excuses the rest of the line
def messagesById(id: String)(f: => List[(NodeSeq, Box[String])]): List[NodeSeq] = f filter (_._2 map (_ equals id) openOr false) map (_._1)
//not here, though
val ret = (Box(connectionManagers.get(name)).flatMap(cm => cm.newSuperConnection(name) or cm.newConnection(name).map(c => new SuperConnection(c, () => cm.releaseConnection(c))))) openOr { ... }
Generally, higher-order types are already provided for you, in the collection libraries. If you need your own Group[Student], then you should keep it simple.
The example may look comprehensible, but using it may cost a lot of time...
trait WRGraph[N <: GNode[N, L], L <: GLink[N]] extends GNode[N, L] { this: N =>
...
}
//this is customary
def using (r:Resource) (f: Resource => Unit) = ...
// this is pointless
def add (i:Int) (j:Int) = i + j
// unless you need to curry it:
add (3) _