How does one avoid side affects when a computation...
# arrow
d
How does one avoid side affects when a computation needs to merge the result of a query to an external API and make business decisions on that? FP afaik is supposed to defer running the side affects till the top level, but here they are part of the calculation in the business logic... Does everything using the API results need to become impure?
p
I think guys here are using context receivers to do "side effects without side effects". I so an example with logging
Copy code
context(Logger)
fun foo() {}
t
It is impossible to make everything pure code, personally I use the facade pattern to deal with that / database call Controller -> Service(facade) - checks the result, and gives it to a pure function \ stores result in database
So in this case, the controller, service and database calls all are not pure functions, but my business logic is pure
d
My problem is that some data from the service needs to complement the input data of the logic for each entry in a map... So I'm looking up that data from the service while/as part of processing the input data
t
I dont know the exact problem, but it sounds that in your businessproces you need to do a service call that cannot be avoided? If it is impossible to supply the information beforehand and give it to your function in the form of a map or something, another way to do it is "cheat" and give your businesslogic a lambda that can do the lookup (that function should be total and not throw an exception). e.g.: doMyLogic(String data, sideEffect : (input : String -> RestResult)) { val a = sideEffect.apply(data) } This way the function itself is pure (it doesnt contain side effects) because the sideeffect is hidden in the input lambda.
d
That was exactly my question 😊... is that still called "pure", it CAN technically fail, but I tried covering that with either... I could pre-load all that data, but that could be less efficient... since there might be some of that data I that I might not really need... Btw, a little coincidence... I was looking for a video on FP while things are compiling and I fell on this:

https://www.youtube.com/watch?v=xxePZQlNyYY

😃 -- nice talk! I was looking for someone less mathematical and more practical!
j
d
Very helpful! It seems to boil down to what @Ties said... better to introduce all the data needed to the business process... but in the article there's no cheating... the lambda solution isn't really pure, since there's no referential transparency, so then the domain logic according to him shouldn't be complete but rather separate the impure parts into an impure controller... I'm just wondering if Arrow gives tools for such things... is that one of the uses of
effect { }
?
t
Well, the function itself is pure 😛 since it does not do side effects, the higher context makes it do a side-effect.. so the function itself is referentially transparent.. but I do agree its a cheat. Also, you cannot really "solve" the problem if your logic requires a side-effect halfway during the busineslogic. There is only fighting symptoms from that point.
d
I was thinking that the controller/use case couldn't be pure, but the functions it uses would be and wouldn't try getting that extra data... but then unit testing would be on the pure functions? Then who says the controller's using them properly? Then testing the controller + those pure functions boils down to testing an impure implementation 🤒.
t
Yes, the controller will be impure, that will always be the case (since it needs to get data from somewhere)
j
Correct, you should try to write “humble” controllers or use cases, then write unit test on your domain and a few integration tests on your the formers.
Regarding use case purity i have seen 2 approaches: 1. inject out of process dependencies on it which of course leads to impurity. 2. inject “descriptions” of out of process dependencies”, similiar to what you get with scala IO or kotlin effects. This will make your use cases pure as it will return a description of some code until you execute it on an impure layer. But it depends on the architecture you are trying to build. A matter of trade-offs.
d
By "descriptions" you probably mean `effect { }`s... but that only applies to things you don't need their results in the actual logic, like saving to a database or logging...?
As soon as you
bind()
to them in the use case, then it becomes impure right?
j
mmm...not sure about bind, but sure about toeither or fold
d
bind()
wouldn't run it and try to return the happy path?
j
..but only when you execute fold/toeither, but you can test it on your own
d
Btw, thanks for all the help guys 👍🏼!
s
The most common approach is to split business domain code, into separate functions and compose them together. I.e. your service layer is by definition almost always impure but in itself shouldn't be more than composing pure and impure code. Working with KotlinX Coroutines suffers from the same problem, if your database or network uses
suspend
it travels all the way to your UI or Route but you can separate all
suspend fun
and non-suspend fun in separate functions and then compose them together in your service layer.
d
But again, then testing the composition is a bit of a nightmare... it's either going for integration tests, or using interfaces for impure functions and test fakes... which is a bit painful. I guess that's why you prefer only integration tests in your Ktor sample video @simon.vergauwen, but when you get to more complex logic, that also gets pretty painful.
In OOP, decoupling is a thing... but in FP, composition is by definition coupled.
s
Oh, you meant in that sense. I would say that is completely unrelated to FP, but more about how you design your code. No, I still use interfaces in almost the identical way I did when I was following OOP patterns.
d
But if all you have are integration tests, then you don't really need those interfaces?
s
Nope, but that is unrelated to FP. The same is true for OOP.
j
@dave08 i have upload to github a piece of code which i use as a playground and will be updating it in the following days in order to practice ktor integration and other features related to arrow. https://github.com/jorgebo10/funcore