Sergei Zubov
07/14/2021, 12:40 PMsuspend () -> A
instead of IO<A>
. Does this literally means that signature of the function that produces a side-effect should be suspend () -> A
, or it can be suspend (parameter1, parameter2...) -> A
? Which one is correct and why:
data class Message(val content: String)
suspend fun sideEffectProducer(message: Message) = println(message)
or
data class Message(val content: String)
suspend fun Message.sideEffectProducer() = println(this)
And a follow-up question - how functions with side effects differ from pure functions that use either
block and .bind()
results? Both of them are suspend
simon.vergauwen
07/14/2021, 1:10 PMsuspend () -> A
we talk about suspend fun f(): A
, or val f: suspend () -> A
since both are the same.
They're just 2 different ways to describe the same thing, and you can move back and forth between the two different notations easily. (For example using method referencing).
So both example you shared is correct and depends on personal preference and code styles used. I personally almost always use suspend fun example(): Unit
and when I need to pass it somewhere as a lambda I reference it as a method reference ?let(::example)
.
either { }
has two different versions:
• either.eager
which works doesn't allow suspension. So you cannot call any other suspend
fun inside.
• either { }
which does allow suspension, so you can call other suspending functions like withContext
inside.
So using either.eager
you can only write pure functions since you cannot call suspend
inside.
either { }
on the other hand, does allow suspension, so we can call side-effecting code from inside.
It's similar to List#map
.
Since it's inline
it allows for suspension to go through, but depending on if you call a suspending function inside then the result is considered not-pure.Sergei Zubov
07/14/2021, 1:37 PMsuspend
to make my program run in a declarative deferred way? Or as far as higher-level function is suspend
, calling non-suspend function from it is fine?simon.vergauwen
07/14/2021, 1:47 PMBut shouldn't I also make all of my functionsYes, that's very typical. You could say that marking all your functions with suspend allows you to run your program in a declarative deferred way. You could also that's the result of being referential-transparent and purity. These are all properties we care about in FP, and so you could also use different arguments for this. They all achieve the same goal I think, which is being able to reason about code in small blocks.to make my program run in a declarative deferred way?suspend
Or as far as higher-level function isCalling pure code from side-effecting code is always fine. So calling non-suspend functions from, calling non-suspend function from it is fine?suspend
suspend
fun is fine 👍
Actually, I always try to make as many non-suspend functions as possible. I am thinking now specifically about domain mappers, computations, combining data, ... etc.simon.vergauwen
07/14/2021, 1:49 PMsuspend fun fetchUser(id: Int): User = ...
suspend fun fetchProfile(id: Int): Profile = ...
suspend fun fetchAvatar(id: Int): Avatar = ...
fun createUserProfile(user: User, profile: Profile, avatar: Avatar): UserProfile = ...
suspend fun fetchUserProfile(id: Int): UserProfile =
createUserProfile(fetchUser(id), fetchProfile(id), fetchAvatar(id))
If later I want to refactor my network calls to return Either<Throwable, A>
my code changes.
suspend fun fetchUserProfile(id: Int): UserProfile =
fetchUser(id).zip(fetchProfile(id), fetchAvatar(id), ::createUserProfile)
Sergei Zubov
07/14/2021, 1:53 PMsimon.vergauwen
07/14/2021, 1:55 PM