Вопрос

The question first: Is the arrow anti pattern the way to do things in Scala?

I've been transitioning from Java to Scala for about 3 months now. I'm starting to see that the anti arrow pattern as a bit of a standard in Scala.

For example, in Java, I like to return from methods as soon as possible. We don't like a lot of inner nestings. If you have lots of them it's called the arrow anti pattern. You can read about it here: http://c2.com/cgi/wiki?ArrowAntiPattern

Java example:

boolean isValid(String input){
  if (input.trim().length() <1) return false //return early
  if (input.substring(0,3).equals("sth")) return false;

  //do some more checks for validity then return true

  return true
}

If I wrote this in Scala I'd have to use match-case statements which results in a lot of indenting. Keep in mind that this is just a dummy example where I try to convey only the nesting and indentation of Scala.

def isValid(input:String):  Boolean = {
  (input.trim.length < 1) match {
    case true => 
      input.substring(0,3) match {
        case sthCase if(sthCase=="sth") =>
          //do some more checks etc

          //eventually arrive at the "true" branch
               ... case valid => true

        case sthElse if (sthCase=="bla") => //some more code etc
        case _ => false
      }
    case _ => false
  }
}

I know this example could have been written the same way I wrote the Java one, but if instead of returning true or false I wanted to assign that value to a variable ie:val valid = (input.trim.length < 1) match ... then this is the only way to do it since you can't reassign to val. I could use var instead of val, but then I'd be using Java instead of Scala.

Question, re-iterated: So the question is, is the arrow anti pattern the way to do things in Scala or am I missing something? Is there a better way to do in Scala what I just wrote (keeping in mind that I want that match statement result to be assigned to a val variable).

Это было полезно?

Решение

Note that you can always use decomposition in Scala as long as all parts of the code are single entry/single exit. This works because nested functions in Scala can access the local variables of the outer function. E.g.:

def isValid(input: String): Boolean = {
  def testLength = input.trim.length >= 1
  def testSomething = input.substring(0, 3) == "sth"

  return testLength && testSomething
}

You can thus break down any arrow-like structure into its subcomponents as much as you desire.

Note also that using match for testing booleans or comparing something for equality is overkill.

Другие советы

Your java example can easily be translated to an if/else in scala without explicit return and without any additional levels of nesting.

def isValid(input: String) = {
  if (input.trim().length() <1) false
  else if (input.substring(0,3).equals("sth")) false
  else true
}

if/else like everything in scala is an expression and has a return value, so you might as well use it for an assignment:

val isValid =
  if (input.trim().length() <1) false
  else if (input.substring(0,3).equals("sth")) false
  else true

Or in this case you could also simply write

val isValid = (input.trim().length() >= 1) && (!input.substring(0,3).equals("sth"))

If there are too many returns in a function, functions are missing:

def isValid(s: String): Boolean = {
  def isLengthValid = s.trim().length() > 0
  def shouldNotContainsSomething = s.substring(0, 3) != "sth"

  isLengthValid && shouldNotContainsSomething
}

Don't do the cargo cult just because some rule says so. The good thing in Scala is that you can do the best from both worlds -- e.g. sometimes algoritm written in a mutable style is much more clear than the immutable one.

By the way, there is also require method, which is used commonly for fail on validation (but it goes up with an exception).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top