Skip to content

Impure validation #17

@CLOVIS-AI

Description

@CLOVIS-AI

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions