{"id":15134541,"url":"https://github.com/thangiee/freasy-monad","last_synced_at":"2025-10-23T11:30:33.953Z","repository":{"id":57723556,"uuid":"67392674","full_name":"Thangiee/Freasy-Monad","owner":"Thangiee","description":"Easy way to create Free Monad using Scala macros with first-class Intellij support.","archived":false,"fork":false,"pushed_at":"2018-06-15T23:31:10.000Z","size":391,"stargazers_count":114,"open_issues_count":0,"forks_count":9,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-01-30T17:43:37.593Z","etag":null,"topics":["cat","free-monad","functional-programming","intellij","metaprogramming","scala","scalaz"],"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/Thangiee.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","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":"2016-09-05T06:07:02.000Z","updated_at":"2024-08-28T17:45:29.000Z","dependencies_parsed_at":"2022-08-25T15:03:09.915Z","dependency_job_id":null,"html_url":"https://github.com/Thangiee/Freasy-Monad","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/Thangiee%2FFreasy-Monad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thangiee%2FFreasy-Monad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thangiee%2FFreasy-Monad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thangiee%2FFreasy-Monad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thangiee","download_url":"https://codeload.github.com/Thangiee/Freasy-Monad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237811556,"owners_count":19370149,"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":["cat","free-monad","functional-programming","intellij","metaprogramming","scala","scalaz"],"created_at":"2024-09-26T05:22:39.770Z","updated_at":"2025-10-23T11:30:33.638Z","avatar_url":"https://github.com/Thangiee.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Freasy Monad\nFreasy Monad library makes it **easy** to create **Free Monad** for [typelevel/cats](https://github.com/typelevel/cats)\nand [scalaz/scalaz](https://github.com/scalaz/scalaz). \n\n## Getting started\n\n**Important** \n* Version 0.6.0 uses [scala.meta](http://scalameta.org/). If using IntelliJ, please uninstall the \nFreasy Monad Plugin if you have it installed. \n\n* Replace `addCompilerPlugin(\"org.scalamacros\" % \"paradise\" % \"2.1.0\" cross CrossVersion.full)` with\n  `addCompilerPlugin(\"org.scalameta\" % \"paradise\" % \"3.0.0-M11\" cross CrossVersion.full)` for those \n  coming from versions before 0.6.0.\n\nFreasy Monad is currently available for Scala 2.11 and 2.12, and [Scala.js](http://www.scala-js.org/).\n\nIf you are using `cats`, add the following to your build.sbt: \n\n```scala\nlibraryDependencies ++= Seq(\n  \"com.github.thangiee\" %% \"freasy-monad\" % \"0.7.0\",\n  \"org.typelevel\" %% \"cats-free\" % \"1.1.0\"\n)\naddCompilerPlugin(\"org.scalameta\" % \"paradise\" % \"3.0.0-M11\" cross CrossVersion.full)\n```\n\nIf you are using `scalaz`, add the following to your build.sbt: \n```scala\nlibraryDependencies ++= Seq(\n  \"com.github.thangiee\" %% \"freasy-monad\" % \"0.7.0\",\n  \"org.scalaz\" %% \"scalaz-core\" % \"7.2.22\"\n)\naddCompilerPlugin(\"org.scalameta\" % \"paradise\" % \"3.0.0-M11\" cross CrossVersion.full)\n```\n\nStarting with version `0.5.0`, group ID has been changed from `com.thangiee` to `com.github.thangiee`.\n\n## `@free` macro\nKey-value store example from [cats website](http://typelevel.org/cats/datatypes/freemonad.html) using `free` macro:\n\n```scala\n  import cats._\n  import cats.free._\n  import freasymonad.cats.free // or freasymonad.scalaz.free\n  import scala.collection.mutable\n\n  @free trait KVStore {                     // you can use any names you like\n    type KVStoreF[A] = Free[GrammarADT, A]  // as long as you define a type alias for Free \n    sealed trait GrammarADT[A]              // and a sealed trait.\n\n    // abstract methods are automatically lifted into part of the grammar ADT\n    def put[T](key: String, value: T): KVStoreF[Unit]\n    def get[T](key: String): KVStoreF[Option[T]]\n\n    def update[T](key: String, f: T =\u003e T): KVStoreF[Unit] =\n      for {\n        vMaybe \u003c- get[T](key)\n        _      \u003c- vMaybe.map(v =\u003e put[T](key, f(v))).getOrElse(Free.pure(()))\n      } yield ()\n  }\n\n  object Main extends App {\n    import KVStore.ops._\n  \n    def program: KVStoreF[Option[Int]] =\n      for {\n        _ \u003c- put(\"wild-cats\", 2)\n        _ \u003c- update[Int](\"wild-cats\", _ + 12)\n        _ \u003c- put(\"tame-cats\", 5)\n        n \u003c- get[Int](\"wild-cats\")\n      } yield n\n  \n    val impureInterpreter = new KVStore.Interp[Id] {\n      val kvs = mutable.Map.empty[String, Any]\n      def get[T](key: String): Id[Option[T]] = {\n        println(s\"get($key)\")\n        kvs.get(key).map(_.asInstanceOf[T])\n      }\n      def put[T](key: String, value: T): Id[Unit] = {\n        println(s\"put($key, $value)\")\n        kvs(key) = value\n      }\n    }\n    \n    impureInterpreter.run(program)\n  }\n```\nAbove example for scalaz [here](https://github.com/Thangiee/Freasy-Monad/blob/meta/jvm/src/test/scala/examples/scalaz/KVStore.scala).\n\nDuring compile time, `KVStore` is expanded to something similar to:\n```scala\n  object KVStore {\n    import cats._\n    import cats.free._\n    import scala.language.higherKinds\n    sealed trait GrammarADT[A]\n    object GrammarADT {\n      case class Put[T](key: String, value: T) extends GrammarADT[Unit]\n      case class Get[T](key: String) extends GrammarADT[Option[T]]\n    }\n    object ops {\n      type KVStoreF[A] = Free[GrammarADT, A]\n      def put[T](key: String, value: T): KVStoreF[Unit] = injectOps.put[GrammarADT, T](key, value)\n      def get[T](key: String): KVStoreF[Option[T]] = injectOps.get[GrammarADT, T](key)\n      def update[T](key: String, f: T =\u003e T): KVStoreF[Unit] = injectOps.update[GrammarADT, T](key, f) \n    }\n    object injectOps {\n      def put[F[_], T](key: String, value: T)(implicit I: Inject[GrammarADT, F]): Free[F, Unit] = Free.liftF(I.inj(GrammarADT.Put(key, value)));\n      def get[F[_], T](key: String)(implicit I: Inject[GrammarADT, F]): Free[F, Option[T]] = Free.liftF(I.inj(GrammarADT.Get(key)));\n      def update[F[_], T](key: String, f: T =\u003e T)(implicit I: Inject[GrammarADT, F]): Free[F, Unit] =\n        for {\n          vMaybe \u003c- get[F, T](key)\n          _      \u003c- vMaybe.map(v =\u003e put[F, T](key, f(v))).getOrElse(Free.pure(()))\n        } yield ()\n    }\n    class Injects[F[_]](implicit I: Inject[GrammarADT, F]) {\n      def put[T](key: String, value: T): Free[F, Unit] = injectOps.put[F, T](key, value);\n      def get[T](key: String): Free[F, Option[T]] = injectOps.get[F, T](key);\n      def update[T](key: String, f: T =\u003e T): Free[F, Unit] = injectOps.update[F, T](key, f)\n    }\n    object Injects {\n      implicit def injectOps[F[_]](implicit I: Inject[GrammarADT, F]): Inject[F] = new Inject[F]()\n    }\n    trait Interp[M[_]] extends ~\u003e[KVStore.GrammarADT, M] {\n      def apply[A](fa: KVStore.GrammarADT[A]): M[A] = fa match {\n        case GrammarADT.Put(key, value) =\u003e put(key, value)\n        case GrammarADT.Get(key) =\u003e get(key)\n      }\n      def run[A](op: KVStore.ops.KVStoreF[A])(implicit m: cats.Monad[M]): M[A] = op.foldMap(this)\n      def put[T](key: String, value: T): M[Unit]\n      def get[T](key: String): M[Option[T]]\n    }\n  }\n```\n\n### Benefits\n\n* From the expanded version, we can see that the `free` macro takes care most of the tedious parts when it\ncomes to writing free monad. The macro uses our abstract methods to define the ADT case classes, create smart \nconstructors to case classes, and do pattern matching on the ADT. \n\n* This library also generate `Inject` for composing Free monads ADTs. See example for \n[**cats**](https://github.com/Thangiee/Freasy-Monad/blob/meta/jvm/src/test/scala/examples/cats/ComposeFreeMonads.scala) and\n[**scalaz.**](https://github.com/Thangiee/Freasy-Monad/blob/meta/jvm/src/test/scala/examples/scalaz/ComposeFreeMonads.scala)\n\n* Writing an interpreter using Intellij becomes a breeze:\n\n  ![impl](https://cloud.githubusercontent.com/assets/4734933/18320271/c2904ed6-74ee-11e6-9202-bdb3fc3dc8c2.gif)\n\n* No more false error marks when writing interpreter with Intellij! \n  \n  ![regular-interp](https://cloud.githubusercontent.com/assets/4734933/18316097/f5de4ff0-74de-11e6-8542-00daa28c04c7.png) \n  ![freasy-monad-interp](https://cloud.githubusercontent.com/assets/4734933/18316104/f9025b4a-74de-11e6-8b4f-8414df117cea.png)\n\n### IntelliJ support\n\nSince switch to [scala.meta](http://scalameta.org/) in version 0.6.0, syntax highlighting \u0026 code completion in IntelliJ \nworks without needing to install a plugin. Therefore, the **Freasy Monad Plugin** needs to be uninstalled if you are \ncoming from a previous version. \n\n### Constraints\n\nThere are some constraints on `@free trait`, and if violated, will result in a compiler error.\n\n* Can not define `var`.\n* All `val` and `def` need to have an explicit return type.\n* Abstract `val` and `def` must have a return type of the defined type alias. From the example above, this would be `KVStoreF[...]`.\n* `private` and `protected` access modifiers are not allowed, use package-private instead.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthangiee%2Ffreasy-monad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthangiee%2Ffreasy-monad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthangiee%2Ffreasy-monad/lists"}