Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Scala abuses

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 some feel intimidated by its expressiveness?

Scala can generate complex constructs. There are some features of the language that, if not used properly, can negatively influence one's perception of its simplicity and generate a waste of many an hour of a frustrated developer.

Newcomers from simply typed, object-oriented, structured languages do not have a full grasp of all the features of scala and their effects or benefits, and 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 is very good. Here we will describe the advanced features of the language, which are aimed at library-developers and should not be abused.

Language constructs that Joe should not abuse

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
  • _ (underscore)
  • do not chain many function calls in the same line.
  • operators
  • implicits visible outside of a class
  • complex type restrictions
  • complex higher types

Dont (re)define operators

Avoid re-defining operators or defining new ones. For instance:

// this is mean
override def + (that:String) = this - that

// coming up with funny operators
def --++==--++ = 5

Basically, don't define operators unless they make a lot of sense as applied on the data type at hand and don't come up with new ones, especially if you're not a DSL designer...

Implicits visible outside of a class

You could use implicits inside a class or object, to simplify the code and learn. DO NOT make them visible outside the class, unless they're used for pimping.

Do not abuse _

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)

Avoid using many of them on the same line or small scope.

Complex type restrictions

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

Do not chain many function calls in the same line.

Once you pickup the functional style, composing functions becomes the norm...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 { ... }

Complex higher types

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 =>
...
}

The def f()()()() notation

It's great only for DSLs but may confuse one to have to call add (1) (2) when all day long I use sum (1,2)...

//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) _

Process - wise

Pair reviews are an excellent means to create good quality code while allowing all team members to help each-other evolve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.