Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provide a helper to map arguments of variable length #67

Closed
robstoll opened this issue Jan 12, 2019 · 2 comments
Closed

provide a helper to map arguments of variable length #67

robstoll opened this issue Jan 12, 2019 · 2 comments
Assignees
Milestone

Comments

@robstoll
Copy link
Owner

Platform (JVM and/or JS): all
Intended for assertion function writers. For instance, say we have a class Person as follows:

data class Person(val firstName: String, val lastName: String, ... /* and others */)

Say we have an algorithm which returns a list of firstName and lastName as a Pair. Since we deal a lot with Person we want to be able to compare the pairs against a variable length of persons, so we write:

fun Assert<List<Pair<String, String>>>.namesOf(
    person: Person, vararg otherPersons: Person
): Assert<List<Pair<String, String>>> {
    //types would not be necessary, only here for illustration
    val pair: Pair<String, String> = person.firstName to person.lastName
    val otherPairs: Array<out Pair<String, String>> = otherPersons.map { it.firstName to it.lastName }.toTypedArray()
    return contains.inAnyOrder.only.values(pair, *otherPairs)
}

in order that we can use it then as follows:

assert(get...WhichReturnsPairs()).namesOf(fKafka, eBloch, kTucholsky)

Using the function is neat but writing it is quite cumbersome. I would like to be able to write the following:

fun Assert<List<Pair<String, String>>>.namesOf(
    person: Person, vararg otherPersons: Person
): Assert<List<Pair<String, String>>> {
    val (pair, otherPairs) = mapArguments(person, otherPersons) { it.firstName to it.lastName }
    return contains.inAnyOrder.only.values(pair, *otherPairs)
}

Another fictional example, say we want to assert that the pairs have the same initials as the given persons and in the given order:

fun Assert<List<Pair<String, String>>>.sameInitialsAs(
    person: Person, vararg otherPersons: Person
): Assert<List<Pair<String, String>>> {
    //types are actually necessary
    val assertionCreator: Assert<Pair<String, String>>.() -> Unit =  {
        first.startsWith(person.firstName[0].toString())
        second.startsWith(person.lastName[0].toString())
    }
    val otherAssertionCreators = otherPersons.map<Person, Assert<Pair<String, String>>.() -> Unit> {
        {
            first.startsWith(person.firstName[0].toString())
            second.startsWith(person.lastName[0].toString())
        }
    }.toTypedArray()
    return contains.inAnyOrder.only.entries(assertionCreator, *otherAssertionCreators)
}

Sure, there is quite a bit of code duplication here but in the end one should not even have the need to factor out duplicated code and instead, one should be able to write:

fun Assert<List<Pair<String, String>>>.sameInitialsAs(
    person: Person, vararg otherPersons: Person
): Assert<List<Pair<String, String>>> {
    val (pair, otherPairs) = mapArguments(person, otherPersons).toAssert<Pair<String, String>> { 
        first.startsWith(it.firstName[0].toString()) 
        second.startsWith(it.lastName[0].toString()) 
    }
    return contains.inOrder.only.values(pair, *otherPairs)
}

That looks already cleaner, no more code duplication and less types necessary (I guess we need the type parameter in toAssert as we need to know to what kind of Assert<T> we want to map the arguments). In case Kotlin gets higher kinded types at some point, then we can still simplify.

I guess it would make sense that one finds the function also on AssertImpl -- AssertImpl should kind of be the starting point for assertion function writers. Maybe simply AssertImpl.mapArguments?

@robstoll robstoll self-assigned this Jan 12, 2019
@robstoll robstoll added this to the 0.8.0 milestone Jan 12, 2019
@robstoll
Copy link
Owner Author

We have parameter objects in the infix API to provide T, vararg T in one go. I thinks we should add a method in order that those objects can be mapped as well.

@robstoll
Copy link
Owner Author

robstoll commented Jan 12, 2019

The idea with AssertImpl is a bit overkill as I would need to duplicate all functions especially because we should not only support Array<out T> but also the special arrays for primitive types such as IntArray

robstoll added a commit that referenced this issue Jan 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant