Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Module for tagless-final & cats-effect #31

Open
fiadliel opened this issue May 4, 2018 · 4 comments
Open

Module for tagless-final & cats-effect #31

fiadliel opened this issue May 4, 2018 · 4 comments

Comments

@fiadliel
Copy link
Contributor

fiadliel commented May 4, 2018

Hi, would you be interested in code to make it easier to work with tagless-final style FP programming, and with cats-effect (FP effect monad)?

The current tracing API is designed for Future users, and isn't very idiomatic for FP.

I would be thinking of something like:

An API for Span

e.g.

trait Span[F[_]] {
  def spanContext: SpanContext
  def end(implicit F: Sync[F]): F[Unit]
  def addAnnotation(description: String)(implicit F: Sync[F]): F[Unit]
  def openCensusSpan: io.opencensus.trace.Span
}

An API for creating spans

e.g.

trait SpanBuilder[F[_]] {
  def rootSpan(spanName: String): F[Span[F]]
  def spanWithRemoteParent(spanName: String,
                           parentSpanContext: Option[SpanContext]): F[Span[F]]
  def spanWithLocalParent(spanName: String, parentSpan: Span[F]): F[Span[F]]
  def threadLocalSpan: F[Span[F]]
}

An API for trace propagation which is similar to Kleisli, i.e. a wrapper around Span[F] => F[A]

e.g.

class Trace[F[_], A](val underlying: Span[F] => F[A]) extends AnyVal {
  def withChildSpan(spanName: String)(
      implicit F: Sync[F],
      SpanBuilder: SpanBuilder[F]): Trace[F, A] =
    Trace(
      parentSpan =>
        F.bracket(SpanBuilder.spanWithLocalParent(spanName, parentSpan))(
          underlying)(_.end))
}

object Trace {
  def apply[F[_], A](fa: Span[F] => F[A]): Trace[F, A] = new Trace(fa)
  def apply[F[_], A](fa: Kleisli[F, Span[F], A]): Trace[F, A] = Trace(fa.run)
  def liftF[F[_], A](fa: F[A]): Trace[F, A] = Trace(_ => fa)
}

An implementation of Trace for the Sync typeclass
(this provides map, flatMap, pure, delay, etc.)

e.g.

implicit def traceSyncInstance[F[_]](implicit F: Sync[F]): Sync[Trace[F, ?]] =
  new Sync[Trace[F, ?]] {
     ...
  }
@fiadliel
Copy link
Contributor Author

fiadliel commented May 4, 2018

(I was working on this idea independently, before I found your project. It can still be done as a separate project if you don't see it fitting into your one.)

@Sebruck
Copy link
Member

Sebruck commented May 4, 2018

I love the idea and I think this is related to https://github.com/Sebruck/opencensus-scala/issues/23

Would you like to contribute somethings? Otherwise @yannick-cw and me will for sure pick this up and build something as soon as we have time.

@fiadliel
Copy link
Contributor Author

fiadliel commented May 4, 2018

I have some local code, I'll try and submit a PR in the next few days.

I'd been thinking about this for a while, coming from the position of how gRPC manages context: I'm pretty sure that we need to use lexical scope when dealing with async APIs (grpc-java uses ThreadLocal values by default, which don't interoperate well in async world, considering the various thread pools callbacks can be executed on). Monix is better than most in this area (with TaskLocal), but I don't want a solution which is restricted to that library.

I've also used New Relic quite a bit, and seen how even the best efforts to automatically instrument code by bytecode injection doesn't really work out that reliably.

@yannick-cw
Copy link
Contributor

Awesome, I was thinking in a similar direction for the api, but you seem to put in already way more thought. I am very interested in what you come up with and willing to review / support any time :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants