Zio: ZIO Execution Traces: Remove "guts" of ZIO from execution traces

Created on 28 Nov 2019  路  4Comments  路  Source: zio/zio

ZIOFn is not being consistently used in all new combinators. The code requires a detailed inspection to identify and cleanup all combinators so that ZIO internals do not appear in any execution trace.

If anyone wants to take on this issue, @neko-kai or myself can provide detailed guidance.

enhancement good first issue

Most helpful comment

@jdegoes I'd like to give this a shot.

All 4 comments

@jdegoes I'd like to give this a shot.

@jaliss

So here is a possible implementation for zipRight (AKA *>):

self.flatMap(_ => that)

The problem with this implementation is that ZIO gets stack trace information from closures. And there is a closure in this function (_ => that). So ZIO will find that closure and put the trace inside the execution trace of the fiber running the effect. That leads to a lot of lines in an execution trace being taken up by the insides of ZIO.

Worse than that, ZIO won't have any stack trace information on the closure passed to *> that was created in user-land code. This closure is the "by name" parameter that; Scala will create a Function0 to wrap it wherever the user invokes the method *>, which will have useful information on where in the user's code the function *> was called. Ideally, this useful information would appear in execution traces.

To solve both problems, ZIO has "wrapper functions" that let us create things like _ => that, but they are tagged in a special way that removes tracing information; in addition, these "wrapper" functions can point to inner closures. So we can point to the Function0 created for that, and add that to an execution trace.

Here is a wrapper class for ZipLeft:

final class ZipLeftFn[R, E, A, B](override val underlying: () => ZIO[R, E, A]) extends ZIOFn1[B, ZIO[R, E, B]] {
    def apply(a: B): ZIO[R, E, B] =
      underlying().as(a)
  }

You can see it holds a reference to underlying, which tells ZIO where to get the execution trace from; and then because it extends ZIOFn1, the ZIOFn1 itself will not be added to the stack trace.

Does this make sense?

If so, the next step is to look for ZIO methods that are just creating raw closures, which create unnecessary stack traces, and which do not point to the underlying closures; and then to wrap them with ZIOFn1 or a new helper class derived from that.

@jdegoes, @kitlangton and I are going to take a look into ways we can possibly get rid of ZioFn (relates to #916 ), or, failing that, work on this one and for bonus points, maybe see if we can figure out a way to test if ZioFn isn't used where it should be. To facilitate this, I just want to make sure I understand how the stack and execution traces are supposed to work - effectively, if it's the user creating the closure, we want it to show up in the stack/execution traces. If it's the library that's creating the closure, then we don't want it to show up. Are there any cases where we want a library created closure to show up?

I completely forgot I said I was going to work on this ... I'm sorry! :-(. @robmwalsh about your question: my understanding is that we only want user code shown in the stack trace but @jdegoes can confirm.

Was this page helpful?
0 / 5 - 0 ratings