Validating parameter

What is the (idiomatic) Scala way to validate input parameters of a function or method?

In Java I would do something like Objects.requireNonNull(Object obj) or checking with StringUtils and manually throwing an Exception.

Is there any idiomatic equivalent in Scala? What’s the way to go?

It depends on how you approach error propagation and handling in general in your application. There’s been some discussion about that - possible approaches include exceptions, Option, Either, Try, Validation, and further considerations will surface once (side) effects and/or async processing are involved. I don’t think there’s a clear-cut consensus regarding the “proper Scala way”, other than that using raw exceptions probably isn’t it.

As for null, in a Scala project I’d only expect this when interfacing with Java code - there I’d wrap it with Option#apply() and handle the None case as a failure. With StringUtils, I’d assume you’re validating user input, so Either or Validation might be appropriate.

require()

I would not recommend doing this.

The idiomatic way in Scala is to represent the possibility of an error in the types rather than throwing an exception. If you define a function to take String as an input then it receives an “invalid” String value then this is not at all exceptional, quite the opposite, it is expected. So rather than throwing an exception, you would wrap the return type in a type that can also carry along additional information. Option , Either , Try , Validation which Patrick mentioned are some good choices.

Rather than having validation everywhere I like to do this as close to the edges as possible, such as when receiving user input, reading from a file, or handling a web request. Then rather than passing a raw String around and just hoping that it has been validated, I would wrap that String inside another value which can only be obtained after validation. Such as:

class NonEmptyString private(val value: String) extends AnyVal {}

object NonEmptyString {

  def apply(value: String): Either[String, NonEmptyString] =
    Either.cond(value.nonEmpty, new NonEmptyString(value), "value was empty")
}

Rather than write this myself, I like to use refinement types. It takes a bit of getting used to but you can eliminate entire classes of bugs and be very precise on

If it is just validating against null then I wouldn’t do that. No one should be passing null to your function, if they do then they deserve a NullPointerException to be thrown. As Patrick said, use Option#apply() to wrap any potential null values that could be returned from a Java API. In Scala enable Wartremover, Scalastyle and/or Scalafix to ensure there are no nulls in your Scala code.

1 Like

Also on the point of null I just saw that Dotty 0.21.0-RC1 now has explicit nulls and flow typing :partying_face:

In your example you have value.nonEmpty where value is of type String. Am I right that this is just for the sake of a short example? Because this will still throw a NPE if value is null.

What is that Validation class? Is it part of Scala or an external library?

Thank you for your answers :slight_smile:

The idea is that null is only a thing in Scala because it’s baked into the JVM, and for Java interop. You should never introduce null yourself (that’s what Option is for) and always guard against null immediately at the interface to Java libs. If the input to NonEmptyString#apply() comes from a Java API, it should be guarded against null separately.

There’s multiple implementations of the concept in 3rd party libs, but none in the Scala standard lib.

I certainly support using good types and using Either, Try, Option where they make sense. Somewhere, some code still needs to validate the input if the basic types of the language are not tight enough. e.g. consider an API that requires its input to be between 1 and 5. You can put the check in the API call or in the construction of an input.

option 1 -

def api(input: Double) = {

require(input > 1 && input < 5)

}

option 2 -

case class Input(input: Double) {

require(input > 1 && input < 5)

}

def api(input: Input) = {

}

I would choose between 1 and 2 based on usual concerns like DRY. Use 1 when there is only one api() that needs this. Use 2, when there are a lot (to avoid duplicating the require() statement)

Mohit.

P.S.: The above examples apply to Option,Try etc also, if the code cannot handle None, Failure etc, it will have to throw an exception and fail. require() is the typical way to handle that.

I would generally discourage use of require() like this. require() is more part of the assertions than anything else, and idiomatically is usually used to enforce runtime contracts between functions. require() is typically used to say “it is a code error for this parameter check to ever be false” – I generally wouldn’t use it on user input, where any old nonsense might be entered.

More generally, I would say that the most typical Scala idiom is to reserve Exception for code errors, and to use things like Option, Either, and Validated (and maybe Try) for “expected” errors that might arise due to inputs. None of this is hard-and-fast, of course, but it’s increasingly the way I see code written, and IMO it’s good style.

This applies to the other approaches just the same. E.g.

Option 1:

def playRound(diceThrow: Int): Either[Exception, RoundResult] = {
  def playValidated(): RoundResult = ???
  Either.cond(
    (1 to 6).contains(diceThrow),
    playValidated(),
    new IllegalArgumentException(s"not a valid dice throw: $diceThrow")
  )
}

Option 2:

import TaggedTypes._

sealed trait DiceThrowTag
type DiceThrow = Int @@ DiceThrowTag
object DiceThrow {
  lazy val tagger: Tagger[DiceThrowTag] = tag[DiceThrowTag]
  def apply[T](i: Int): Either[IllegalArgumentException, DiceThrow] = {
    Either.cond(
      (1 to 6).contains(i),
      tagger[Int](i),
      new IllegalArgumentException(s"not a valid dice throw: $i")
    )
  }
}

def playRound(diceThrow: DiceThrow): RoundResult = ???

Can you elaborate - how would the code not be able to “handle” failure types? I’d think this only is an issue when implementing a 3rd party interface or similar.

I don’t think it’s that typical. First, my impression from discussion threads like the one I linked above was that throwing exceptions is not generally considered “the Scala Way”. (Even when advocating the use of exceptions internally and only converting them at module boundaries only - we’re talking specifically about input validation here, and this by definition should usually happen at module boundaries.) Second, even if throwing exceptions were considered good practice, #require() doesn’t yield much useful information about the actual cause of the problem - as a user, I’d prefer “not a valid dice throw: 12” over “requirement failed”.