Skip to content

Commit

Permalink
Implement filterIsInstance and traverseFilterIsInstance (#129)
Browse files Browse the repository at this point in the history
* Implement `filterIsInstance` and `traverseFilterIsInstance`

* Fix after merge

* Fix :arrow-core-data:ktlintMainSourceSetCheck

* Fix Ank problem

Co-authored-by: Alberto Ballano <aballano@users.noreply.github.com>
  • Loading branch information
hadilq and aballano authored Jun 1, 2020
1 parent ff9b715 commit 7a79421
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@ interface FunctorFilter<F> : Functor<F> {
*/
fun <A> Kind<F, A>.filter(f: (A) -> Boolean): Kind<F, A> =
filterMap { a -> if (f(a)) Some(a) else None }

/**
* Filter out instances of [B] type.
*/
fun <A, B> Kind<F, A>.filterIsInstance(klass: Class<B>): Kind<F, B> =
filter(klass::isInstance)
.map { klass.cast(it) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,12 @@ interface TraverseFilter<F> : Traverse<F>, FunctorFilter<F> {

override fun <A> Kind<F, A>.filter(f: (A) -> Boolean): Kind<F, A> =
filterA({ Id(f(it)) }, IdApplicative).value()

/**
* Filter out instances of [B] type and traverse the [G] context.
*/
fun <G, A, B> Kind<F, A>.traverseFilterIsInstance(AP: Applicative<G>, klass: Class<B>): Kind<G, Kind<F, B>> = AP.run {
filterA({ a -> just(klass.isInstance(a)) }, AP)
.map { fa -> fa.map { a -> klass.cast(a) } }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ object FunctorFilterLaws {
val EQ = EQK.liftEq(Int.eq())

return FunctorLaws.laws(FFF, GENK, EQK) + listOf(
Law("Functor Filter: filterMap composition") { FFF.filterMapComposition(GEN, EQ) },
Law("Functor Filter: filterMap map consistency") { FFF.filterMapMapConsistency(GEN, EQ) },
Law("Functor Filter: flattenOption filterMap consistency") { FFF.flattenOptionConsistentWithfilterMap(GEN, EQ) },
Law("Functor Filter: filter filterMap consistency") { FFF.filterConsistentWithfilterMap(GEN, EQ) }
)
Law("Functor Filter: filterMap composition") { FFF.filterMapComposition(GEN, EQ) },
Law("Functor Filter: filterMap map consistency") { FFF.filterMapMapConsistency(GEN, EQ) },
Law("Functor Filter: flattenOption filterMap consistency") { FFF.flattenOptionConsistentWithfilterMap(GEN, EQ) },
Law("Functor Filter: filter filterMap consistency") { FFF.filterConsistentWithfilterMap(GEN, EQ) },
Law("Functor Filter: filterIsInstance filterMap consistency") { FFF.filterIsInstanceConsistentWithfilterMap(GEN, EQ) }
)
}

fun <F> FunctorFilter<F>.filterMapComposition(G: Gen<Kind<F, Int>>, EQ: Eq<Kind<F, Int>>): Unit =
Expand Down Expand Up @@ -62,4 +63,11 @@ object FunctorFilterLaws {
) { fa: Kind<F, Int>, f ->
fa.filter(f).equalUnderTheLaw(fa.filterMap { if (f(it)) Some(it) else None }, EQ)
}

fun <F> FunctorFilter<F>.filterIsInstanceConsistentWithfilterMap(G: Gen<Kind<F, Int>>, EQ: Eq<Kind<F, Int>>): Unit =
forAll(
G
) { fa: Kind<F, Int> ->
fa.filterIsInstance(Integer::class.java).map { it as Int }.equalUnderTheLaw(fa.filterMap { Some(it) }, EQ)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ object TraverseFilterLaws {
return TraverseLaws.laws(TF, GA, GENK, EQK) +
listOf(
Law("TraverseFilter Laws: Identity") { TF.identityTraverseFilter(GEN, GA, EQ_NESTED) },
Law("TraverseFilter Laws: filterA consistent with TraverseFilter") { TF.filterAconsistentWithTraverseFilter(GEN, genBool, GA, EQ_NESTED) }
Law("TraverseFilter Laws: filterA consistent with TraverseFilter") { TF.filterAconsistentWithTraverseFilter(GEN, genBool, GA, EQ_NESTED) },
Law("TraverseFilter Laws: traverseFilterIsInstance consistent with TraverseFilter") { TF.traverseFilterIsInstanceConsistentWithTraverseFilter(GEN, GA, EQ_NESTED) }
)
}

Expand All @@ -48,4 +49,16 @@ object TraverseFilterLaws {
fa.filterA(f, GA).equalUnderTheLaw(fa.traverseFilter(GA) { a -> f(a).map { b: Boolean -> if (b) Some(a) else None } }, EQ)
}
}

fun <F> TraverseFilter<F>.traverseFilterIsInstanceConsistentWithTraverseFilter(
genInt: Gen<Kind<F, Int>>,
GA: Applicative<F>,
EQ: Eq<Kind<F, Kind<F, Int>>>
) = run {
forAll(
genInt
) { fa: Kind<F, Int> ->
fa.traverseFilterIsInstance(GA, Integer::class.java).map { it.map { it as Int } }.equalUnderTheLaw(fa.traverseFilter(GA) { a -> GA.just(Some(a)) }, EQ)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ Apply a filter to a structure such that the output structure contains all `A` el
Some(1).filter { false }
```

#### Kind<F, A>#filterIsInstance

Filter out instances of a specific type.

```kotlin:ank
import arrow.core.extensions.option.functorFilter.filterIsInstance
Some(1).filterIsInstance(Int::class.java)
```

### Laws

Arrow provides [`FunctorFilterLaws`][functor_filter_law_source]{:target="_blank"} in the form of test cases for internal verification of lawful instances and third party apps creating their own `FunctorFilter` instances.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ import arrow.core.extensions.option.traverseFilter.filterA
Some(1).filterA({ Id.just(false) }, Id.applicative())
```

#### Kind<F, A>#traverseFilterIsInstance

Filter out instances of a specific type and traverse a context.

```kotlin:ank
import arrow.core.*
import arrow.core.extensions.id.applicative.applicative
import arrow.core.extensions.option.traverseFilter.traverseFilterIsInstance
Some(1).traverseFilterIsInstance(Id.applicative(), Int::class.java)
```

### Laws

Arrow provides [`TraverseFilterLaws`][travers_filter_laws_source]{:target="_blank"} in the form of test cases for internal verification of lawful instances and third party apps creating their own `TraverseFilter` instances.
Expand Down

0 comments on commit 7a79421

Please sign in to comment.