Jonathan
02/27/2020, 2:29 PMFlow
and Arrow's IO
?
Here is an example of what I'd like to achieve. But it doesn't compilbe because IO.fx
restrict suspension:
val flow: Flow<Int> =
flowOf(1, 2, 3)
fun putStrLn(value: Any?): IO<Unit> =
IO { println(value) }
val program = IO.fx {
flow.collect {
!putStrLn(it)
}
}
fun main() {
program.unsafeRunSync()
}
simon.vergauwen
02/27/2020, 2:32 PMputStrLn(it).suspended()
instead of !
if you’re in a suspended
contextsimon.vergauwen
02/27/2020, 2:33 PM0.10.5-SNAPSHOT
where we expose operations such as suspendCancelable
and unsafeRunScoped
to integrate with structured concurrencyJonathan
02/27/2020, 2:35 PMsimon.vergauwen
02/27/2020, 2:35 PMaballano
02/27/2020, 4:43 PMJonathan
02/28/2020, 10:57 AMIO
, I have a hard time to understand its benefits. To me it looks like IO<T>
is equivalent to suspend () -> T
. So why wouldn't I declare suspend
functions instead of using IO
?aballano
02/28/2020, 11:28 AMaballano
02/28/2020, 11:28 AMaballano
02/28/2020, 11:41 AMaballano
02/28/2020, 11:42 AMsuspend fun openFile(): File =
File("./some/path")
with coroutines, you would need to surround either that function or the calling site with a try-catch to avoid exceptionsaballano
02/28/2020, 11:42 AM.handleError {...}
aballano
02/28/2020, 11:43 AM!lines.parTraverse { IO { it.transformLine) } }
aballano
02/28/2020, 11:44 AMJonathan
02/28/2020, 12:51 PMEither
helpful, but I don't see how IO
helps. It anyway represents error with Throwable
, forcing me to use IO<Either<L, R>>
in order to get proper error handling. And I can do the same with suspending function that return Either
.
// Let's assume this function being in a third party lib.
// So I cannot change it's signature or implementation
fun boom(): Int = throw Exception("boom")
// how this IO oriented code:
val foo = IO { boom() }
// whould be supperior that:
suspend fun foo() = boom()
suspend fun main() {
foo() // <-- I agree, it throws. Which is not good.
foo.suspended() // <-- But this trows as well.
foo.unsafeRunSync() // <-- And that throws too.
// Yes, that one doesn't throw:
foo.runAsync { either ->
// But I only get an exception. So it is still not that helpful.
// And I will have to have at least one unsafe call at my entry point.
// So how `IO` is preventing it from crashing, more than what suspend function and Either?
IO.unit
}
}
Jonathan
02/28/2020, 12:52 PMval openFile: IO<File>
would be superior to suspend fun openFile(): File
Could you write a complete example?Jonathan
02/28/2020, 12:53 PM// how this IO oriented code:
val foo = IO {
Either.catch { boom() }
}
// would be superior that:
suspend fun foo() = Either.catch { boom() }
aballano
02/28/2020, 12:54 PMJonathan
02/28/2020, 12:54 PMJonathan
02/28/2020, 12:55 PMaballano
02/28/2020, 12:56 PMJonathan
02/28/2020, 12:57 PMIO<E, A>
be prefereable to suspend () -> Either<E, A>
?aballano
02/28/2020, 12:58 PMJonathan
02/28/2020, 12:59 PMsuspend
function already support cancellation. And Either
already suport typed error handling.aballano
02/28/2020, 12:59 PMaballano
02/28/2020, 1:01 PMJonathan
02/28/2020, 1:01 PMIO
?Jonathan
02/28/2020, 1:01 PMJonathan
02/28/2020, 1:01 PMaballano
02/28/2020, 1:01 PMJonathan
02/28/2020, 1:01 PMaballano
02/28/2020, 1:01 PMaballano
02/28/2020, 1:05 PMaballano
02/28/2020, 1:05 PMaballano
02/28/2020, 1:08 PMyield()
or to check cancellation in any way
• runBlocking == fx + unsafeRunXXX (because of lazyness)
• launch inside runBlocking or others == IO.fork()Jonathan
02/28/2020, 1:11 PMJonathan
02/28/2020, 1:11 PMaballano
02/28/2020, 1:13 PMrunBlocking {
openFile()
.read()
.transformLines(Transformations.RemoveEveryXLetter(1))
.forEach(::println)
}
IO + fx
IO.fx {
val file = !IO { openFile() }
val lines = !IO { file.read() }
val transformedLines = !IO { lines.map { it.transformLine(Transformations.RemoveEveryXLetter(1)) } }
!IO { transformedLines.forEach(::println) }
}.unsafeRunXXX
so not big difference, actually the IO is more verboseaballano
02/28/2020, 1:13 PMaballano
02/28/2020, 1:15 PMval lines = openFile().read()
val transformedLines = try {
lines.map { it.transformLine(Transformations.RemoveEveryXLetter(1)) }
} catch (t: Throwable) {
emptyList<String>()
}
transformedLines.forEach(::println)
or something equivalentaballano
02/28/2020, 1:16 PM.handleError { emptyList() }
so
IO.fx {
val file = !IO { openFile() }
val lines = !IO { file.read() }
val transformedLines = !IO { lines.map { it.transformLine(Transformations.RemoveEveryXLetter(1)) } }.handleError { emptyList() }
!IO { transformedLines.forEach(::println) }
}.unsafeRunXXX
aballano
02/28/2020, 1:17 PMval transformedLines = !lines.parTraverse { IO { it.transformLine(Transformations.RemoveEveryXLetter(1)) } }
and done, each line in the file will be paralelized and treated independantly 🤷aballano
02/28/2020, 1:18 PMaballano
02/28/2020, 1:18 PMJonathan
02/28/2020, 1:19 PMJonathan
02/28/2020, 1:22 PMJonathan
02/28/2020, 1:22 PMIO<E, A>
!Jonathan
02/28/2020, 1:24 PMFlow
or equivalent ? I mean some kind equivalent of an IO
but that can emit multiple results instead of one.Jonathan
02/28/2020, 1:25 PMlines
in your error handling example?aballano
02/28/2020, 1:26 PMList<String>
aballano
02/28/2020, 1:26 PMaballano
02/28/2020, 1:26 PMJonathan
02/28/2020, 1:27 PMFlow
with IO
rather than creating an equivalent of Flow
?aballano
02/28/2020, 1:32 PMaballano
02/28/2020, 1:32 PMJonathan
02/28/2020, 1:32 PMaballano
02/28/2020, 1:33 PM