2
\$\begingroup\$

I am learning Scala and FP (Functional Programming), coming from Java, OO and a strong imperative pardigm. I am now trying to implement a small puzzle solving project so I can get some deeper hands on experience with both Scala and FP.

So, I have the following Scala class:

class Bitmap2d(rowsByColumns: List[List[Int]]) {
  if (!validateRectangular) {
    throw new IllegalArgumentException("all rows must have same length")
  }

  def validateRectangular: Boolean = {
    rowsByColumns.size == rowsByColumns.filter(_.size / rowsByColumns.first.size != 1)
  }
}

I am attempting to validate the single class constructor parameter. The requirements of a properly constructed instance of this class are:

  1. The list is not null or empty.
  2. The list contains at least one list which is itself not null or empty.
  3. If the outer list contains more than a single inner list, validate all the inner lists are of identical length.

I was quite proud of figuring out how to implement validateRectangular in the FP style of immutability (my first try was the traditional Java imperative approach). However, upon further analysis, I am now not happy with it as it visits every row (as opposed to stopping the evaluation at the first row that fails to have the correct size).

So, here are my questions:

  1. In Java, the null and non-empty checks are obvious. Do I need to perform these checks in Scala (where null seems to be quite abhored)? If so, what would be considered the idiomatically correct way to do so. If not, why must I check for it in Java, but not in Scala?
  2. While my validateRectangular method works fine, I would prefer a fail fast test which stopped iterating as soon as a row had a size different than the first row (the first row's size is considered the baseline to which all the remaining rows must conform). So, what would be the ideal FP way to approach re-writing validateRectangular such that it properly validates all rows are the same size, but fails fast (stops iterating) as soon as the current row is of a different size?
  3. What is the correct way to reject constructing an instance of a Scala class if the parameters are not correctly defined? In Java, I use the standard style of throwing an exception in the constructor after a parameter fails to validate. That's what I have implemented above. Is that considered idiomatic Scala?

UPDATE:

Here's the same class modified per Daniel C Sobral's suggestions:

class Bitmap2d(rowsByColumns: List[List[Int]]) {
  require(validateRectangular, "all rows must have same length")

  def validateRectangular: Boolean = {
    rowsByColumns.forall(_.size == rowsByColumns.head.size)
  }
}
\$\endgroup\$
1
\$\begingroup\$
  1. In Scala, never use null unless some API requires it of you; use Option instead. If you have to interface with an API that returns null, convert the result into an Option. As long as you do that, you can ignore null checks, for they'll always be errors: you got a null from some code that shouldn't be producing it, or you forgot to convert some return value to Option, or else you didn't even know you had to.

  2. Use exists or forall -- forall seems better suited, but either will do with the proper conditionals. Also, do not use first -- use head. Yeah, it looks weird, but head/tail is idiomatic, and faster.

  3. There's a require function that is used for parameter validation. Either require(validateRectangular) or require(validateRectangular, "all rows must have same length") will do.

And, yes, validateRectangular was a good start, and for new comers to FP it is not obvious to implement something that will stop at the first incorrect size in functional style -- at least on a strict language like Scala. You'd either throw an exception or use recursion.

\$\endgroup\$
  • 1
    \$\begingroup\$ Tyvm for your response. I have updated the original question with a modified version of the class per your suggestions. Could you verify I have correctly represented the changes you outlined? \$\endgroup\$ – chaotic3quilibrium Mar 26 '12 at 3:07
  • \$\begingroup\$ @chaotic3quilibrium Yes, it does. However, I note now that the test seems to be in error. For example, 3 / 2 == 1, but you don't want size 3 and 2 on the same list, do you? Perhaps you should write _.size == rowsByColumns.head.size instead? \$\endgroup\$ – Daniel C. Sobral Mar 26 '12 at 15:31
  • \$\begingroup\$ Good catch. I've corrected it. Tyvm! \$\endgroup\$ – chaotic3quilibrium Mar 26 '12 at 17:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.