jpaMM
jpaMM

Reputation: 1

Smart constructors on data class unsafe when using copy: how to avoid that?

In the doc about Validation, when speaking of Smart constructor, it's suggested to use them on data class, but making this possible:

object EmptyAuthorName

data class Author private constructor(val name: String) {
    companion object {
        operator fun invoke(name: String): Either<EmptyAuthorName, Author> = either {
            ensure(name.isNotEmpty()) { EmptyAuthorName }
            Author(name)
        }
    }
}

Author("fo")
    .map { it.copy(name = "") }
    .run { println(this) } // Either.Right(Author(name=))
 

The EmptyAuthorName constraint wasn't respected. Really bad isn't it?

Redefining copy for the data class isn't possible...

Currently my only way out is to not use data class.

Am i missing something?

Upvotes: 0

Views: 434

Answers (1)

user8681
user8681

Reputation:

The primary constructor of a data class is always available via the copy method. You either have to make sure that the result of the primary constructor is valid, or you must not use a data class.

In this case, the validation logic seems to be in a wrapper around the data class, not in the data class itself. That means you can do the validation on an instance of Author, instead of "faking" a constructor call. Then you can call it after operations like map, and you'll always get a properly validated Either.

data class Author(val name: String) {
        fun validate(): Either<EmptyAuthorName, Author> = either {
            ensure(name.isNotEmpty()) { EmptyAuthorName }
            this
        }
}

Author("fo").validate()
    .map { it.copy(name = "").validate() }
    .run { println(this) } // Either.Left(EmptyAuthorName)

Another upside of doing it this way is that your code becomes easier to understand. In your example, you have a function that looks like an Author constructor, but it doesn't return an instance of Author (but an instance of Either). That's confusing. Making a function that clearly specifies that it validates and returns a validated result is better and easier to understand.

Upvotes: 2

Related Questions