Databricks Scala Style Guide
github.com
github.com
def getAddress(name: String): Option[String] = {
database.get(name).flatMap { elem =>
elem.data.get("address")
.flatMap(Option.apply) // handle null value
}
}
could just be def getAddress(name: String): Option[String] = {
for {
elem <- database.get(name)
address <- elem.data.get("address")
} yield address
}
and it's way more readable than their given rewrite.Then again, for-yield might be too magical and "can confuse programmers less familiar with Scala," which seems to be a reason for a lot of the stylistic decisions.
Part of the reasons people can learn new langagues "easily" is that there are universal concepts. When you don't change words but change the meaning then that creates unnecessary difficulty.
In my opinion if you want to introduce a new concept, also introduce a new vocabulary to address it.
We should strive for programming to be easier and more user friendly, and things like this do the opposite. Especially for people like me who switch between multiple languages in a day, it can be mentally taxing to make that context switch.
I've never understood ppl on HN defending things being optionally harder. Such is the CS mindset.
OTOH what's done is done and I don't think one needs to eschew an idiomatic concept just because the people who designed it suck at naming.
Another example is 'case class', because of its heritage I have colleagues who insist it should only be used for pattern matching purposes. I strongly suspect that if the designers were to do things over again that what we know as 'case class' would just be called 'class'
I use them all the time because of all the convienence associated with them. I'm not really aware of the downsides except for the ~22 parameter limit (I hit it when deserializing JSON that represents many possible Option(al) keys... And named parameters are awesome).
I like for/yield because it reminds me of do-notation in Haskell, and it's used for a similar purpose.
I feel like it's one of those things that seems weird the first time you see it but idioms are idioms.
The explicit use of flatMap(Option.apply) in the first example is to deal with the case of address existing, but being a null string. Your code doesn't handle that.
def getAddress(name: String): Option[String] = {
for {
elem <- database.get(name)
nullableAddress <- elem.data.get("address")
address <- Option(nullableAddress)
} yield address
}
def getAddress(name: String): Option[String] = {
for {
elem <- database.get(name)
address <- elem.data.get("address").flatMap(Option.apply)
} yield address
}
are both still better though.Also it seems like a bit of a strawman to use such a pathological example (null values stored in a map are frowned upon even in Java-land) to motivate the entire styleguide section about monadic chaining. They say it's contrived but still -.-
Is there a good theoretical (not technical) reason why we can't mix types in a for-yield structure? There is an implicit conversion from Option to Seq added, so I can mix lists and options, but is there a fundamental issue preventing mixing other types? e.g. in the basic math of it?
So for { a <- someOption b <- someOtherOPtion } yield a + b //returns Option[Int]
ends up being someOption.flatMap(a => someOtherOption.map(b => a+b) )
and remember, all flatMap does is 'map', (so you'd get Option[Option[Int]), then "flatten" it so you end up with just Option[Int]. So if you have 4 'nestings', you only get one level of Option and not 4.
The signature for all flatMaps is ...
class Something[A] { def flatMap[B](f: A => Something[B]): Something[B] }
So, you could have that for Option, for Future, for NonEmptyList, Stream, etc.
But they can't mix and match cause you don't necessarily know how to 'flatten' a List into a future. List[Future[A]] is something very different than say just a List[A]. (One is a concurrent computation).
So, yes, you can do implicit conversions to alleviate this, or you can do what is known as a 'monad transformer' which sounds SUPER scary but I go over them in some slides and they're really easy to use.
https://speakerdeck.com/vmarquez/using-scala-futures-the-fun...
If you want to get into the nitty gritty of how flatMap works and how to mix up futures/options.
We will include more guidelines on this in future iterations of the guide.
I agree with several of their recommendations, but I strongly disagree with their general philosophy of "[feature] can confuse programmers less familiar with Scala". It's one thing for [feature] to be genuinely confusing, and an entirely different thing for it to be "confusing to novices / Java programmers". In the latter case, the right course of action is to educate them; otherwise we'd be stuck programming in Java++.
I like it how they prefer JAVA_STYLE_CONSTANTS over ScalaStyleConstants and same thing for annotations.
The instruction on Implicits is really making sense. use it if you build a DSL or use it internally, keep the principle of least astonishment.
Great style guide. I'm adopting it. It's strict on one way, but let's you escape if you have a reason.
The only caveat is what others have mentioned regarding monadic chaining. I prefer to use for-yield when it's clear that I'm working with collection transformations, nested futures, and even nested options. only when it's too cumbersome to read I break it down to the desugared form.
From the official style guide:
> Symbolic Method Names: Avoid!
http://docs.scala-lang.org/style/naming-conventions.html#sym...
If such a thing existed some portion of this long list of sensible conventions wouldn't have to be memorized and/or constantly referenced and manually enforced.
Such a tool would empower scala programmers to focus more on that that which is most important- the functionality and logic, rather than manually applied aesthetics.
I wish organisations would provide IntelliJ configuration files (or similar) when releasing such style guides as well... ;)
try foo catch {...}
is readable and consistent; the idea that {} just makes a block is really useful, making try/catch, if/then and even method definitions a lot less "magic", a lot less cluttered by ceremony.Infix method calls can likewise read much more clearly:
string contains "foo"
Try is bad, but \/ makes a better alternative to throwing an exception for exactly the same reasons that Option is better than returning null.