{"id":15159350,"url":"https://github.com/oleg-py/meow-mtl","last_synced_at":"2025-04-09T20:15:43.992Z","repository":{"id":37431933,"uuid":"130396807","full_name":"oleg-py/meow-mtl","owner":"oleg-py","description":"Next Level MTL for Scala","archived":false,"fork":false,"pushed_at":"2023-01-07T22:35:43.000Z","size":114,"stargazers_count":163,"open_issues_count":2,"forks_count":21,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-09T20:15:38.129Z","etag":null,"topics":["cats","functional-programming","mtl","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oleg-py.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-20T17:58:37.000Z","updated_at":"2025-02-21T20:27:12.000Z","dependencies_parsed_at":"2023-02-08T03:03:22.050Z","dependency_job_id":null,"html_url":"https://github.com/oleg-py/meow-mtl","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleg-py%2Fmeow-mtl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleg-py%2Fmeow-mtl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleg-py%2Fmeow-mtl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleg-py%2Fmeow-mtl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oleg-py","download_url":"https://codeload.github.com/oleg-py/meow-mtl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103872,"owners_count":21048245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cats","functional-programming","mtl","scala"],"created_at":"2024-09-26T21:05:11.837Z","updated_at":"2025-04-09T20:15:43.957Z","avatar_url":"https://github.com/oleg-py.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# meow-mtl\n![Maven central](https://img.shields.io/maven-central/v/com.olegpy/meow-mtl-core_2.13.svg?style=flat-square)\n\nA catpanion library for [cats-mtl] and [cats-effect] providing:\n\n- Easy composition of MTL-style functions\n- MTL instances for cats-effect compatible datatypes (e.g. `IO`) and monix TaskLocal\n- Conflict-free implicits for sub-instances (e.g. `Stateful` =\u003e `Monad`)\n\nAvailable for Scala 2.12 and 2.13, for Scala JVM and Scala.JS (1.x)\n\n```scala\n// Use %%% for scala.js or cross projects\n// Classy lenses derivation (requires shapeless)\nlibraryDependencies += \"com.olegpy\" %% \"meow-mtl-core\" % \"0.5.0\"\n// MTL instances for cats-effect Ref and effectful functions\nlibraryDependencies += \"com.olegpy\" %% \"meow-mtl-effects\" % \"0.5.0\"\n// MTL instances for TaskLocal\nlibraryDependencies += \"com.olegpy\" %% \"meow-mtl-monix\" % \"0.5.0\"\n```\n\n\nInspired by [Next-level MTL talk][mtl-talk] and discussions on cats gitter.\n\nYou can also see demonstration of techniques this library enables in [a post](https://typelevel.org/blog/2018/08/25/http4s-error-handling-mtl.html) or [a talk](https://www.youtube.com/watch?v=gYnbOUGpWK0) by [Gabriel Volpe](https://github.com/gvolpe)\n\n### Quick Example\n```scala\n\ntype Headers = Map[String, String]\ncase class User(name: String)\ncase class AuthedRequest(headers: Headers, user: User)\n\ndef greetUser[F[_]: Functor](implicit F: Stateful[F, User]): F[String] = {\n  F.get.map(user =\u003e s\"Hello, ${user.name}\")\n}\n\ndef addRequestIdHeader[F[_]: Sync](implicit F: Stateful[F, Headers]): F[Unit] =\n  for {\n    id \u003c- Sync[F].delay(UUID.randomUUID().toString)\n    _  \u003c- F.modify(_ + (\"X-Request-ID\" -\u003e id))\n  } yield ()\n```\n\nNow, if you had `AuthedRequest` as a state, that *should* mean that you\nhave a state of `User` and `Headers` too. This library allows you to call these\nfunctions directly:\n\n```scala\nimport com.olegpy.meow.hierarchy._\n\ndef handleGreetRequest[F[_]: Sync](implicit F: Stateful[F, AuthedRequest]) =\n  for {\n    _ \u003c- addRequestIdHeader[F]\n    r \u003c- greetUser[F]\n  } yield r\n```\n\nTo get that `Stateful` instance, it's possible to use `StateT`\ntransformer. But meow-mtl allows you to use `Ref` from cats-effect\ninstead, yielding better performance. So at the edge of your application\nit is possible to do this:\n\n```scala\nimport com.olegpy.meow.effects._\n\ndef handleRequest: IO[String] =\n  for {\n    ref \u003c- Ref[IO].of(AuthedRequest(Map(), User(\"John\")))\n    res \u003c- ref.runState { implicit monadState =\u003e\n      handleGreetRequest[IO]\n    }\n  } yield res\n```\n\n## Classy optics and MTL composition\n\nPrimary feature of meow-mtl is enabling boilerplate-free composition of\nfunctions using cats-mtl typeclasses, in cases where instance clearly\neither contains necessary fields (like State example above) or can be\nconverted to a necessary type. For example, it's possible to narrow\ntype of `MonadError` from `Throwable` to a custom exception type:\n\n```scala\ncase class MyException(msg: String) extends Throwable\n\ndef handleOnlyMy[F[_], A](f: F[A], fallback: F[A])(implicit F: MonadError[F, MyException]) =\n  f.handleErrorWith(_ =\u003e fallback)\n\n\nval io: IO[Int] = ???\nhandleOnlyMy(io, 42)\n```\n\nThis is witnessed by `Lens` and `Prism` optics that meow-mtl generates\nwhen you try to make a call to such method.\n\nAs another neat example, generated typeclasses can be used as ad-hoc lenses\n\n```scala\ncase class Part(int: Int)\ncase class Whole(part: Part)\n\ndef modify[F[_]: Stateful[?[_], Whole]] =\n  Stateful[F, Part].set(Part(42)) // automatically \"zooms\" into Whole.part\n```\n\n### High-level API: automatic derivation\n\nAll automatic derivation requires is a single import:\n\n```scala\nimport com.olegpy.meow.hierarchy._\n```\n\nThis needs to be done in every file where your call requires deriving an\ninstance.\n\nSupported typeclasses:\n\n| Typeclass         | Required optic |\n|-------------------|----------------|\n| ApplicativeError  | Prism          |\n| Handle | Prism          |\n| MonadError        | Prism          |\n| Raise      | Prism          |\n| Tell       | Prism          |\n| Ask    | Lens           |\n| Local  | Lens           |\n| Stateful        | Lens           |\n\n#### IMPORTANT!\n\nDon't use `cats.mtl.implicits._` or `cats.mtl.hierarchy.base._` imports.\nImport `cats.mtl.instances.all._` and `cats.mtl.syntax.all._` if you\nneed it.\n\nFailure to do this will result in ambiguous implicit instances.\n\nIn cats-mtl 0.4.0 hierarchy has been mostly replaced by subtyping. The\nremaining hierarchy imports will possibly be [phased out](https://github.com/typelevel/cats-mtl/issues/31)\n\n### Low-level API: optic providers\n\nAlternatively, `com.olegpy.meow.optics` can be used directly:\n\n```scala\ncase class User(name: String)\ntype HasUser[A] = MkLensToType[A, User]\n\n\ndef isFred[A](a: A)(implicit mkLens: HasUser[A]) =\n  mkLens().get(a).name == \"Fred\"\n```\n\nIn here, `mkLens` is an object with 0-args `apply` method, that creates\na shapeless Lens from A to User, e.g.:\n\n```scala\ncase class RequestCtx(user: User, id: String)\n\nassert { isFred(RequestCtx(User(\"Fred\"), \"0x42\")) }\n```\n\nPrism works in similar way, but it's a custom class (not shapeless\nPrism) with `apply` and `unapply` methods for construction and matching.\n\nThis is a very bare-bones implementation of optics, having only minimal\nfunctionality needed to support automatic derivation without adding\nextra dependencies. If you need a full-fledged optics library, consider\nusing [monocle] instead.\n\n## Cats-effect instances\n\nmeow-mtl provides instances for cats-effect compatible data types like\ncats-effect own `IO` or [monix] `Coeval` and `Task`. These instances\nreside in `com.olegpy.meow.effects` package and provide a more flexible\nand performant alternative to monad transformer stacks.\n\n\nBecause construction of such instances is typically effectful, they are\n*locally scoped*. That means, instead of being available by importing,\nthey require a special method to be called with a lambda, which will\nreceive an instance, i.e.:\n\n```scala\n// `unsafe` is used for the sake of an example. I don't recommend doing that.\nRef.unsafe[IO, Int](0).runAsk { implicit askInstance =\u003e\n  ??? // Ask[IO, Int] is available in this scope\n}\n```\n\nAlternatively, you can pull it out with specific methods if you intend\nto use it explicitly or with better-monadic-for implicit patterns:\n\n```scala\nimplicit val instance: Stateful[IO, Int] =\n  Ref.unsafe[IO, Int](0).stateInstance\n\n// Stateful available below\n???\n```\n\n### Ref\n`Ref` is a referentially transparent variable added in cats-effect\n1.0.0-RC2. It supports `Stateful`, `Ask` and `Tell`\neffects (the latest requires a `Semigroup` instance for type of\ncontained data).\n\nInstances are provided by extension methods `runState`, `runAsk` and\n`runTell` respectively.\n\n#### Example: counter\n\nThis is a simple example of using `Stateful` instance of `Ref`. Note\nhow updated state can be retrieved from `ref` after executing operation.\n\n```scala\ndef getAndIncrement[F[_]: Apply](implicit MS: Stateful[F, Int]) =\n  MS.get \u003c* MS.modify(_ + 1)\n\n\nfor {\n  ref \u003c- Ref.of[IO](0)\n  out \u003c- ref.runState { implicit ms =\u003e\n    getAndIncrement[IO].replicateA(3).as(\"Done\")\n  }\n  state \u003c- ref.get\n} yield (out, state) == (\"Done\", 3)\n```\n\n### Consumer\n\n`Consumer` is a simple wrapper around `A =\u003e F[Unit]`. It supports a\nsingle effect - `Tell`, and can be used for things like logging,\npersistence, notifications, etc.\n\n`Consumer` instances are constructed with `apply` method on a companion.\n\n\n#### Example: async logger\n\nThat logger only waits if a previous message is still being processed,\nto ensure correct ordering:\n\n```scala\n def greeter(name: String)(implicit ev: Tell[IO, String]): IO[Unit] =\n   ev.tell(s\"Long time no see, \\$name\") \u003e\u003e IO.sleep(1.second)\n\n def forever[A](ioa: IO[A]): IO[Nothing] = ioa \u003e\u003e forever(ioa)\n\n for {\n    mVar \u003c- MVar.empty[IO, String]\n    logger = forever(mVar.take.flatMap(s =\u003e IO(println(s)))\n    _ \u003c- logger.start // Do logging in background\n    _ \u003c- Consumer(mVar.put).runTell { implicit tell =\u003e\n      forever(greeter(\"Oleg\"))\n    }\n } yield ()\n```\n\n### TaskLocal\nSimilar to Ref, but with TaskLocal scoping it's possible to provide Local\n\n#### Example: request context\n\nHere, you can build a middleware for a service that provides some additional data per call.\nThis can be used for e.g. generating request IDs in HTTP server.\n\n\n```scala\ndef service[F[_]: Monad](greeting: String, print: String =\u003e F[Unit])(implicit ev: Ask[F, String]): F[Unit] =\n  ev.ask.map(name =\u003e s\"$greeting $name\") \u003e\u003e= print\n\ndef middleware[F[_]: Monad, A](getName: F[String])(service: F[Unit])(implicit ev: Local[F, String]) =\n  getName.flatMap(n =\u003e ev.scope(n)(service))\n\n// Can be looking up something in external system, or random ID\nval getName = Task(if (Random.nextBoolean()) \"Oleg\" else \"Olga\")\ndef putStrLn(s: String) = Task(println(s))\n\nval run =\n  for {\n    name \u003c- TaskLocal(\"\")\n    // note that you can create service separately from middleware,\n    // as long as they share the TaskLocal\n    svc  = name.runLocal { implicit ev =\u003e\n      service[Task](\"Hello,\", putStrLn)\n    }\n    withRandomName = name.runLocal { implicit ev =\u003e middleware(getName) _ }\n    // and run them in another place entirely that doesn't know about TaskLocal\n    // Randomly prints \"Hello, Oleg\" or \"Hello, Olga\"\n    _ \u003c- withRandomName(svc)\n    // prints \"Hello, \" since we don't set a context\n    _ \u003c- svc\n  } yield ()\n\n\n// Don't forget to enable Local support!\nrun.executeWithOptions(_.enableLocalContextPropagation)\n```\n\n## Sub-instances\nmeow-mtl also provides a set of implicits which let you use\n`Monad`/`Applicative`/`Functor` instances if you have an MTL instance of\ncompatible type (e.g. `Stateful`/`Ask`/`Tell`)\n\n```scala\nimport cats.implicits._\nimport com.olegpy.meow.prelude._ // just this one import\n\n// Can use pure and flatTap without having a Monad constraint or pull\n// it out manually\ndef test[F[_]](implicit MS: Stateful[F, Int]): F[Int] =\n  42.pure[F].flatTap(MS.set)\n```\n\nIt uses `LowPriority` mechanism from `shapeless` to ensure that _having_\na constraint does not result in ambiguities:\n\n```scala\nimport cats.effect.Sync\n// Uses Sync as a Monad instance, instead of getting it from Stateful\ndef test2[F[_]: Sync](implicit MS: Stateful[F, Int]): F[Int] =\n  42.pure[F].flatTap(MS.set)\n```\n\n## License\nMIT\n\n\n[cats-effect]: https://github.com/typelevel/cats-effect\n[cats-mtl]: https://github.com/typelevel/cats-mtl\n[monocle]: https://github.com/julien-truffaut/Monocle\n[monix]: https://github.com/monix/monix\n[mtl-talk]: https://www.youtube.com/watch?v=GZPup5Iuaqw\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foleg-py%2Fmeow-mtl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foleg-py%2Fmeow-mtl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foleg-py%2Fmeow-mtl/lists"}