Represent validation errors as data
refine currently returns an Either[String, A]. String is an awful type for errors and should probably be replaced by a dedicated error type like RefinementError.
case class RefinementError(msg: String)
Even RefinementError is not good enough. Suppose we have a predicate A And B. How do I know if only A failed, only B failed or both failed? (Parsing the error message is of course not an option.)
This issue is twofold. Strings are okay for presenting an error to the user. In case of refined there are two kinds of users. If validation happens at compile-time, the user is the developer who is using the library. If validation happens at runtime, the user is the user of the application which is developed by the developer who uses refined.
In the first case we need to print a string as error message at compile-time. In the second case we probably want to represent the validation error as data so that the developer can write recovery code that is tailored to the specific error. This way we also avoid error message i18n.
I started working on this here: https://github.com/fthomas/refined/compare/wip/errors-as-data
Current WIP branch is https://github.com/fthomas/refined/compare/wip/errors-as-data-4
The -4 branch has been superseded by https://github.com/fthomas/refined/compare/wip/errors-as-data-5 which is pretty close to being merged to master. I'll open a PR once the last rough edges are gone.
Anyhow, the branch is stable enough to play around with the new representation of validation results. You could start playing by evaluating
Validate[Unit, True Or False].validate(())
which should return Passed(Or(Passed(True()), Failed(False()))) with type Result[Or[Result[True], Result[False]]].