Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
javierg1975
@javierg1975

Hi all,
I have the following use case

def getUsers(dateWindow: Interval[LocalDate]): IO[List[User]] = ???

Ideally I'd like to iterate over the resulting Users using something like this

def getImportantUserReport(user: User): IO[Option[ImportantUserReport]] = ???

But that won't compose. As in

for {
  users<- getUsers(currentTimeWindow)
  aUser <- users  // <- NOPE
  report <- getImportantUserReport(aUser)
  // above pattern repeats multiple times
  finalResult <- someFunction
} yield finalResult

The issue here is that I need User downstream to query other services, so my current solution does this

def getImportantUserReports(user: List[User]): IO[List[(User, Option[ImportantUserReport])]] = ???

This works, but I can't help but feeling there's a more elegant solution that doesn't rely on passing data along with every function call.
Any hints will be much appreciated

Luis Miguel Mejía Suárez
@BalmungSan
traverse?
getUsers(currentTimeWindow).flatMap { users =>
  users.traverse(getImportantUserReport)
}
1 reply
Adam Rosien
@arosien
:tada:
Dylan Halperin
@dylemma

is there anything in Cats that provides an Option[Either[A, B]] => Either[A, Option[B]] functionality like

opt match {
  case None => Right(None)
  case Some(e) => e.map(Some(_))
}

?

Luis Miguel Mejía Suárez
@BalmungSan
traverse?
Well, since you already have the Option[Either[A, B]] it would be sequence
Dylan Halperin
@dylemma
hmm I swear I tried that... maybe I was missing an implicit
Rob Norris
@tpolecat
Yeah it’s sequence. If you got there via map(f).sequence you can replace that with .traverse(f)
Luis Miguel Mejía Suárez
@BalmungSan
@dylemma maybe you have an extra import? Or you do not have partial-unification on.
scala> import cats.syntax.all._
import cats.syntax.all._

scala> def test[A, B](x: Option[Either[A, B]]): Either[A, Option[B]] = x.sequence
def test[A, B](x: Option[Either[A,B]]): Either[A,Option[B]]
Dylan Halperin
@dylemma
hm yeah that works as desired, at least in this REPL session, thanks!
bblfish
@bblfish:matrix.org
[m]
Li Haoyi once wrote a small library for stateful objects that were functional. I keep seeing references to this type of structure, but I can't remember the name right now.
Rob Norris
@tpolecat
There are a few ways to do this and a few things in cats and cats-effect that help. Depends on what you really need to do.
bblfish
@bblfish:matrix.org
[m]
I was thinking of using a functional stateful object in Akka, to reduce message passing. It's a long time (5 years or so) I looked at that lib, so I am not sure it's a good idea. I just wanted to check out if it fit.
But I can't find the library I had seen on his repo anymore.
Rob Norris
@tpolecat
Not super sure what you mean by functional stateful.
bblfish
@bblfish:matrix.org
[m]
yes, I can't quite remember what it did. The argument is that you could change the object that had state, and other ojbects would get the state change on using it. But somehow it was not OO...
I think there was an example using equations and variables
God it: Rx Reactive variables. His library is here.
Christopher Davenport
@ChristopherDavenport
That is not referentially transparent.
bblfish
@bblfish:matrix.org
[m]
yes, it does not look like it is :-)
I guess what I was thinking of falls probably in the space of premature optimisation. The thought was of sharing state with child actors, since On the same VM one does not really need message passing....
bblfish
@bblfish:matrix.org
[m]
(there are blog posts on how it is an anti pattern even. :-) )
Zett98
@Zett98
one dumb question but why infix operators are rarely used in cats effect docs? why almost all of the examples use for comprehensions?
Fabio Labella
@SystemFw
I don't know of a specific reason, it's a matter of taste
I personally use for very rarely, and don't really recommend it if you're starting out with IO (it clouds the picture)
Zett98
@Zett98
i thought that infix ops weren't used because of some issues or something, but it looks like with scala 3 and its for comprehensions infix ops are way to go?
or is it considered bad to write it using infix ops?
Christopher Davenport
@ChristopherDavenport
Meanwhile on the other hand I find that it fights right shift and has clearer semantic abilities for those who like it.
infix ops are fine in scala 3 and scala 2 is mostly fine as long as you don't mind brackets.
Fabio Labella
@SystemFw

or is it considered bad to write it using infix ops?

there's nothing wrong using functions (e.g. flatMap, flatTap, etc), including operators (mostly >>)

and that's even more true once you move to Resource and Stream
Christopher Davenport
@ChristopherDavenport
Stream should very rarely be in a for comprehension imo
Fabio Labella
@SystemFw
:+1:
Christopher Davenport
@ChristopherDavenport
It was more common when Resource didn't exist yet.
Fabio Labella
@SystemFw
the reason why I don't recommend for with Stream is different (and stronger) than the reason I don't recommend it with IO
with Resource, there is one place (the big resource in main where you build the world) where for is usually distinctly prettier than explicit combinators, so I'd use for if I was on my own. In a codebase shared with beginners I'd take the ugliness though and avoid for even then
(btw I doubt that this opinion on for is shared by many people :) )
Christopher Davenport
@ChristopherDavenport
I've fairly successfully explained for comprehensions to make them work for new folks(I think). But it does require walking through each step to explain what the compiler is doing and why we like the clarity it gives.
Fabio Labella
@SystemFw

(I respect that)

For me it's not about them being hard to explain, but that they make for a confused mental model

when you write in imperative style you have this def vs val dichotomy (for being and doing), and we spend a lot of time discussing how FP shifts the focus to composing programs instead, where doing is expressed through combinators
for shifts you back to to the previous dichotomy (<- vs =), and now you have three layers of abstraction to deal with
an imperative dsl is translated to explicit programs which are then translated back to an imperative program at runtime
Christopher Davenport
@ChristopherDavenport
If you can switch between the two, I don't follow how the mental model differs. <- = flatMap except for final which is <- = map. = is always equal
Fabio Labella
@SystemFw
that's a lot of layers to deal with, especially if they aren't all clear to begin with, and I found people get confused
what you describe is the translation, not the mental model
the mental model when your write imperative programs is that you can either do, which you achieve with val a = operation
or be (give a name), which you achieve with def a = operation
for has the same mental model, you can either do with <-, or be with =