Skip to content

Commit 28925ce

Browse files
Stdlib: Add OneOrBoth and zipLongest on lists and streams (#1003)
Co-authored-by: Jiří Beneš <[email protected]>
1 parent ab94c0a commit 28925ce

File tree

7 files changed

+107
-2
lines changed

7 files changed

+107
-2
lines changed

examples/stdlib/list/zip.check

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
Cons((1, 100), Cons((2, 200), Cons((3, 300), Nil())))
22
Nil()
33
Nil()
4-
Cons((1, 42), Nil())
4+
Cons((1, 42), Nil())
5+
Cons(Both(1, 100), Cons(Both(2, 200), Cons(Both(3, 300), Nil())))
6+
Cons(This(1), Cons(This(2), Cons(This(3), Nil())))
7+
Cons(That(1), Cons(That(2), Cons(That(3), Nil())))
8+
Cons(Both(1, 42), Cons(This(2), Cons(This(3), Nil())))

examples/stdlib/list/zip.effekt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,25 @@ def printListOfPairs(list: List[Tuple2[Int, Int]]): Unit = {
1010
println(strings)
1111
}
1212

13+
// used to circumvent missing universal `show` & `println` on some backends
14+
def printListOfOneOrBoth(list: List[OneOrBoth[Int, Int]]): Unit = {
15+
val strings: List[String] = list.map {
16+
case Both(fst, snd) => "Both(" ++ show(fst) ++ ", " ++ show(snd) ++ ")"
17+
case This(fst) => "This(" ++ show(fst) ++ ")"
18+
case That(snd) => "That(" ++ show(snd) ++ ")"
19+
}
20+
println(strings)
21+
}
22+
1323
def main() = {
1424
// synchronized with doctest in `zip`
1525
printListOfPairs(zip([1, 2, 3], [100, 200, 300]))
1626
printListOfPairs(zip([1, 2, 3], Nil[Int]()))
1727
printListOfPairs(zip(Nil[Int](), [1, 2, 3]))
1828
printListOfPairs(zip([1, 2, 3], [42]))
29+
// synchronized with doctest in `zipLongest`
30+
printListOfOneOrBoth(zipLongest([1, 2, 3], [100, 200, 300]))
31+
printListOfOneOrBoth(zipLongest([1, 2, 3], Nil[Int]()))
32+
printListOfOneOrBoth(zipLongest(Nil[Int](), [1, 2, 3]))
33+
printListOfOneOrBoth(zipLongest([1, 2, 3], [42]))
1934
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
1, H
2+
2, e
3+
3, l
4+
4, l
5+
5, o
6+
6
7+
7
8+
8
9+
9
10+
10
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import stream
2+
3+
def main() = {
4+
def stream1() = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].each
5+
// Ideally this would be an array to demonstrate the capabilities.
6+
def stream2() = ['H', 'e', 'l', 'l', 'o'].each
7+
8+
zipLongest[Int, Char]{stream1}{stream2} {
9+
case Both(a, b) => println(show(a) ++ ", " ++ show(b))
10+
case This(a) => println(show(a))
11+
case That(b) => println("?, " ++ show(b))
12+
}
13+
}

libraries/common/effekt.effekt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,13 @@ record Tuple4[A, B, C, D](first: A, second: B, third: C, fourth: D)
715715
record Tuple5[A, B, C, D, E](first: A, second: B, third: C, fourth: D, fifth: E)
716716
record Tuple6[A, B, C, D, E, F](first: A, second: B, third: C, fourth: D, fifth: E, sixth: F)
717717

718+
// OneOrBoth
719+
// =========
720+
type OneOrBoth[A, B] {
721+
This(a: A)
722+
That(b: B)
723+
Both(a: A, b: B)
724+
}
718725

719726
// Control Flow
720727
// ============

libraries/common/list.effekt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,40 @@ def zip[A, B](left: List[A], right: List[B]): List[(A, B)] = {
601601
go(Nil(), left, right)
602602
}
603603

604+
/// Produce a list of OneOrBoth from two lists.
605+
/// The length is the maximum of lenghts of the two lists.
606+
///
607+
/// Examples:
608+
/// ```
609+
/// > zipLongest([1, 2, 3], [100, 200, 300])
610+
/// [Both(1, 100), Both(2, 200), Both(3, 300)]
611+
///
612+
/// > zipLongest([1, 2, 3], Nil[Int]())
613+
/// [This(1), This(2), This(3)]
614+
///
615+
/// > zipLongest(Nil[Int](), [1, 2, 3])
616+
/// [That(1), That(2), That(3)]
617+
///
618+
/// > zipLongest([1, 2, 3], [42])
619+
/// [These(1, 42), This(2), This(3)]
620+
/// ```
621+
///
622+
/// O(N)
623+
def zipLongest[A, B](left: List[A], right: List[B]): List[OneOrBoth[A, B]] = {
624+
def go(acc: List[OneOrBoth[A, B]], left: List[A], right: List[B]): List[OneOrBoth[A,B]] = {
625+
(left, right) match {
626+
case (Cons(a, as), Cons(b, bs)) =>
627+
go(Cons(Both(a, b), acc), as, bs)
628+
case (Cons(a, as), Nil()) =>
629+
go(Cons(This(a), acc), as, Nil())
630+
case (Nil(), Cons(b, bs)) =>
631+
go(Cons(That(b), acc), Nil(), bs)
632+
case _ => acc.reverse
633+
}
634+
}
635+
go(Nil(), left, right)
636+
}
637+
604638
/// Combine two lists with the given function.
605639
/// The length of the result is the minimum of lengths of the two lists.
606640
///

libraries/common/stream.effekt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ def feed[R](bytes: ByteArray) { reader: () => R / read[Byte] }: R = {
286286
def source[A] { stream: () => Unit / emit[A] } { reader: () => Unit / read[A] }: Unit =
287287
returning::source[A, Unit]{stream}{reader}
288288

289-
/// Combines two streams together producing a stream of pairs in lockstep.
289+
/// Combines two streams together calling action on pairs in lockstep.
290290
/// Terminates when either of them terminates.
291291
def zip[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / emit[B] } { action: (A, B) => Unit }: Unit = {
292292
with source[A] { stream1 }
@@ -297,6 +297,28 @@ def zip[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / emit[B]
297297
}
298298
}
299299

300+
/// Combines two streams together calling action on OneOrBoth in lockstep.
301+
/// Terminates when both of them terminated.
302+
def zipLongest[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / emit[B] } { action: OneOrBoth[A, B] => Unit }: Unit = {
303+
with source[A] { stream1 }
304+
with source[B] { stream2 }
305+
306+
exhaustively {
307+
try {
308+
val a = do read[A]()
309+
try {
310+
val b = do read[B]()
311+
action(Both(a, b))
312+
} with stop { () =>
313+
action(This(a))
314+
}
315+
} with stop{ () =>
316+
val b = do read[B]()
317+
action(That(b))
318+
}
319+
}
320+
}
321+
300322
def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[IOError] = {
301323

302324
val file = openForWriting(path);

0 commit comments

Comments
 (0)