Tuesday, December 8, 2015

Naming of identifiers

One of the conventions recommended by the designers of Scala is to use short identifier names. It's fine to make method names long (especially when they are private methods whose meaning may not be entirely clear otherwise) but context usually gives a pretty good clue to identifiers.

You've probably noticed code like this (taken from the Artima web site: Working with Lists):

def isort(xs: List[Int]): List[Int] =
    if (xs.isEmpty) Nil
    else insert(xs.head, isort(xs.tail))

where xs is used for a sequence of things and pronounced x's (the plural of x).

Here, I want to present a recommendation for generalizing this notion. In the same way that a sequence (Seq) of x is written xs, then a Future of x should be written xf. A Try of x might be written xt but I'm going to recommend something slightly different (xy) because t is useful for Tuple. So, the general scheme is:

val ac = C[A]

where C is some container type and A is some element type. Obviously, these can be composed such that if A happens to be a sequence of X (for example) then we end up with:

val xsc = C[Seq[X]]
Note that the order of the container/type definitions is mirrored for the name of the identifier. The advantage of all this is that when a reader (or even yourself) comes to read that code later, it is clear what the type of the identifier is, even if it isn't shown explicitly. If you're not reading the code through an IDE, then having an indication of type will be quite helpful. It also makes debugging a lot easier.

In a for-comprehension, patterns/generators look very simple: for (x <- xs) yield x, for example.

Here are my recommendations for the abbreviations (but of course you should choose your own):


ContainerAbbreviation
Seq/List, etc.s
Arraya
Mapm
Futuref
Tryy
Promisep
Tuplet
Optiono
Iterator/iterablei
Eitherh
RDDr

TypeAbbreviation
My own typex
Generic type A, B, T, etc.a, b, t, etc.
String (line/document)g
String (word)s or w
URLu
Doubled
Intn
Functionf
ActorRefa
Exceptione

Sometimes you might have more than one identifier of the same type in which case you can follow the single letter with 1, 2, etc. Or, alternatively, you might use a single uppercase character to add context, for example gD for a document string and gL for a line string. You might also decide to use t2 for a Tuple2, etc. In the situation where we have an Either of form Either[String,X] or Either[Throwable,X] then we simply use the identifier xe, ignoring the left-side types.

Some confusion might arise between a as an Array (i.e. a container) and a as a generic type A (or ActorRef). This should be clear in that types are found at the start of the identifier string. Similarly, f as a Future and f as a Function. And s if you use it both for a String and a Seq.

Here are some examples of code which uses this idea:

  def flatten[X](xsfs: Seq[Future[Seq[X]]])(implicit c: ExecutionContext): Future[Seq[X]] = Future.sequence(xsfs) map {_ flatten}

  def sequence[X](xy: Try[X]): Either[Throwable,X] =
    xy match {
    case Success(s) => Right(s)
    case Failure(e) => Left(e)
  }

  def sequence[X](xos : Seq[Option[X]]): Option[Seq[X]] = (Option(Seq[X]()) /: xos) {
    (xso, xo) => for (xs <- xso; x <- xo ) yield xs :+ x
  }

  val ws = List("http://www.htmldog.com/examples/","http://www.google.com")

  val uys = for ( w <- ws ) yield Try(new URL(w))

3 comments:

  1. BTW, an alternative for a tuple, say a (K,V) tuple, is to use kV (or _kv_) instead of t. This makes it clearer that it's a tuple of k and v, rather than just any old tuple. If you use that style, then you can use t for Try (rather than y).

    ReplyDelete
    Replies
    1. And yet another possibility: In keeping with the right-to-left identifiers, I'm recommending that a variable for, say, a Map[String,Int] should be named: iSm where S indicates a "perpendicular" direction in type-space. Lower-case letters indicate leftward (outer) direction in type-space.

      Delete
  2. And yet another possibility: name the identifiers based on Tuple (or Map, which is really just a sequence of 2-tuples) using the backtick, such as: val `[wn]m` = Map[String,Int]()

    ReplyDelete