Skip to content

Commit 543bfbd

Browse files
authored
Merge pull request #241 from pt2121/pt/sequence
Add wrapper and instances for Sequence
2 parents 128f330 + 1a34582 commit 543bfbd

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package kategory
2+
3+
fun <A> SequenceKWKind<A>.toList(): List<A> = this.ev().sequence.toList()
4+
5+
@higherkind
6+
@deriving(Monad::class, Traverse::class, MonoidK::class)
7+
data class SequenceKW<out A> constructor(val sequence: Sequence<A>) : SequenceKWKind<A>, Sequence<A> by sequence {
8+
9+
fun <B> flatMap(f: (A) -> SequenceKWKind<B>): SequenceKW<B> = this.ev().sequence.flatMap { f(it).ev().sequence }.k()
10+
11+
fun <B> map(f: (A) -> B): SequenceKW<B> = this.ev().sequence.map(f).k()
12+
13+
fun <B> foldL(b: B, f: (B, A) -> B): B = this.ev().fold(b, f)
14+
15+
fun <B> foldR(lb: Eval<B>, f: (A, Eval<B>) -> Eval<B>): Eval<B> {
16+
fun loop(fa_p: SequenceKW<A>): Eval<B> = when {
17+
fa_p.sequence.none() -> lb
18+
else -> f(fa_p.first(), Eval.defer { loop(fa_p.drop(1).k()) })
19+
}
20+
return Eval.defer { loop(this.ev()) }
21+
}
22+
23+
fun <G, B> traverse(f: (A) -> HK<G, B>, GA: Applicative<G>): HK<G, SequenceKW<B>> =
24+
foldR(Eval.always { GA.pure(emptySequence<B>().k()) }) { a, eval ->
25+
GA.map2Eval(f(a), eval) { (sequenceOf(it.a) + it.b).k() }
26+
}.value()
27+
28+
fun <B, Z> map2(fb: SequenceKWKind<B>, f: (Tuple2<A, B>) -> Z): SequenceKW<Z> =
29+
this.ev().flatMap { a ->
30+
fb.ev().map { b ->
31+
f(Tuple2(a, b))
32+
}
33+
}.ev()
34+
35+
companion object {
36+
37+
fun <A> pure(a: A): SequenceKW<A> = sequenceOf(a).k()
38+
39+
fun <A> empty(): SequenceKW<A> = emptySequence<A>().k()
40+
41+
fun <A, B> tailRecM(a: A, f: (A) -> HK<SequenceKWHK, Either<A, B>>): SequenceKW<B> {
42+
tailrec fun <A, B> go(
43+
buf: MutableList<B>,
44+
f: (A) -> HK<SequenceKWHK, Either<A, B>>,
45+
v: SequenceKW<Either<A, B>>) {
46+
if (!v.isEmpty()) {
47+
val head: Either<A, B> = v.first()
48+
when (head) {
49+
is Either.Right<A, B> -> {
50+
buf += head.b
51+
go(buf, f, v.drop(1).k())
52+
}
53+
is Either.Left<A, B> -> {
54+
if (v.count() == 1)
55+
go(buf, f, (f(head.a).ev()).k())
56+
else
57+
go(buf, f, (f(head.a).ev() + v.drop(1)).k())
58+
}
59+
}
60+
}
61+
}
62+
63+
val buf = mutableListOf<B>()
64+
go(buf, f, f(a).ev())
65+
return SequenceKW(buf.asSequence())
66+
}
67+
68+
fun functor(): SequenceKWHKMonadInstance = SequenceKW.monad()
69+
70+
fun applicative(): SequenceKWHKMonadInstance = SequenceKW.monad()
71+
72+
fun <A> semigroup(): SequenceKWMonoid<A> = object : SequenceKWMonoid<A> {}
73+
74+
fun semigroupK(): SequenceKWHKMonoidKInstance = SequenceKW.monoidK()
75+
76+
fun <A> monoid(): SequenceKWMonoid<A> = object : SequenceKWMonoid<A> {}
77+
78+
fun foldable(): SequenceKWHKTraverseInstance = SequenceKW.traverse()
79+
}
80+
}
81+
82+
fun <A> SequenceKW<A>.combineK(y: SequenceKWKind<A>): SequenceKW<A> = (this.sequence + y.ev().sequence).k()
83+
84+
fun <A> Sequence<A>.k(): SequenceKW<A> = SequenceKW(this)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package kategory
2+
3+
interface SequenceKWMonoid<A> : Monoid<SequenceKW<A>> {
4+
override fun combine(a: SequenceKW<A>, b: SequenceKW<A>): SequenceKW<A> = (a + b).k()
5+
6+
override fun empty(): SequenceKW<A> = emptySequence<A>().k()
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package kategory.data
2+
3+
import io.kotlintest.KTestJUnitRunner
4+
import kategory.*
5+
import org.junit.runner.RunWith
6+
7+
@RunWith(KTestJUnitRunner::class)
8+
class SequenceKWTest : UnitSpec() {
9+
val applicative = SequenceKW.applicative()
10+
11+
init {
12+
val eq: Eq<HK<SequenceKWHK, Int>> = object : Eq<HK<SequenceKWHK, Int>> {
13+
override fun eqv(a: HK<SequenceKWHK, Int>, b: HK<SequenceKWHK, Int>): Boolean =
14+
a.toList() == b.toList()
15+
}
16+
17+
testLaws(MonadLaws.laws(SequenceKW.monad(), eq))
18+
testLaws(MonoidKLaws.laws(SequenceKW.monoidK(), applicative, eq))
19+
testLaws(TraverseLaws.laws(SequenceKW.traverse(), applicative, { n: Int -> SequenceKW(sequenceOf(n)) }, eq))
20+
}
21+
}

0 commit comments

Comments
 (0)