Skip to content
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

Deprecate implicit materialization of Slf4jFactory #683

Conversation

armanbilge
Copy link
Member

See #681 (comment). Since there is not one canonical LoggerFactory implementation, they should be passed explicitly, not implicitly.

Closes #681.

def apply[F[_]: Slf4jFactory]: Slf4jFactory[F] = implicitly

def create[F[_]: Sync]: Slf4jFactory[F] = new Slf4jFactory[F] {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would have been more convenient as an apply method, but unfortunately that's been squatted :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good this way! cats.effect.std.Console is also instatiated using make instead of apply. Maybe we should rename this to make rather than create :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree now that we decided we are not totally deprecating implicit (otherwise, the apply was dead deprecated weight).

Regarding naming: I chose create based on this.

def create[F[_]: Sync](implicit name: LoggerName): F[SelfAwareStructuredLogger[F]] =

But I think make is good too 🤔 and maybe better to keep it independent of create.

@lorandszakacs
Copy link
Member

lorandszakacs commented Aug 23, 2022

To me LoggerFactory is akin to capability traits like cats.effect.std.Console and fs2.io.Network, and can be passed around implicitely as here in skunk.

It's just that I would highly encourage explicit instantiation of these by the user, and then allow them to chose if they wish to pass implicitely or explicitely in their own app :). I think the status quo is that Console is fine to be passed implicitely, right? If so, I'd reword the deprecation message on all methods to indicate that explicit instantation of a LoggerFactory of the user's choosing + the calling of methods directly on said LoggerFactory.

I'd also prefer to see this paired with some docs that showcase the explicit use of Slf4jFactory.create and passing it along to some classes (both implicitely and explicitely). Basically something like:

// the assorted imports, also code does not compile
object Main extends IOApp.Simple {
  override def run: IO[Unit] = for {
     implicit0(lf: LoggerFactory[IO]) = Slf4jFactory.create[IO]
     // other capabilities like cats.effect.std.{Console, Random}, etc here.
     app <- instantiateApp[IO]
  } yield ()
  
  type MyApp = Nothing

  def instantiateApp[F[_]: LoggerFactory]: F[MyApp] = 
   for {
      // many others in F
      someClass = new SomeClass[F]()
      // many others in F
   } yield ???
}

class SomeClass[F[_]](implicit loggers: LoggerFactory[F]){
   private val logger = loggers.getLogger //explicit call the LoggerFactory passed along by the user, even if implicit
}

LE: paired with updating this section of the docs which does rely on these implicits.

@lorandszakacs
Copy link
Member

Maybe we should make some kind of integrative typelevel doc that showcases a pattern of instantiating capabilities available in the ecosystem and link that as an example? 😅

Since it would serve well to be consistent across everything under org.typelevel and people would also have a handle on why we do something like this in log4cats, fs2, cats-effect, etc.

@armanbilge
Copy link
Member Author

armanbilge commented Aug 23, 2022

To me LoggerFactory is akin to capability traits like cats.effect.std.Console and fs2.io.Network, and can be passed around implicitely as here in skunk.

I definitely agree those examples can be passed around implicitly :) that's because they have canonical implementations. Meaning that there is essentially only one true implementation.

  • are you aware of any library providing an implementation of Console besides the one in Cats Effect? What would such an implementation do?
  • Network is in fact sealed and therefore only FS2 can provide the one and only canonical implementation

This is not the case for LoggerFactory, and indeed would be quite inconvenient if it was :)

The fact that there are legitimate fears in #681 (review) that the wrong/unexpected implementation could be passed implicitly reinforces that this should be explicit.


I definitely agree the docs can/should be improved (indeed CI is failing about that). But first let's make sure we're all on the same page with this change :)

@armanbilge
Copy link
Member Author

Ah, your comment in #681 (comment) clarifies things for me.

But I do not agree with encouraging people to not pass LoggerFactory implicitly, only with its automagic implicit creation.

That seems reasonable to me: we'd deprecate just this method and nothing more.

implicit def loggerFactoryforSync[F[_]](implicit F: Sync[F]): Slf4jFactory[F] =

Happy to hear other's thoughts :)

@lorandszakacs
Copy link
Member

The fact that there are legitimate fears in #681 (review) that the wrong/unexpected implementation could be passed implicitly reinforces that this should be explicit.

Yes, but the fear is accidentally getting a new LoggerFactory created implicitely, not that it is passed around implicitely. I detailed the reasoning on this comment in #681. I'll copy paste the relevant bits.

At the end of the day how many users will have more than one instance of a LoggerFactory in their codebase? And if they do, there always various ways of newtype-ing, or subclassing it, etc. to solve the problem. For large codebases with literally hundreds of classes 90% of which do logging users will probably pass these around implicitly.

that's because they have canonical implementations

Well, there are good implementations of the capabilities. And I definitely would not be the one to try to implement a fs2.io.Network differently 😅. But they are hardly canonical in the way implementing Monad for Option is.

@lorandszakacs
Copy link
Member

That seems reasonable to me: we'd deprecate just this method and nothing more.

Then we are in agreement 🥳

@armanbilge armanbilge changed the title Deprecate implicit use of LoggerFactory Deprecate implicit materialization of Slf4jFactory Aug 23, 2022
docs/index.md Outdated Show resolved Hide resolved
@bplommer
Copy link
Contributor

To repeat my comment in #681 (comment) - Why not provide this as a convenience but in an implicits namespace, like we do with (for example) the cats-effect global runtime? That way it's not just "floating around out there" but it's available in a convenient form when needed.

override def fromSlf4j(logger: JLogger): F[SelfAwareStructuredLogger[F]] =
Slf4jLogger.fromSlf4j(logger)
}
@deprecated("Use Slf4jFactory.create[F] explicitly", "2.5.0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version needs to be bumped to 2.6.0 now 😅

@lorandszakacs
Copy link
Member

To repeat my comment in #681 (comment) - Why not provide this as a convenience but in an implicits namespace, like we do with (for example) the cats-effect global runtime? That way it's not just "floating around out there" but it's available in a convenient form when needed.

@danicheg what do you think about this approach?

I'm fine going forward with this even without convenience .implicits import. But I'm not against it.

@danicheg
Copy link
Member

To repeat my comment in #681 (comment) - Why not provide this as a convenience but in an implicits namespace, like we do with (for example) the cats-effect global runtime? That way it's not just "floating around out there" but it's available in a convenient form when needed.

@danicheg what do you think about this approach?

I think that Runtime abstraction and providing instances for LoggerFactory through the implicit scope – differ in their nature. But I'm biased in this question. I don't like the approach of using logging via capabilities.

@lorandszakacs
Copy link
Member

Merging and we'll defer the discussion about implicit LoggerFactory when the ecosystem reaches more of a consensus about implicit capabilities in general 😅

@lorandszakacs lorandszakacs merged commit 5e239db into typelevel:main Sep 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants