user811602
user811602

Reputation: 1354

Writing error code and its string message: Scala

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

Upvotes: 5

Views: 1251

Answers (1)

ColOfAbRiX
ColOfAbRiX

Reputation: 1059

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.

Upvotes: 2

Related Questions