Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • 18:50
    SethTisue commented #9674
  • 18:50
    SethTisue demilestoned #9674
  • 16:54
    som-snytt closed #9674
  • 16:46
    som-snytt synchronize #9674
  • 10:10
    bjornregnell commented #1248
  • 10:09
    bjornregnell commented #1248
  • 10:08
    bjornregnell commented #1250
  • 10:04
    pjfanning synchronize #2086
  • 10:00
    bjornregnell commented #1248
  • 09:59
    pjfanning opened #2086
  • 09:22

    Philippus on main

    Fix typo: eather -> either Merge pull request #2085 from m… (compare)

  • 09:22
    Philippus closed #2085
  • 08:34
    myyk opened #2085
  • 05:03
    scala-jenkins milestoned #9674
  • 05:03
    som-snytt opened #9674
  • 05:02
    retronym labeled #9673
  • 05:01
    retronym review_requested #9673
  • 05:01
    scala-jenkins milestoned #9673
  • 05:01
    retronym opened #9673
  • 01:41
    SethTisue edited #1433
Derek Wickern
@dwickern
and a cancellable traverse, etc
trepidacious
@trepidacious
It's funny that the even on a VM there isn't a safe way to stop a thread or something like a thread
Martijn Hoekstra
@martijnhoekstra
you will probably always need cooperation
Seth Tisue
@SethTisue

whats the best way to cancel a future in scala?

relevant: https://viktorklang.com/blog/Futures-in-Scala-protips-6.html

Fabio Labella
@SystemFw

@trepidacious

or something like a thread

Depends on what you consider "something like a thread"

a cats-effect Fiber can be interrupted
although obviously you are still limited by the JVM, i.e. if you build your stuff as F.delay { huge block } you still can't do much about it
trepidacious
@trepidacious
@SystemFw Ah that's cool, I'll have a look. I was thinking about the huge block case - I guess if the block is pure, it should theoretically be possible to just stop it dead without problems, but there's no actual way of doing that?
Martijn Hoekstra
@martijnhoekstra
if the block is pure, it should theoretically be possible to just stop it dead
it's very difficult to define what "it" is in that sentence
Fabio Labella
@SystemFw
very fine grained interruption needs fine grained control of evaluation as well
for example the Haskell runtime uses the points in which the code yields to the garbage collector (which happens often due to the interaction with laziness) as yield points for concurrency as well
once you start thinking about interruption from an implementor point of view
which we had to do a lot in cats-effect
you'll see that "just stop it" is one of the those things that seems very straightforward on paper, but that it isn't at all
Josh
@joshlemer
Did you guys look at "pull-based" futures that Rust has?
Fabio Labella
@SystemFw
any link?
the implementation of interruption in cats-effect currently checks the interrupt flag at async boundaries
which are introduced by several operations
Marcin Sokrates
@marcinsokrates_twitter
Turning asynchronous OS signals into exceptions is possible, you can do it in C++/GCC, the JVM does it for NPEs I think, but I'm sure there are lots of things to consider while doing that
Fabio Labella
@SystemFw
there's also another whole bunch of complexity that comes with the interaction with resource safety and finalisers
Josh
@joshlemer
hmm having trouble finding a good article
Fabio Labella
@SystemFw
but that being said, both fs2 and cats-effect, and Monix ( as well as Twitter futures iirc) are cancelable
Josh
@joshlemer
Basically, Rust futures are pull- (aka poll-) based not push-based, which means that to cancel it, children just have to stop polling their parents
Fabio Labella
@SystemFw
I'm not sure how exactly that maps to IO and/or Stream (Streamis pull-based for example)
I think IO will map to pull-based as well
trepidacious
@trepidacious
@SystemFw @martijnhoekstra Yes, it's pretty clear it's one of those areas where a lot of complexity is covered up by a short description :)
Fabio Labella
@SystemFw
@joshlemer one immediate question for example is deciding when do you want to check the interrupt flag
trepidacious
@trepidacious
@SystemFw @martijnhoekstra Makes me think of real-time C where you handle these things in processor interrupts to be able to do non-cooperative threading, I have no idea what the VM equivalent of that is. I guess there is also a pretty significant tradeoff between latency and throughput.
Fabio Labella
@SystemFw
if you're interested I'd read about how Haskell does it. The fact the runtime controls both evaluation of pure code as well as IO and concurrency means you have a lot more control about what you can do
srimugunthan
@srimugunthan
trepidacious
@trepidacious
@SystemFw I should stop being surprised when it turns out Haskell does something more elegantly :)
Maybe I'll use it one day :)
Marcin Sokrates
@marcinsokrates_twitter
Oh, the JVM actually has Thread.stop(), but they deprecated it for fear of inconsistent state in interrupted lock-protected code sections
trepidacious
@trepidacious
@marcinsokrates_twitter Yes that's what I was thinking of. Maybe if you have pure code that definitely doesn't use locks you could use it... ;)
Marcin Sokrates
@marcinsokrates_twitter
I wish they just added a mechanism for suspending interrupts in critical sections instead
Josh
@joshlemer
@SystemFw cancelling, here, ends up meaning to just not poll any more
the work is driven through the polling method. When a child is polled, it polls its parent etc
if no polling happens, no work gets done
Fabio Labella
@SystemFw

cancelling, here, ends up meaning to just not poll any more

@joshlemer I don't think that says the whole story, let me expand, because I feels like this is similar to how cats-effect (or Monix) work

Josh
@joshlemer
although I wonder if this means they are not stack-safe...
Fabio Labella
@SystemFw

@joshlelemer
basically the way IO works is by constructing a datatype, which contains all the info to then interpret the IO (e.g. the constructor for side effecting code takes an => A, the constructor for flatMap takes IO[A] and A => IO[B], and they get stored in cases of an ADT). This datatype is then interpreted by the runloop once you unsafeRunSync at the end of world (typically the top of the call chain, in main).


The way the interpreter works is basically a while loop over two vars, the current IO[Any], and a Stack[Any => IO[Any]], you get the current, dispatch on its constructors and do what you need to do (e.g. evaluate the => A) and keep going with the next. This is a powerful approach because manipulating the stack allows you to implement complex features (e.g. you can shift to a trampoline every n calls to solve the stack safety problem you would otherwise have). It's also similar to what you mean by "polling": if you don't fetch the next thing off the stop, things just stop executing.


However, the crucial detail is that this by itself is not Interruption, it's suspension: i.e. you have defined a mechanism for an IO to suspend itself and give control back to the main thread (which is how fiber level concurrency is implemented), but interruption means that another IO needs to stop you.
So you need to have a shared (volatile, at the very least) variable which is the interrupt flag, the other IO sets it when it wants to interrupt you, and in your runloop you need to check this variable, and only then suspend yourself when the variable is true.


And that indeed brings up back to the initial question: how often do you check it? And there are different choices: you can check it at every flatMap, that's very fine grained but also quite expensive. The current choice in IO is to check it at every async boundary. And as I said, there's another whole bunch of complexity (in library code, not user code) to make sure things are resource safe and finalisers are registered and run correctly, in all scenarios

RoelofWobben
@RoelofWobben
How do I refractor the whole flatMap thing here :
sealed trait Expression{

  def eval: Sum[String, Double] = this match {

    case Addition(left, right) => left.eval flatMap { x => right.eval flatMap(y => Success(x + y))}
    case  Subtraction(left, right) => left.eval flatMap { x => right.eval flatMap(y => Success(x - y))}
}
Gavin Bisesi
@Daenyth
what monad is left/right/Success in?
RoelofWobben
@RoelofWobben
Sorry, monads are not explained yet. That will be in the cats book
RoelofWobben
@RoelofWobben
@Daenyth
Bastin Orar
@bastinOrar_twitter
I want a link to any beginner ML post with scala
Rob Norris
@tpolecat
@RoelofWobben assuming your result type acts like Either you can probably say for { x <- right.eval; y <- left.eval } yield x + y for example. That's probably where they are headed.
Gavin Bisesi
@Daenyth
@tpolecat I would have said that also except that he has a flatMap there on Success, so the yield is wrong
but that feels weird