-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Tracer.translate
#264
Add Tracer.translate
#264
Conversation
@@ -172,6 +174,8 @@ trait Tracer[F[_]] extends TracerMacro[F] { | |||
*/ | |||
def noopScope[A](fa: F[A]): F[A] | |||
|
|||
def mapK[G[_]: Sync](fk: F ~> G, gk: G ~> F): Tracer[G] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here the type constraint must satisfy SpanRunner
and TraceScope
from the java-trace package.
@@ -172,6 +174,8 @@ trait Tracer[F[_]] extends TracerMacro[F] { | |||
*/ | |||
def noopScope[A](fa: F[A]): F[A] | |||
|
|||
def mapK[G[_]: Sync](fk: F ~> G, gk: G ~> F): Tracer[G] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you need both F ~> G
and G ~> F
then actually it's an imapK
😜 also sometimes the method is called translate
.
Also, Sync
is a very strong constraint, and I don't think it's necessary. Once you have F ~> G
and G ~> F
then you should be able to derive Sync[G]
from Sync[F]
.
See a similar idea in:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
translate
is more suitable indeed.
Once you have F ~> G and G ~> F then you should be able to derive Sync[G] from Sync[F].
I didn't know that! I will take a look at the example, looks interesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you need F ~> G and G ~> F, then the invariant nature means almost all interesting transformers are eliminated from use, OptionT, EitherT, Kleisli, IorT, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random thought: you might be able to do this with something weaker than F ~> G
+ G ~> F
, like Haskell's UnliftIO
. That only gets you Kleisli
while staying sensible, but it's something at least.
Prototype: Prillan@355c68d
|
||
object TracerImpl { | ||
|
||
private def liftSync[F[_], G[_]]( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! We might consider upstreaming these methods to Cats Effect is they are generally useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I was thinking about too
# Conflicts: # core/trace/src/main/scala/org/typelevel/otel4s/trace/Tracer.scala
I remembered the A few notes:
|
@NthPortal - would you like to take a look? These changes intersect with your #261 PR. |
implicit final class SpanBuilderSyntax[F[_]]( | ||
private val builder: SpanBuilder[F] | ||
) extends AnyVal { | ||
|
||
def translate[G[_]](fk: F ~> G, gk: G ~> F)(implicit | ||
F: MonadCancel[F, _], | ||
G: MonadCancel[G, _] | ||
): SpanBuilder[G] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the reason for this so that the primary API doesn't contain MonadCancel
?
(side note: I thought it needed MonadCancelThrow
specifically)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, do we really need both MonadCancel
for F
and G
? If we have both fk
and gk
seems like we should be able to derive it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the reason for this so that the primary API doesn't contain MonadCancel?
We need it in several places, for example: https://github.com/typelevel/otel4s/pull/264/files#diff-36bdd6d74392dca0ddc0d9c6312f06c778e77a49e87fbc5c6fc966c936171f1cR208
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, do we really need both
MonadCancel
forF
andG
? If we have bothfk
andgk
seems like we should be able to derive it.
Indeed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need it in several places
oops, I worded that question really poorly. what's the reason it's an extension method and not in the trait?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That way I do not need to deal with recursive definitions, e.g.:
trait Tracer[F[_]] { self =>
def mapK[G[_]](fk: F ~> G): Tracer[G] =
new Tracer {
...
def mapK[Q[_]](fk1: G ~> Q): Tracer[Q] =
self.mapK(fk.andThen(fk1)))
}
}
I like your approach with a dedicated type MappedK
way more.
I actually worked on a solution to both of these problems while working on #273; perhaps it can be combined with this PR for a union of the features of the two. I haven't quite finished the scaladocs, but I've pushed up my work as #284 |
FWIW, cats-tagless has some laws for FunctorK and InvariantK that might apply here. (It'd be nice to also provide no-import instances for those typeclasses where possible, but I understand that cats-tagless may not be stable enough to include as a dependency.) |
def translate[G[_]](fk: F ~> G, gk: G ~> F): Res[G] = | ||
new Res[G] { | ||
def span: Span[G] = res.span.mapK(fk) | ||
def trace: G ~> G = res.trace.andThen(fk).compose(gk) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a reason not to use Res.apply
for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trace
at least should probably be a val
because if you don't use it, nothing ends up traced, so it will be used in almost all cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, Res.apply
fits better
I like |
@iRevive how do you want to reconcile our two PRs? do you want me to rewrite the parts of mine on top of yours, or yours on top of mine, or something else? |
@NthPortal I prefer your version. It's more flexible, in my opinion. I'll close this PR in favor of #284. |
Proof of concept for #261