-
Notifications
You must be signed in to change notification settings - Fork 77
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 Local[F, Span[F]] instance where F is Kleisli[F, Span[F], *] #713
Conversation
Hmm, is the problem here? I.e. should that be asking for a Edit: even better, the We'll have to add a (low-priority?) instance for |
Hmm, in principle I like that, but I'm not sure it solves my problem. If I'm understanding you correctly, I was able to implement implicit def natchezMtlTraceViaLocal[F[_], G[_]](implicit ev: Local[F, Span[G]],
fk: MonadPartialOrder[G, F],
F: MonadCancelThrow[F],
G: MonadCancelThrow[G],
): Trace[F] and then use it by changing the launch point of my program from def foo[F[_] : MonadCancelThrow](implicit L: Local[F, Span[F]]): F[Unit] to def foo[F[_] : MonadCancelThrow, G[_] : MonadCancelThrow](implicit L: Local[F, Span[G]],
fk: MonadPartialOrder[G, F]): F[Unit] and then call it with one of foo[Kleisli[IO, Span[IO], *], IO]
foo[IO, IO] instead of one of foo[Kleisli[IO, Span[IO], *]]
foo[IO] which is obviously not as pleasant to use. Am I missing something? |
Thanks for trying that! Maybe we can use the partially-applied trick here? So that you only need to specify one of the type parameters. |
Or a bespoke encoding. trait LocalSpanAndMonadPartialOrderAndMonadCancelThrow[F[_]] {
type G[_]
def localSpan: Local[F, Span[G]]
def monadPartialOrder: MonadPartialOrder[G, F]
def monadCancelThrow: MonadCancelThrow[G]
}
object LocalSpanAndMonadPartialOrderAndMonadCancelThrow {
type Aux[F[_], G0[_]] = LocalSpanAndMonadPartialOrderAndMonadCancelThrow[F] { type G = G0 }
implicit def instance[F[_], G0[_]](
implicit ls: Local[F, Span[G0]],
mpo: MonadPartialOrder[G0, F]
mct: MonadCancelThrow[G0]
): Aux[F, G0] = new LocalSpanAndMonadPartialOrderAndMonadCancelThrow[F] {
type G = G0
def localSpan: Local[F, Span[G]] = ls
def monadPartialOrder: MonadPartialOrder[G, F] = mpo
def monadCancelThrow: MonadCancelThrow[G] = mct
}
}
implicit def natchezMtlTraceViaLocal[F[_]](
implicit ev: LocalSpanAndMonadPartialOrderAndMonadCancelThrow[F],
F: MonadCancelThrow[F],
): Trace[F] = ??? |
…ram[F[_]: MonadCancelThrow](implicit L: Local[F, Span[F]]): F[Unit]`
I think I got that working on Scala 2.13 and 3, but I haven't been able to get it to infer on 2.12. I also had to change the app code method definition to def foo[F[_]: LocalSpanAndMonadPartialOrderAndMonadCancelThrow : MonadCancelThrow]: F[Unit] which still doesn't feel ideal since it's a new thing that would have to be explained and understood. I added a |
Hm I see 🤔 in that case, I wonder if the partially-applied trick would be better. I think this boils down to:
Pick 2. Suffice to say, I see the merits of your original proposal 😅 Still, I think the implementation should be in terms of |
…rio being contemplated
…i[F, Span[F], *]]
Maybe I've been looking at this too long and I'm just missing something, but I updated the implementation to use (I did take it a lot farther on another branch, introducing a new thing I called |
Oh crap, no I think it's just me 😬 as soon as you mentioned I previously learned this in #448, but seems I forgot 🤦 So sorry for the wild goose chase, thanks for trying all my suggestions 😕 |
No worries! Given that, do you still think the changes in dcf71de (that introduced |
Let me revise this (and hopefully get it right this time :) — the implementation should be in terms of To be more concrete, can we implement this roughly signature? def natchezMtlTraceForLocal[F[_], G[_]](implicit
local: Local[F, Span[G]],
F: MonadCancel[F, _],
lift: G ~> F,
lower: F ~> G
): Trace[F] = ??? and then we just do something like this? implicit def localSpanForKleisli[F[_]: MonadCancel] = natchezMtlTraceForLocal[Klesli, F] and another one for the case where |
I think that signature maybe misses the point of this PR? This is meant to handle the situation where I have a method def doSomethingInNewRoot[F[_]](entryPoint: EntryPoint[F])(implicit L: Local[F, Span[F]]) and I want to call it with In this situation, I don't need a |
Gah 🤦 yes it does. I'm ... going to shut my mouth now 😅 the call site is |
When fulfilling a effect-polymorphic function demanding an implicit
Local[F, Span[F]]
with[F[_], A] =>> Kleisli[F, Span[F], A]
, the implicit instances provided by cats-mtl don't quite fit. The code demandsbut
Local.baseLocalForKleisli
returns(i.e., the
E
parameter is wrong), andLocal.localForKleisli
requires an implicitwhich we don't have.
AFAICT, it's safe to (effectively)
imap
theLocal[Kleisli[F, Span[F], *], Span[F]]
returned byLocal.baseLocalForKleisli
usingKleisli.liftK
andKleisli.applyK
.Kleisli.liftK
will turnSpan[F]
intoSpan[Kleisli[F, Span[F], *]]
, which, when run, will ignore anySpan[F]
s passed to eachKleisli[F, Span[F], *]
. We take advantage of that by immediatelymapK
ing it back toSpan[F]
, applying a no-op span that we know will never actually be used.(Alternatively, if there's a better way to do this, let me know and I'll change this PR to document that method instead.)