Skip to content

Commit 7a79421

Browse files
hadilqaballano
andauthored
Implement filterIsInstance and traverseFilterIsInstance (#129)
* Implement `filterIsInstance` and `traverseFilterIsInstance` * Fix after merge * Fix :arrow-core-data:ktlintMainSourceSetCheck * Fix Ank problem Co-authored-by: Alberto Ballano <[email protected]>
1 parent ff9b715 commit 7a79421

File tree

6 files changed

+64
-6
lines changed

6 files changed

+64
-6
lines changed

arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/FunctorFilter.kt

+7
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,11 @@ interface FunctorFilter<F> : Functor<F> {
2929
*/
3030
fun <A> Kind<F, A>.filter(f: (A) -> Boolean): Kind<F, A> =
3131
filterMap { a -> if (f(a)) Some(a) else None }
32+
33+
/**
34+
* Filter out instances of [B] type.
35+
*/
36+
fun <A, B> Kind<F, A>.filterIsInstance(klass: Class<B>): Kind<F, B> =
37+
filter(klass::isInstance)
38+
.map { klass.cast(it) }
3239
}

arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/TraverseFilter.kt

+8
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,12 @@ interface TraverseFilter<F> : Traverse<F>, FunctorFilter<F> {
4444

4545
override fun <A> Kind<F, A>.filter(f: (A) -> Boolean): Kind<F, A> =
4646
filterA({ Id(f(it)) }, IdApplicative).value()
47+
48+
/**
49+
* Filter out instances of [B] type and traverse the [G] context.
50+
*/
51+
fun <G, A, B> Kind<F, A>.traverseFilterIsInstance(AP: Applicative<G>, klass: Class<B>): Kind<G, Kind<F, B>> = AP.run {
52+
filterA({ a -> just(klass.isInstance(a)) }, AP)
53+
.map { fa -> fa.map { a -> klass.cast(a) } }
54+
}
4755
}

arrow-libs/core/arrow-core-test/src/main/kotlin/arrow/core/test/laws/FunctorFilterLaws.kt

+13-5
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ object FunctorFilterLaws {
2424
val EQ = EQK.liftEq(Int.eq())
2525

2626
return FunctorLaws.laws(FFF, GENK, EQK) + listOf(
27-
Law("Functor Filter: filterMap composition") { FFF.filterMapComposition(GEN, EQ) },
28-
Law("Functor Filter: filterMap map consistency") { FFF.filterMapMapConsistency(GEN, EQ) },
29-
Law("Functor Filter: flattenOption filterMap consistency") { FFF.flattenOptionConsistentWithfilterMap(GEN, EQ) },
30-
Law("Functor Filter: filter filterMap consistency") { FFF.filterConsistentWithfilterMap(GEN, EQ) }
31-
)
27+
Law("Functor Filter: filterMap composition") { FFF.filterMapComposition(GEN, EQ) },
28+
Law("Functor Filter: filterMap map consistency") { FFF.filterMapMapConsistency(GEN, EQ) },
29+
Law("Functor Filter: flattenOption filterMap consistency") { FFF.flattenOptionConsistentWithfilterMap(GEN, EQ) },
30+
Law("Functor Filter: filter filterMap consistency") { FFF.filterConsistentWithfilterMap(GEN, EQ) },
31+
Law("Functor Filter: filterIsInstance filterMap consistency") { FFF.filterIsInstanceConsistentWithfilterMap(GEN, EQ) }
32+
)
3233
}
3334

3435
fun <F> FunctorFilter<F>.filterMapComposition(G: Gen<Kind<F, Int>>, EQ: Eq<Kind<F, Int>>): Unit =
@@ -62,4 +63,11 @@ object FunctorFilterLaws {
6263
) { fa: Kind<F, Int>, f ->
6364
fa.filter(f).equalUnderTheLaw(fa.filterMap { if (f(it)) Some(it) else None }, EQ)
6465
}
66+
67+
fun <F> FunctorFilter<F>.filterIsInstanceConsistentWithfilterMap(G: Gen<Kind<F, Int>>, EQ: Eq<Kind<F, Int>>): Unit =
68+
forAll(
69+
G
70+
) { fa: Kind<F, Int> ->
71+
fa.filterIsInstance(Integer::class.java).map { it as Int }.equalUnderTheLaw(fa.filterMap { Some(it) }, EQ)
72+
}
6573
}

arrow-libs/core/arrow-core-test/src/main/kotlin/arrow/core/test/laws/TraverseFilterLaws.kt

+14-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ object TraverseFilterLaws {
2929
return TraverseLaws.laws(TF, GA, GENK, EQK) +
3030
listOf(
3131
Law("TraverseFilter Laws: Identity") { TF.identityTraverseFilter(GEN, GA, EQ_NESTED) },
32-
Law("TraverseFilter Laws: filterA consistent with TraverseFilter") { TF.filterAconsistentWithTraverseFilter(GEN, genBool, GA, EQ_NESTED) }
32+
Law("TraverseFilter Laws: filterA consistent with TraverseFilter") { TF.filterAconsistentWithTraverseFilter(GEN, genBool, GA, EQ_NESTED) },
33+
Law("TraverseFilter Laws: traverseFilterIsInstance consistent with TraverseFilter") { TF.traverseFilterIsInstanceConsistentWithTraverseFilter(GEN, GA, EQ_NESTED) }
3334
)
3435
}
3536

@@ -48,4 +49,16 @@ object TraverseFilterLaws {
4849
fa.filterA(f, GA).equalUnderTheLaw(fa.traverseFilter(GA) { a -> f(a).map { b: Boolean -> if (b) Some(a) else None } }, EQ)
4950
}
5051
}
52+
53+
fun <F> TraverseFilter<F>.traverseFilterIsInstanceConsistentWithTraverseFilter(
54+
genInt: Gen<Kind<F, Int>>,
55+
GA: Applicative<F>,
56+
EQ: Eq<Kind<F, Kind<F, Int>>>
57+
) = run {
58+
forAll(
59+
genInt
60+
) { fa: Kind<F, Int> ->
61+
fa.traverseFilterIsInstance(GA, Integer::class.java).map { it.map { it as Int } }.equalUnderTheLaw(fa.traverseFilter(GA) { a -> GA.just(Some(a)) }, EQ)
62+
}
63+
}
5164
}

arrow-libs/core/arrow-docs/docs/arrow/typeclasses/functorfilter/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ Apply a filter to a structure such that the output structure contains all `A` el
4444
Some(1).filter { false }
4545
```
4646

47+
#### Kind<F, A>#filterIsInstance
48+
49+
Filter out instances of a specific type.
50+
51+
```kotlin:ank
52+
import arrow.core.extensions.option.functorFilter.filterIsInstance
53+
54+
Some(1).filterIsInstance(Int::class.java)
55+
```
56+
4757
### Laws
4858

4959
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.

arrow-libs/core/arrow-docs/docs/arrow/typeclasses/traversefilter/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ import arrow.core.extensions.option.traverseFilter.filterA
4242
Some(1).filterA({ Id.just(false) }, Id.applicative())
4343
```
4444

45+
#### Kind<F, A>#traverseFilterIsInstance
46+
47+
Filter out instances of a specific type and traverse a context.
48+
49+
```kotlin:ank
50+
import arrow.core.*
51+
import arrow.core.extensions.id.applicative.applicative
52+
import arrow.core.extensions.option.traverseFilter.traverseFilterIsInstance
53+
54+
Some(1).traverseFilterIsInstance(Id.applicative(), Int::class.java)
55+
```
56+
4557
### Laws
4658

4759
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.

0 commit comments

Comments
 (0)