{"id":23902746,"url":"https://github.com/mtumilowicz/scala-cats-free-monad-workshop","last_synced_at":"2025-09-14T04:26:53.194Z","repository":{"id":110879031,"uuid":"494815213","full_name":"mtumilowicz/scala-cats-free-monad-workshop","owner":"mtumilowicz","description":"Introduction into functional programming free structures: free monads, free monoids, free applicatives.","archived":false,"fork":false,"pushed_at":"2022-12-10T14:13:20.000Z","size":90,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-02T19:44:55.452Z","etag":null,"topics":["cats","cats-core","cats-effect","cats-free","church-encoding","free-monad","freemonad","functional-programming","functional-programming-examples","functional-programming-language","pure-functional","scala","workshop-material","workshop-materials","workshops","zio-layer"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtumilowicz.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-05-21T15:03:17.000Z","updated_at":"2025-03-06T23:50:53.000Z","dependencies_parsed_at":"2023-03-13T13:47:17.617Z","dependency_job_id":null,"html_url":"https://github.com/mtumilowicz/scala-cats-free-monad-workshop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mtumilowicz/scala-cats-free-monad-workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala-cats-free-monad-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala-cats-free-monad-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala-cats-free-monad-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala-cats-free-monad-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtumilowicz","download_url":"https://codeload.github.com/mtumilowicz/scala-cats-free-monad-workshop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala-cats-free-monad-workshop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275062543,"owners_count":25398883,"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","status":"online","status_checked_at":"2025-09-14T02:00:10.474Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","cats-core","cats-effect","cats-free","church-encoding","free-monad","freemonad","functional-programming","functional-programming-examples","functional-programming-language","pure-functional","scala","workshop-material","workshop-materials","workshops","zio-layer"],"created_at":"2025-01-04T22:50:17.219Z","updated_at":"2025-09-14T04:26:53.170Z","avatar_url":"https://github.com/mtumilowicz.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://app.travis-ci.com/mtumilowicz/scala-cats-free-monad-workshop.svg?branch=master)](https://app.travis-ci.com/mtumilowicz/scala-cats-free-monad-workshop)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n\n# scala-cats-free-monad-workshop\n\n* references\n    * [Zymposium - Data vs Function (Part 1) — Church Encodings](https://www.youtube.com/watch?v=-xo7VvKpE8w)\n    * [Zymposium — Data vs Function — Free Structures (Part 2)](https://www.youtube.com/watch?v=hbyVu5tgAiA)\n    * [Move Over Free Monads: Make Way for Free Applicatives! — John de Goes](https://www.youtube.com/watch?v=H28QqxO7Ihc)\n    * [John De Goes: One Monad to Rule Them All](https://www.youtube.com/watch?v=M0Fe2SRTm5c)\n    * [Free as in Monads by Daniel Spiewak](https://www.youtube.com/watch?v=aKUQUIHRGec)\n    * [Free monad or tagless final? How not to commit to a monad too early - Adam Warski](https://www.youtube.com/watch?v=IhVdU4Xiz2U)\n    * [Free Monads—Paweł Szulc](https://www.youtube.com/watch?v=ycrpJrcWMp4)\n    * [Uniting Church and State: FP and OO Together by Noel Welsh](https://www.youtube.com/watch?v=IO5MD62dQbI)\n    * https://softwaremill.com/free-tagless-compared-how-not-to-commit-to-monad-too-early/\n    * https://github.com/softwaremill/free-tagless-compare\n    * https://github.com/zivergetech/Zymposium\n    * https://github.com/noelwelsh/church-and-state\n    * https://underscore.io/blog/posts/2017/06/02/uniting-church-and-state.html\n    * http://jim-mcbeath.blogspot.com/2008/11/practical-church-numerals-in-scala.html\n    * https://softwaremill.com/free-tagless-compared-how-not-to-commit-to-monad-too-early/\n    * https://softwaremill.com/free-monads/\n    * https://gist.github.com/igor-ramazanov/bd7d2a9dd5726d8ca9c356cf6cd85abf\n    * https://stackoverflow.com/questions/46789807/can-i-represent-non-sequential-parallel-execution-with-monads\n    * https://hackage.haskell.org/package/base-4.16.1.0/docs/Control-Monad.html\n    * https://hackage.haskell.org/package/base-4.16.1.0/docs/Control-Applicative.html\n    * https://stackoverflow.com/questions/46913472/how-exactly-does-the-ap-applicative-monad-law-relate-the-two-classes\n    * https://www.reddit.com/r/scala/comments/hu1xgk/struggling_with_cats_applicative_implementation/\n    * https://stackoverflow.com/questions/54164346/what-is-the-main-difference-between-free-monoid-and-monoid\n    * https://typelevel.org/cats/datatypes/freemonad.html#composing-free-monads-adts\n\n## preface\n* goals of this workshop:\n    * show free monads as a opposition to tagless-final\n    * introduction to other free structures: monoid, applicative, functor\n    * introduction to church encoding and reification\n    * understand how to use reification and free structures in practice\n    * show how resolve layers graph with reification and free structures\n\n## free monad\n* example\n    ```\n    sealed trait Console[A]\n    case object ReadLine extends Console[String]\n    case class PrintLine(line: String) extends Console[Unit]\n\n    object Console {\n      type Dsl[A] = Free[Console, A]\n\n      def readLine: Dsl[String] = Free.liftF(ReadLine)\n      def printLine(line: String): Dsl[Unit] = Free.liftF(PrintLine(line))\n    }\n\n    val program: Free[Console, Unit] = for {\n      _ \u003c- Console.printLine(\"What is your name?\")\n      name \u003c- Console.readLine\n      _ \u003c- Console.printLine(s\"Hi $name!\")\n    } yield ()\n\n    // interpreter for live app\n    val ioInterpreter : Console ~\u003e IO = new (Console ~\u003e IO) {\n      override def apply[A](fa: Console[A]): IO[A] = fa match {\n        case ReadLine =\u003e IO { scala.io.StdIn.readLine }\n        case PrintLine(line) =\u003e IO { println(line) }\n      }\n    }\n\n    // interpreter for tests\n    def inMemoryInterpreter(input: ..., output: ...): Console ~\u003e Id = new (Console ~\u003e Id) {\n        ...\n    }\n\n\n    IO.run(program.foldMap(IO.ioInterpreter))\n    ```\n* free monad = represent program as data\n    * `Free[F, A]`\n        * Free = program\n        * F - algebra of the program (language)\n            * you can go through cases one by one and say: program can this or that\n            * easier to transform, compose, reason about\n        * A - value produced by the program\n* allows us to separate the structure of the computation from its interpretation\n    * different interpretation depending on context (ex. live vs tests)\n* pros\n    * we can pattern-match on the programs (which are values) to transform \u0026 optimize them\n    * interpretation is deferred until an interpreter is provided\n* digression: trouble with monads\n    * `def flatMap[A, B](fa: F[A])(f: A =\u003e F[B]): F[B]`\n        * sequential computation: a program can depend on a value produced by previous program\n        * fa - first program\n        * A - runtime value\n        * F[B] - second program\n        * return = result program\n        * can only be interpreted, not introspected and transformed (prior to interpretation)\n    * however, in applicatives: `def ap[A, B](ff: F[A =\u003e B])(fa: F[A]): F[B]`\n        * no program depends on any runtime value: structure is static\n        * `(doX, doY, doZ) mapN (doResults(_, _, _))`\n            * parallel, not sequential\n    * back to monads\n        * can I represent the `ap` function in terms of the `bind` function?\n            * yes - every monad is an applicative\n        * can I represent parallel execution with monads\n            * a type which exposes a monadic interface cannot use Applicative to do parallel computation\n            * the Monad and Applicative operations should relate as follows:\n                * `(\u003c*\u003e) = ap`, where\n                    * `(\u003c*\u003e) :: f (a -\u003e b) -\u003e f a -\u003e f b` // from applicative\n                    * `ap :: Monad m =\u003e m (a -\u003e b) -\u003e m a -\u003e m b`\n                * `(\u003c*\u003e) = ap` is exactly the same as\n                    ```\n                    m1 \u003c*\u003e m2 = do {\n                        x1 \u003c- m1;\n                        x2 \u003c- m2;\n                        return (x1 x2)\n                    }\n                    ```\n            * example for List\n                * `Applicative[List].map2(List(1, 2, 3), List(1, 2, 3))(_ + _)`\n                * two possible returns\n                    * `List(2, 4, 6)`\n                    * `List(2, 3, 4, 3, 4, 5, 4, 5, 6)`\n                * explanation\n                    * if you only consider Applicative then both implementations are valid\n                    * however\n                        * List should have also an instance of Monad\n                        * all Monads are also Applicatives (in cats: `Monad[F[_]] extends Applicative[F]`)\n                        * then the implementation of `map2` in this case has not only to be consistent with\n                        the Applicative laws but also with the Monad laws\n                            * `map2` is implemented in terms of `ap` which is implemented in terms of `flatMap`\n                        * as such, only the second implementation is valid\n                            * this is called typeclass coherence\n                    * reason why things like Validated has only Applicative instances but not Monad ones (contrary to\n                    for example Either)\n* intuition for free functor hierarchy\n    * free functor: programs that change values\n    * free applicatives: programs that build data\n        * classic example: parsers\n            ```\n            case class AuthConfig(port: Int, host: String)\n            val authConfig = (int(\"port\"), server(\"host\")) mapN (AuthConfig)\n            ```\n    * free monads: programs that build programs\n\n## composing free monads\n* `EitherK[F[_], G[_], A]` - either on type constructors\n* `FunctionK[F[_], G[_]]` is a natural transformation from `F` to `G`\n    * we can compose them: `interpreter1 or interpreter2` (`FunctionK[EitherK[...], ...]`)\n    * last problem: `type Dsl[A] = Free[F, A]` should have the same type `F`\n        * `InjectK[F[_], G[_]]` is a type class providing an injection from type constructor `F` into type constructor `G`\n        * we have to lift our DSLs into that common `F`\n            * \n\n## free monoid\n* when more than one monoid exists for a type, the free monoid defers\nthe decision on which specific monoid to use\n* example\n    * for integers, infinitely many monoids exist, but the most common are addition and multiplication\n    * val out = FreeMonoid.Value(2) ++ FreeMonoid.Value(3) ++ ...\n    * println(out) // Combine(Combine(Value(2), Value(3)), ...)\n* similar to list\n    * you could store elements in the list, then fold them with monoid\n\n## church encoding\n* OO vs FP\n    * OO: easy to add operations (without code changes), hard to add actions (ex. return type change)\n        ```\n        class Calculator {\n            def literal(v: Double): Double = v\n            def add(a: Double, b: Double): Double = a + b\n            def subtract(a: Double, b: Double): Double = a - b\n            ...\n        }\n\n        val c = new Calculator\n        import c._\n\n        add(literal(1.0), subtract(literal(3.0), literal(2.0)))\n\n        // inheritance (add operations)\n        class TrigonometricCalculator extends Calculator {\n            def sin(a: Double): Double = Math.sin(a)\n        }\n        ```\n    * FP: hard to add operations, easy to add actions (ex. new interpreter)\n        ```\n        // classic FP = represent operations as data\n        sealed trait Calculation\n        case class Literal(v: Double) extends Calculation\n        case class Add(a: Calculation, b: Calculation) extends Calculation\n        case class Subtract(a: Calculation, b: Calculation) extends Calculation\n        ...\n\n        // define interpreters (add actions)\n        def eval(c: Calculation): Double =\n            c match {\n                case Literal(v) =\u003e v\n                case Add(a, b) =\u003e eval(a) + eval(b)\n                case Subtract(a, b) =\u003e eval(a) - eval(b)\n                ...\n            }\n\n        def pretty(c: Calculation): String =\n            c match {\n                case Literal(v) =\u003e v.toString\n                case Add(a, b) =\u003e s\"${pretty(a)} + ${pretty(b)}\"\n                case Subtract(a, b) =\u003e s\"${pretty(a)} - ${pretty(b)}\"\n                ...\n            }\n        ```\n* OO and FP are related by the Church encoding\n    * takes us from FP to OO\n        * constructors become method calls\n        * operator types become action types\n        * example\n            ```\n            trait ChurchBoolean {\n              def apply[A](ifTrue: =\u003e A, ifFalse: =\u003e A): A\n\n              def \u0026\u0026(that: ChurchBoolean): ChurchBoolean =\n                this(\n                  that,\n                  ChurchBoolean.False\n                )\n            }\n\n            object ChurchBoolean {\n              val True = new ChurchBoolean {\n                override def apply[A](ifTrue: =\u003e A, ifFalse: =\u003e A): A = ifTrue\n              }\n\n              val False = new ChurchBoolean {\n                override def apply[A](ifTrue: =\u003e A, ifFalse: =\u003e A): A = ifFalse\n              }\n\n            }\n            ```\n    * reification: takes from OO to FP\n* type classes are Church encodings of free structures\n* free structures are reifications of type classes\n* in scala, according to \"Towards Equal Rights for Higher-Kinded Types\" by Moors, Piessens and Odersky, July 2007\n    * Scala's kinds correspond to the types of the simply-typed lambda calculus\n    * this means that we can express addition on natural numbers on the level of types using a Church Encoding\n    * example: https://w.pitula.me/2017/typelevel-church-enc/\n\n## zio layer context\n* suppose that we have two time consuming computations\n    * A ===================\u003e C\n    * A ===================\u003e C'\n* and suppose there is some other orchestration that is trivial\n    * B ==\u003e C\n    * B ==\u003e C'\n* there is often shorter path to some type B\n    * A ===========\u003e B\n* so we can go:\n    * A ===========\u003e B\n    * and then\n        * B ==\u003e C and B ==\u003e C'\n* and therefore save a lot of time\n    * ===================\u003e===================\u003e\n    * ===========\u003e==\u003e==\u003e\n* it could be difficult to see what is that intermediary structure B\n    * replace functions call with data representation (B) and then fold over it (C, C')\n    * reverse process to church encoding\n* suppose we would like to have a program that:\n    * shows how to correctly compose zio layers based on set of given layers\n    * and prints all layers that are missing\n    * so it's better to create first representation of graph, then - evaluate it\n        * otherwise we would have to reuse big parts of the same logic twice (generating graph twice)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fscala-cats-free-monad-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtumilowicz%2Fscala-cats-free-monad-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fscala-cats-free-monad-workshop/lists"}