Closed t3hmrman closed 6 years ago
Not sure why you need a typeclass here, this seems to complicate things unnecessarily.
The problem at hand is that your startFn
has type SpanOpts -> IO Span
, but Tracer
expects MonadIO m => SpanOpts -> m Span
. liftIO startFn
should make the types align.
Hey @kim thanks for the speedy response. The typeclass has to do with how my app is structured -- I use it to separate the functionality of large-ish components. This typeclass is specific but I think it's helpful when others aren't so specific (and only have one implementation)
I tried to lift the function earlier and it didn't work but I will try again, IIRC the m
is wasn't being deduced as the same m
. I'll give it another shot
Maybe I'm still doing it wrong, here's the code changed:
-- Create Standard ENV for Tracer to use, create standard tracer from the env mkTracer :: MonadIO m => IO (SpanOpts -> m Span)
mkTracer = newStdEnv sampling
>>= \env -> pure (\spanOpts -> liftIO ((stdTracer env) spanOpts))
-- Update Tracer with a JaegerAgentReporter (instead of log based one), produce the middleware
augmentTracerWithJaeger :: MonadIO m => Tracer -> (SpanOpts -> m Span) -> IO Tracer
augmentTracerWithJaeger tracer startFn = withJaegerAgent jaegerOptions (\j -> return (tracer { tracerStart=startFn
, tracerReport=jaegerAgentReporter j
}))
It's a little messy, but I'm super careful to reassemble into a type that matches, but it's unclear (to the compiler) that that m
is the same as the m
in Tracer
. I enabled ScopedTypeVariables
, no difference -- Is it possible this is broken for me since I'm inside a typeclass function?
Here's the error that still emerges:
src/Tracing/TracingBackend.hs:92:118-124: error: …
• Couldn't match type ‘m’ with ‘m1’
‘m’ is a rigid type variable bound by
the type signature for:
augmentTracerWithJaeger :: forall (m :: * -> *).
MonadIO m =>
Tracer -> (SpanOpts -> m Span) -> IO Tracer
at /path/to/src/Tracing/TracingBackend.hs:91:38
‘m1’ is a rigid type variable bound by
a type expected by the context:
forall (m1 :: * -> *). MonadIO m1 => SpanOpts -> m1 Span
at /path/to/src/Tracing/TracingBackend.hs:92:97
Expected type: SpanOpts -> m1 Span
Actual type: SpanOpts -> m Span
• In the ‘tracerStart’ field of a record
In the first argument of ‘return’, namely
‘(tracer
{tracerStart = startFn, tracerReport = jaegerAgentReporter j})’
In the expression:
return
(tracer
{tracerStart = startFn, tracerReport = jaegerAgentReporter j})
• Relevant bindings include
startFn :: SpanOpts -> m Span
(bound at /path/to/src/Tracing/TracingBackend.hs:92:42)
augmentTracerWithJaeger :: Tracer
-> (SpanOpts -> m Span) -> IO Tracer
(bound at /path/to/src/Tracing/TracingBackend.hs:92:11)
src/Tracing/TracingBackend.hs:103:34-41: error: …
• Ambiguous type variable ‘m0’ arising from a use of ‘mkTracer’
prevents the constraint ‘(MonadIO m0)’ from being solved.
Probable fix: use a type annotation to specify what ‘m0’ should be.
These potential instances exist:
instance [safe] MonadIO IO -- Defined in ‘Control.Monad.IO.Class’
...plus 18 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the second argument of ‘(>>)’, namely ‘mkTracer’
In the first argument of ‘(>>=)’, namely
‘liftIO (logMsg t INFO "Instrumenting application...") >> mkTracer’
In the first argument of ‘(>>=)’, namely
‘liftIO (logMsg t INFO "Instrumenting application...") >> mkTracer
>>= augmentTracerWithJaeger (Tracer undefined undefined)’
Hey @t3hmrman, it's a bit hard to follow because I can't take your code and have a machine typecheck it for me 😬
That said, I guess the signature of augmentTracerWithJaeger
should stay the same as before (with concrete IO
, the MonadIO
constraint is what makes the m
"rigid"), and then use liftIO
in the function body. Apart from that, it doesn't seem like you need to set the tracerStart
field - it can only meaningfully be the stdTracer
currently. Just make a tracer like here and pass that to augmentTracerWithJaeger
, where you only update the tracerReport
field.
Hope this helps.
hey @kim thanks I think that does help -- I'll keep trying at it. The code you linked too should be pretty helpful. I'm basically trying my hardest not to have to set tracerStart
.
Thanks for your help!
After some more fiddling I got it to type check!
traceApplication t app = if appWideTracingEnabled then instrumentApp app else pure app
where
jOpts = tracingSettings t
appWideTracingEnabled = tracingAppWide $ tracingCfg t
sampling = probSampler $ tracingSamplingRate $ tracingCfg t
propagation = jaegerPropagation
defaultReporter :: FinishedSpan -> IO ()
defaultReporter = makeLoggerReporter t
-- Create Standard ENV for Tracer to use, create standard tracer from the env
mkTracer :: MonadIO m => m Tracer
mkTracer = newStdEnv sampling
>>= \env -> liftIO (pure (Tracer (stdTracer env) undefined))
-- Update Tracer with a JaegerAgentReporter (instead of log based one), produce the middleware
augmentTracerWithJaeger :: Tracer -> IO Tracer
augmentTracerWithJaeger tracer = withJaegerAgent jOpts (\j -> return (tracer { tracerReport=jaegerAgentReporter j }))
-- Use the created tracer to make the middleware
makeMiddleware :: Application -> Tracer -> IO Application
makeMiddleware app tracer = pure (opentracing tracer propagation (\activeSpan -> app))
-- Instrument an actual application with the agent reporter
instrumentApp :: Application -> IO Application
instrumentApp app = liftIO (logMsg t INFO "Instrumenting application...")
>> mkTracer
>>= augmentTracerWithJaeger
>>= makeMiddleware app
>>= pure
Hopefully it will help anyone else that finds this issue. I think I did two key thigs that might have helped:
ScopedTypeVariables
(I think it was binding the m
to my local function, ignoring the constraint in Tracer.hs
)OpenTracing.Standard
(before I was only selectively importing bits of it)
I'm trying to get the
WAI
integration working, and I'm almost there but I can't create the last bit I need, aTracer
value.I have the functions I want the tracer to run, but due to the type variable on tracer's definition (I think), haskell gets confused about which type to use. Even when I specialize to
IO
(instead of using aforall m. MonadIO m
), I get this type error:Here's what the code looks like:
To me it seems like haskell is expecting the IO to be an
m
(as bound by tracer's definition), and cannot get over the requirement.I've tried setting
ScopedTypeVariables
(and abstracting theMonadIO m
type in the typeclass code too), but it still won't convince Haskell that bothm
s are the same, I just get the same error withm1
vsm
instead ofm
andIO
.I've looked at
Backends.hs
and I'm honestly not sure how it works when you're in the IO monad yet manage to create aTracer
properly.