Daniel Ryan
12/07/2020, 1:36 PMinternal fun polygonStringToPolygon(polygonString: String): Either<String, Polygon> {
fun collectResults(
results: Iterable<MatchResult>,
acc: MutableList<Position> = mutableListOf()
): Either<String, List<Position>> {
if (results.none()) {
return Either.right(acc.toList())
}
val (match, rest) = results.splitAt(1)
val x = match.single().groupValues.getOrNull(1)?.toDoubleOrNull()
val y = match.single().groupValues.getOrNull(2)?.toDoubleOrNull()
return if (x == null || y == null) {
Either.left("Unable to parse polygon")
} else {
acc.add(Position(x, y))
collectResults(rest, acc)
}
}
val regex = "\\[([0-9]*.[0-9]*),([0-9]*.[0-9]*)]".toRegex()
val matchResult = regex.findAll(polygonString)
return collectResults(matchResult.asIterable())
}
Jannis
12/07/2020, 1:48 PMtraverse
.
traverse
iterates over a structure similar to map
but at each step you can return an effectful result like Either
(depending on what applicative instance you pass to traverse). When it receives a left it'll short circuit, on a right it'll accumulate.
traverse
is a bit more general than what I explained but for sequences this should be accurate. It is also lazy enough to stop when it receives a failure (at least for sequences that is!)
Here is how it could look with `traverse`: (I copied this together from your code, I have not tested or looked at it in an ide ๐ )
internal fun polygonStringToPolygon(str: String): Either<String, Polygon> {
val regex = "\\[([0-9]*.[0-9]*),([0-9]*.[0-9]*)]".toRegex()
val matchResult = regex.findAll(polygonString)
matchResult.traverse(Either.applicative()) { match ->
val x = match.single().groupValues.getOrNull(1)?.toDoubleOrNull()
val y = match.single().groupValues.getOrNull(2)?.toDoubleOrNull()
if (x == null || y == null) Either.left("Unable to parse polygon")
else Either.right(Position(x, y))
}
}
Jannis
12/07/2020, 1:50 PMtraverse
has a very weird type fun <F, G, A, B> Kind<G, A>.traverse(ap: Applicative<F>, f: (A) -> Kind<F, B>): Kind<F, Kind<G, B>>
but if you fill this in for sequence and either you simply get:
fun <L, A, B> Sequence<A>.eitherTraverse(f: (A) -> Either<L, B>): Either<L, Sequence<B>>
which is much clearer as to what is happeningJannis
12/07/2020, 1:51 PMDaniel Ryan
12/07/2020, 2:07 PMDaniel Ryan
12/08/2020, 4:28 PMinternal fun polygonStringToPolygon(str: String): Either<String, Polygon> {
val regex = "\\[([0-9]*.[0-9]*),([0-9]*.[0-9]*)]".toRegex()
val matchResult = regex.findAll(str)
return matchResult.traverse(Either.applicative()) { match ->
val x = match.groupValues.getOrNull(1)?.toDoubleOrNull()
val y = match.groupValues.getOrNull(2)?.toDoubleOrNull()
if (x == null || y == null) Either.left("Unable to parse polygon")
else Either.right(Position(x, y))
}.map { it.fix().toList() }
}