Tuesday, 10 July 2018

Ceremony Δ

There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.

The Zen of Python

...Scala's syntax is a bit more flexible than many people are used to..
underscore.io blog

When programming language gets the new features - it has to pay some price in complexity - but extra in ceremonies aspect. I call this Ceremony Delta, the learning curve for the language has some dependency on it - because of developer should study the implicit conventions.

Dynamically growing ecosystem isn't able to follow the bottom row closely for a long distance. Naturally it tends to jump away. It takes a lot of power to keep it on "The shortest way is the right one". Of course the language authors are allowed to review concepts and release the "brand new" language version.

Scala as a language with FP and OOP paradigms support was always hard to keep balance.
Unfortunately it's bring the requirement to invest into best practices.

It already has a big scope of implicit ceremonies that is growing.

Sometimes compiler helps us to find the problems:

eitherResult match { // [warn] match may not be exhaustive.  
    case Right(value) => assert(value == expected)

The example with matching only some part actually is the shortest one - especially with some context that it's a part of Unit test - and having failures for Left cases  is acceptable

This code still compiles but doesn't make sense:

val intList = List(1, 2)
intList.filter(_ == "4") 
// comparing values of types Int and String using `==' will always yield false

Closure is a shortest way to filter the list. As a solution there are different libraries implement === method that is type safe.

Here are example of "smell code" that is easy to detect for experienced developers - but compiler stays silent

// Declaring public variable that is expected to be injected // that is why it's initialized by null
@Inject var service: Service = _

Some(null) // ridiculous declaration 

Future.failed[T](new Exception) foreach doSideEffects // using foreach for side effects - but it's ignoring errors

Thread.sleep(1000) // Let the word wait for 1 second

val result = for {
  part1 <-callService1  // Future[T]  
  part2 <- callService2 // Future[T]
} yield (part1, part2)  // part2 starts execution after part1 has been completed// Tuple is used to combine the result

Using null from JVM sometimes is the shortest way, making side effects on Future in foreach callback is the easiest approach - but not the right one.

Sometimes it's not that easy to find the problem even for experienced developers and it's definitely of of the compiler's responsibility:
// despite method returns a Future - it hides the calling // of blocking code
def asyncCallService(params: Parameter): Future[T] = {
  val extraParam = callSomeBlockingCode() // usingBlockingApi
  callService3(extraParam, params) // returns Future[T] 

To reduce the Ceremony Δ there are many domain specific libraries addressing boilerplate places. The most of the examples for the bad usages of scala.concurrent.Future are solved in ScalaZ Task, cats-effect, Monix etc libs, that still is increasing the learning curve for language but keeps Ceremony delta low. The example with making side-effects in map-flatmap can't be solved with libraries and probably requires language support. It leaves as best practices recommendation on use a pure functions in map/flatmap in cats-effect library - that is ceremony we have to follow.

No comments:

Post a Comment