arrow-exact icon indicating copy to clipboard operation
arrow-exact copied to clipboard

Impure validation

Open CLOVIS-AI opened this issue 1 year ago • 5 comments

Although arrow-exact is originally meant for type refinements, our current DSL can handle any kind of validation.

In my projects, I have two major kinds of validation needs: pure and impure. So far, we have concentrated on pure validation (and I think we're doing a good job of it).

Here's an example of impure validation, that I have seen in the real world (for the curious). We can simplify this example to: the class we want to do validation on is called Reference. It stores an identifier to another business entity called File (here, we don't care what it is). A Reference is only valid if the referenced File exists at the time of instantiation. For this, we need two things:

  • suspend, to make a network request
  • an instance of FileService

Here's an example of how it could look like:

fun interface ImpureExact<out E, A, in C, out R> {
    suspend fun from(value: A, context: C): Either<E, R>
    // …
}

// If we just need suspension but no context, we provide a simplified interface
fun interface SuspendExact<out E, A, out R> : ImpureExact<E, A, Unit, R> {
    suspend fun from(value: A): Either<E, R> = from(value, Unit)
    // …
}

Usage:

data class Ref private constructor(
    val id: String,
) {
    companion object : ImpureExact<String, String, FileService, Ref> by exact({ it, service ->
        ensure(ExactId)
        
        val file = service.find(it)
        ensureNotNull(file) { "Could not find file $it" }
        
        Ref(it)
    })
}

suspend fun main() {
    val service = FileService(…)
    val id = service.create(…)
    
    Ref.fromOrThrow(id)
}

What do you think?

CLOVIS-AI avatar May 20 '23 21:05 CLOVIS-AI