5

I am storing error code and its string message in this way:

object Error {
  def FileNotFound(filename: String) = Error("ERR01", s"${filename} not found")
  def UserNotExist(userName: String) = Error("ERR02", s"${userName} not exist")
}

case class Error(code: String, value: String) {}

Benefit of keeping it like this is that I can pass string value to error message.

And I am creating it like

def validate(data: SomeType): List[Error] = {
    var errors = ListBuffer[Error]()
    if (validation1("abc") == false) {
      errors+= Error.FileNotFound("abc")
    }
    if (validation2("lmn") == false) {
      errors+= Error.UserNotExist("lmn")
    }
    errors.toList
}

I am new to Scala and functional programming. Is it right way to write error code like this? Is it following functional programming paradigm?

Scala: 2.11

11
  • What are you trying to achieve? What do those functions within the object have to do with the case class? I'm not sure what you're trying to do here as the question lacks context. Commented Jan 30, 2019 at 10:10
  • 1
    it seems fine to me. didn't get the question what you are trying to achieve? Commented Jan 30, 2019 at 10:15
  • 1
    @RamanMishra I don't want to create exception, as single validation will be sending multiple validation messages. Creating exceptions means one validation per run. I wanted to list all validation error in one go Commented Jan 30, 2019 at 11:08
  • 1
    : List[Error] { - what, this is supposed to compile, really? And... no, that's not following functional programming style. In functional programming style you would concentrate on solving the problem, and stash all the annoying error accumulation away in some applicative effect. Your validate doesn't seem to produce anything useful, all it returns are errors. Commented Jan 30, 2019 at 11:28
  • 2
    You might find this rather detailed introduction to Validated useful (even though I cannot guarantee that it will be pleasant to use with 2.11). Commented Jan 30, 2019 at 11:37

1 Answer 1

2

Here are my two cents with a full example.

I assume you want to do this in nice-and-easy functional style, so let's remove the mutable ListBuffer and see how to deal with it. The description of the steps are after the code.

UPDATE: I want to note that, in your code, the usage of ListBuffer is acceptable because it doesn't break rerefential transparency. This is because you don't expose its mutable nature outside the function and the output of the function depends only on its inputs.

Nonetheless I usually prefer to avoid using mutable data if not for specific reasons, like compactness or performance.

object StackOverflowAnswer {

  /* Base type with some common fields */
  class MyError( val code: String, val msg: String )

  /* FileNotFound type, subtype of MyError, with its specific fields */
  case class FileNotFound( filename: String ) extends MyError(
    "ERR01", s"$filename not found"
  )

  /* UserDoesntExist type, subtype of MyError, with its specific fields */
  case class UserDoesntExist( userName: String ) extends MyError(
    "ERR01", s"$userName doesn't exist"
  )

  /*
   * Validates the file. If it finds an error it returns a Some(MyError) with
   * the error that's been found
   * */
  def checkForBadFile( data: String ): Option[MyError] =
    if( data.contains("bad_file.txt") )
      Some(FileNotFound("bad_file.txt"))
    else
      None

  /*
   * Validates the user. If it finds an error it returns a Some(MyError) with
   * the error that's been found
   * */
  def checkForMissingUser( data: String ): Option[MyError] =
    if( data.contains("bad_username") )
      Some(UserDoesntExist("bad_username"))
    else
      None

  /*
   * Performs all the validations and returns a list with the errors.
   */
  def validate( data: String ): List[MyError] = {
    val fileCheck = checkForBadFile( data )
    val userCheck = checkForMissingUser( data )
    List( fileCheck, userCheck ).flatten
  }

  /* Run and test! */
  def main( args: Array[String] ): Unit = {
    val goodData = "This is a text"
    val goodDataResult = validate( goodData ).map( _.msg )
    println(s"The checks for '$goodData' returned: $goodDataResult")

    val badFile = "This is a text with bad_file.txt"
    val badFileResult = validate( badFile ).map( _.msg )
    println(s"The checks for '$badFile' returned: $badFileResult")

    val badUser = "This is a text with bad_username"
    val badUserResult = validate( badUser ).map( _.msg )
    println(s"The checks for '$badUser' returned: $badUserResult")

    val badBoth = "This is a text with bad_file.txt and bad_username"
    val badBothResult = validate( badBoth ).map( _.msg )
    println(s"The checks for '$badBoth' returned: $badBothResult")
  }

}

I start defining the type structure for the error, similar to your.

Then I have two functions that performs validations for each check you want. When they find an error, they return it using the Option type of Scala. If you're not familiar with Option, you can have a look at this link or do some googling.

Then I have the validation function that calls and store each the above single checks. The last bit is the use of flatten (doc here) that "flattens" List[Option[MyError]] into List[MyError], removing the middle Option.

Then there is the actual code that shows you some examples.

Sign up to request clarification or add additional context in comments.

3 Comments

Your validate function can just use flatten rather than collect
In Scala 2.13 you will be able to do Option.when(test)(error) which will be nice :)
Nice! Any link to that?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.