{"id":23902669,"url":"https://github.com/mtumilowicz/scala213-functional-programming-functor-monoid-monad-workshop","last_synced_at":"2026-02-18T19:01:09.136Z","repository":{"id":110879295,"uuid":"283318388","full_name":"mtumilowicz/scala213-functional-programming-functor-monoid-monad-workshop","owner":"mtumilowicz","description":"Functor, Monoid, Monads in practice.","archived":false,"fork":false,"pushed_at":"2025-01-15T20:47:35.000Z","size":163,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-07T00:23:39.847Z","etag":null,"topics":["applicative-functors","functional-programming","functors","monad-laws","monoids","scala","workshop","workshop-materials"],"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":"2020-07-28T20:19:14.000Z","updated_at":"2025-04-28T17:36:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"0b743ff6-91dc-467c-8e98-bf4f52a20d9d","html_url":"https://github.com/mtumilowicz/scala213-functional-programming-functor-monoid-monad-workshop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mtumilowicz/scala213-functional-programming-functor-monoid-monad-workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtumilowicz","download_url":"https://codeload.github.com/mtumilowicz/scala213-functional-programming-functor-monoid-monad-workshop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29590679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T18:54:29.675Z","status":"ssl_error","status_checked_at":"2026-02-18T18:50:50.517Z","response_time":162,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["applicative-functors","functional-programming","functors","monad-laws","monoids","scala","workshop","workshop-materials"],"created_at":"2025-01-04T22:49:44.053Z","updated_at":"2026-02-18T19:01:09.103Z","avatar_url":"https://github.com/mtumilowicz.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://app.travis-ci.com/mtumilowicz/scala213-functional-programming-functor-monoid-monad-workshop.svg?branch=master)](https://travis-ci.com/mtumilowicz/scala213-functional-programming-functor-monoid-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# scala213-functional-programming-functor-monoid-monad-workshop\n* references\n    * http://blog.higher-order.com/assets/fpiscompanion.pdf\n    * https://typelevel.org/cats/typeclasses/functor.html\n    * https://typelevel.org/cats/typeclasses/monad.html\n    * https://stackoverflow.com/questions/14598990/confused-with-the-for-comprehension-to-flatmap-map-transformation\n    * https://docs.scala-lang.org/tutorials/FAQ/yield.html\n    * https://www.james-willett.com/scala-map-flatmap-filter/\n    * https://miklos-martin.github.io/learn/fp/2016/03/10/monad-laws-for-regular-developers.html\n    * https://typelevel.org/blog/2016/08/21/hkts-moving-forward.html\n    * https://netvl.github.io/scala-guidelines/type-system/higher-kinded-types.html\n    * https://dzone.com/articles/application-type-lambdas-scala-0\n    * https://carlo-hamalainen.net/2014/01/02/applicatives-compose-monads-do-not/\n    * https://github.com/kitlangton/zio-from-scatch\n    * https://www.manning.com/books/functional-programming-in-scala-second-edition\n    * https://github.com/fpinscala/fpinscala/wiki\n    * [Scala with Cats Book - Noel Welsh](https://underscore.io/books/scala-with-cats/)\n    * [Functional Programming in Scala - Paul Chiusano](https://www.manning.com/books/functional-programming-in-scala)\n    * [ZIO from Scratch — Part 1](https://www.youtube.com/watch?v=wsTIcHxJMeQ)\n    * [ZIO from Scratch — Part 2](https://www.youtube.com/watch?v=g8Tuqldu2AE)\n    * [Software Transactional Memory](https://www.youtube.com/watch?v=bLfxaHIvHfc)\n    * [A Pragmatic Introduction to Category Theory—Daniela Sfregola](https://www.youtube.com/watch?v=Ss149MsZluI)\n    * https://chat.openai.com/\n\n## preface\n* goals of this workshop\n    * introduction to scala features:\n        * for-comprehension\n        * higher kinded types\n    * developing basic intuitions concerning standard functional structures\n        * monoid\n        * functor and applicative functor\n        * monad\n    * show fundamental differences between above constructs\n    * apply knowledge to real life\n        * functional approach to validation\n        * functional approach to IO\n* workshops order\n    1. `ForComprehensionWorkshop`\n    1. `MonoidWorkshop`\n    1. `FunctorWorkshop`\n    1. `ApplicativeWorkshop`\n    1. `PersonValidatorWorkshop`\n    1. `MonadWorkshop`\n    1. `EchoWorkshop`\n    \n## introduction    \n* whenever we create an abstraction like Functor we should\n    * consider abstract methods it should have\n    * and laws we expect to hold for the implementations\n        * of course Scala won’t enforce any of these laws\n* laws are important for two reasons\n    1. help an interface form a new semantic level\n        * algebra may be reasoned about independently of the instances\n        * example\n            * we could proof that `Monoid[(A,B)]` constructed from `Monoid[A]` and `Monoid[B]`\n            is actually a monoid\n                * we don’t need to know anything about `A` and `B` to conclude this\n    1. we often rely on laws when writing various combinators\n        * example\n            * unzip `List[(A, B)]` into `List[A]`, `List[B]`\n                * same length\n                * corresponding elements in the same order\n            * algebraic reasoning can potentially save us a lot of work, since \n            we don’t have to write separate tests for these properties\n                * we could write a generic unzip function that works for any functor\n      \n## monoids\n* monoid consists of the following:\n    * trait\n        ```\n        trait Monoid[A] {\n          def combine(a1: A, a2: A): A\n        \n          def zero: A\n        }\n        ```\n    * laws (semigroup with an identity element)\n        * associativity\n            ```\n            combine(combine(x,y), z) == combine(x, combine(y,z)) for any choice of x: A, y: A, z: A\n            ```\n        * identity\n            ```\n            exists zero: A, that combine(x, zero) == x and combine(zero, x) == x for any x: A\n            ```\n    * is a type A and an implementation of `Monoid[A]` that satisfies the laws\n* example\n    * string monoid\n        ```\n        val stringMonoid = new Monoid[String] {\n            def combine(a1: String, a2: String) = a1 + a2\n            val zero = \"\"\n        }\n        ```\n    * various Monoid instances don’t have much to do with each other\n        * monoid is a type, together with the monoid operations and a set of laws\n        * you may build some intuition by considering the various concrete instances\n            * but nothing guarantees all monoids you encounter will match your intuition\n    * we can say that\n        * type A forms a monoid\n        * type A is monoidal\n        * less precisely: type A is a monoid or even type A is monoidal\n        * but not: type A has monoid\n            * analogy: the page you’re reading forms a rectangle, is rectangular or it is a rectangle\n            but not: it has a rectangle\n        * in any case, the Monoid[A] instance is simply evidence of this fact\n* folding context\n    ```\n    def foldRight[B](z: B)(f: (A, B) =\u003e B): B\n    ```\n    and we want to fold into the same type so\n    ```\n    def foldRight[A](z: A)(f: (A, A) =\u003e A): A\n    ```\n    monoid fit these argument types like a glove\n    ```\n    foldRight(monoid.zero)(monoid.combine)\n    ```\n    `foldLeft` and `foldRight` gives the same results when folding with monoid (associativity) \n* parallelism\n    * below three operations give the same result\n        ```\n        combine(a, combine(b, combine(c, d)))\n        combine(combine(combine(a, b), c), d)\n        combine(combine(a, b), combine(c, d)) // allows for parallelism\n        ```\n    * proof (associativity)\n        ```\n        combine(combine(combine(a, b), c), d) = combine(combine(x, y), z), where x = combine(a, b), y = c, z = d\n        combine(combine(x, y), z) = combine(x, combine(y, z))\n        combine(x, combine(y, z)) = combine(combine(a, b), combine(c, z))\n        so: combine(combine(combine(a, b), c), d) = combine(combine(a, b), combine(c, d))\n        ```\n* additional properties\n    * monoids compose\n        * example: `A, B monoids -\u003e (A, B) is also a monoid`\n    * monoid homomorphism\n        * condition: `A, B monoids =\u003e A.combine(f(x), f(y)) == f(B.combine(x, y))`\n    * monoid isomorphism\n        * homomorphism in both directions\n        * example: `String` and `List[Char]` monoids with concatenation\n    * commutative combine\n        * when `combine(x, y) == combine(y, x))`\n* semigroup\n    * semigroup for a type A has an associative combine operation that returns an A given two input A values\n        ```\n        trait Semigroup[A] {\n          def combine(a1: A, a2: A): A\n        }\n        ```\n    * example: `NonEmptyList[A]`\n        * there is no instance `Monoid[NonEmptyList[A]]` - you cannot specify `zero` element\n## functors\n* definition\n    * we say that a type constructor `F` is a functor, and the `Functor[F]` instance constitutes proof \n    that `F` is in fact a functor\n    ```\n    trait Functor[F[_]] {\n      def map[A, B](fa: F[A])(f: A =\u003e B): F[B]\n    }\n    ```\n* must obey two laws:\n    * `fa.map(f).map(g) = fa.map(f.andThen(g))`\n    * `fa.map(x =\u003e x) = fa`\n* functors compose: https://github.com/mtumilowicz/scala212-cats-category-theory-composing-functors\n* for more formal reasoning, please refer: https://github.com/mtumilowicz/java11-category-theory-optional-is-not-functor\n\n## applicative functors\n* definition\n    ```\n    trait Applicative[F[_]] {\n    \n      def unit[A](a: =\u003e A): F[A]\n    \n      def map2[A, B, C](fa: F[A], fb: F[B])(f: (A, B) =\u003e C): F[C]\n    }\n    ```\n    * could be formulated using `unit` and `apply` (therefore called Applicative), rather than \n    `unit` and `map2`\n        * `def apply[A,B](fab: F[A =\u003e B])(fa: F[A]): F[B]`\n* applicative laws\n    * left and right identity\n        ```\n        map(v)(id) == v\n        map(map(v)(g))(f) == map(v)(f compose g)\n        ```\n        in other words\n        ```\n        map2(unit(()), fa)((_,a) =\u003e a) == fa\n        map2(fa, unit(()))((a,_) =\u003e a) == fa     \n        ```\n    * associativity (in terms of product)\n         * `def product[A,B](fa: F[A], fb: F[B]): F[(A,B)] = map2(fa, fb)((_,_))`\n         * `def assoc[A,B,C](p: (A,(B,C))): ((A,B), C) = p match { case (a, (b, c)) =\u003e ((a,b), c) }`\n         * product(product(fa,fb),fc) == map(product(fa, product(fb,fc)))(assoc)\n    * naturality law\n        * `fa.map2(fb)((a, b) =\u003e (f(a), g(b))) == fa.map(f).product(fb.map(g))`\n* all applicatives are functors\n* advantages\n    * preferable to implement combinators using as few assumptions as possible\n        * it’s better to assume that a data type can provide `map2` than `flatMap`\n        * otherwise we’d have to write a new `traverse` every time we encountered \n        a type that’s Applicative but not a Monad \n* validation context\n    * only reporting the first error means the user would have to repeatedly submit \n    the form and fix one error at a time\n    * consider what happens in a sequence of `flatMap` calls like the following\n        ```\n        validName(field1) flatMap (f1 =\u003e\n            validBirthdate(field2) flatMap (f2 =\u003e\n                validPhone(field3) map (f3 =\u003e ValidInput(f1, f2, f3))\n        ```\n        * if `validName` fails with an error, then `validBirthdate` and `validPhone` won’t even run \n    * now think of doing the same thing with `map3`\n        ```\n        map3(validName(field1), validBirthdate(field2), validPhone(field3))(ValidInput(_,_,_))\n        ```\n        * no dependency implied between the three expressions\n* additional properties\n    * large number of the useful combinators on Monad can be defined using only unit and map2\n        * example\n            ```\n            def map2[B, C](fb: F[B])(f: (A, B) =\u003e C): F[C] =\n                fa.flatMap(a =\u003e fb.map(b =\u003e f(a, b)))\n            ```\n    * applicative functors compose\n        * `F[_]` and `G[_]` are applicative functors =\u003e `F[G[_]]` is applicative functor\n## monads\n* definition\n    ```\n    trait Monad[F[_]] {\n    \n      def unit[A](a: =\u003e A): F[A]\n    \n      def flatMap[A, B](ma: F[A])(f: A =\u003e F[B]): F[B]\n    \n    }\n    ```\n* monad laws\n    * given compose function\n        ```\n        def compose[A,B,C](f: A =\u003e F[B], g: B =\u003e F[C]): A =\u003e F[C] =\n            a =\u003e flatMap(f(a))(g)\n        ```\n        * digression\n            * functions like that: `A =\u003e F[B]` are called Kleisli arrows\n    * left identity and right identity\n        ```\n        compose(f, unit) == f // m.flatMap(unit) == m\n        compose(unit, f) == f // unit(a).flatMap(func) == func(a)\n      \n        // example for option\n        option.flatMap(Some(_)) == option\n      \n        Some(value).flatMap(f) == f(value)\n        ```\n        * similar to zero element in monoids\n    * associative law for monads\n        ```\n        compose(compose(f, g), h) == compose(f, compose(g, h))\n      \n        // example for option\n        option.flatMap(f).flatMap(g) == option.flatMap(f(_).flatMap(g))\n        ```\n        * similar to associative law for monoids\n* monad is an implementation of one of the minimal sets of primitive combinators\nsatisfying the monad laws\n    * combinator sets\n        * unit and flatMap\n            ```\n            def unit[A](a: =\u003e A): F[A]\n            def flatMap[A, B](ma: F[A])(f: A =\u003e F[B]): F[B]\n            ```\n        * unit and compose\n            ```\n            def unit[A](a: =\u003e A): F[A]\n            def compose[A,B,C](f: A =\u003e F[B], g: B =\u003e F[C]): A =\u003e F[C]\n            ```\n        * unit, map and join\n            ```\n            def unit[A](a: =\u003e A): F[A]\n            def map[A, B](fa: F[A])(f: A =\u003e B): F[B]\n            def join[A](mma: F[F[A]]): F[A]      \n            ```\n* each monad brings its own set of additional primitive operations that are specific to it\n    * example: `Option`, `Either`, `List`\n* vs interfaces\n    * interfaces ~ relatively complete API for an abstract data type\n        * merely abstracting over the specific representation\n        * example: List - LinkedList, ArrayList\n    * monad ~ many vastly different data types can satisfy the Monad interface and laws\n        * like Monoid, is an abstract, purely algebraic interface\n* chain of `flatMap` calls is like an imperative program with statements \nthat assign to variables\n    * monad specifies what occurs at statement boundaries\n    * example\n        * `Option` monad - may return None at some point and effectively terminate the processing\n    * example: identity monad\n        ```\n        case class Id[A](value: A) {\n          def map[B](f: A =\u003e B): Id[B] = Id(f(value))\n        \n          def flatMap[B](f: A =\u003e Id[B]): Id[B] = f(value)\n        }\n        ```\n        ```\n        val id = Id(\"Hello, \")\n          .flatMap(a =\u003e Id(\"monad!\")\n          .flatMap(b =\u003e Id(a + b)))\n      \n        // id = Id(Hello, monad!)\n        ```\n        * simply variable substitution\n            ```\n            for {\n                a \u003c- Id(\"Hello, \")\n                b \u003c- Id(\"monad!\")\n            } yield a + b\n            ```\n            vs\n            ```\n            val a = \"Hello, \"\n            val b = \"monad!\"\n            val c = a + b\n            ```\n            * variables `a` and `b` get bound to `\"Hello, \"` and `\"monad!”`\n        * monads provide a context for introducing and binding variables, and performing \n        variable substitution\n* unlike Functors and Applicatives, not all Monads compose\n    * to show that \"monads do not compose\", it is sufficient to find a counterexample, namely two monads \n    `f` and `g` such that `f g` is not a monad\n    * proof: https://carlo-hamalainen.net/2014/01/02/applicatives-compose-monads-do-not/\n    * problem\n        * to implement `join` for nested monads `F` and `G`, write something of a type like:\n            ```\n            F[G[F[G[A]]]] =\u003e F[G[A]]\n            ```\n        * that can’t be written generally\n        * but if `G` also happens to have a `Traverse` instance\n            ```\n            F[F[G[G[A]]]] =\u003e F[G[A]] // use sequence to turn G[F[_]] into F[G[_]], then use join on F then on G\n            ```\n    * problem is often addressed with a custom-written version specifically constructed for composition\n        * example: monad transformers like `OptionT`\n* all monads are applicatives\n    * not all applicatives are monads\n    * difference between monads and applicatives\n        * applicative constructs context-free computations, while Monad allows for context sensitivity\n        * `join` and `flatMap` can’t be implemented with just `map2` and `unit`\n            * `def join[A](f: F[F[A]]): F[A] // removes a layer of F`\n            * `unit` function only lets us add an `F` layer\n            * `map2` lets us apply a function within `F` but does no flattening of layers\n    * `Option` applicative versus the `Option` monad\n        * combine the results from two (independent) lookups: `map2`\n            ```\n            val F: Applicative[Option] = ...\n            val departments: Map[String,String] = ...\n            val salaries: Map[String,Double] = ...\n            val o: Option[String] = F.map2(departments.get(\"Alice\"), salaries.get(\"Alice\")) {\n                (dept, salary) =\u003e s\"Alice in $dept makes $salary per year\"\n            }\n            ```\n        * result of one lookup to affect next lookup: `flatMap` \n            ```\n            val idsByName: Map[String,Int]\n            val departments: Map[Int,String] = ...\n          \n            val o: Option[String] = idsByName.get(\"Bob\")\n                .flatMap { id =\u003e departments.get(id) }\n            }\n            ```\n* each monad brings its own set of additional primitive operations that are specific to it\n    * example: `State[S, A]`\n        ```\n        def get[S]: State[S, S]\n        def set[S](s: =\u003e S): State[S, Unit]\n        ```          \n* monad is a monoid in a category of endofunctors\n    * endofunctor - functor that maps a category to itself\n    * let's look for minimal set of combinators for a monad: unit, map and join\n        * monoid = unit + join\n        * endofunctor = map\n    * `Monoid[M]` - objects: Scala types, arrows: Scala functions\n    * `Monad[F]` - objects: Scala functors, arrows: natural transformations\n    * overview\n        |             | `zero`/`unit`   | `op`/`join`|\n        --------------|-----------------|------------|\n        | `Monoid[M]` | `1 =\u003e M`        | `M² =\u003e M`  |\n        | `Monad[F]`  | `1 ~\u003e F`        | `F² ~\u003e F`  |\n        * `~\u003e` is natural transformation\n        * `M²` is `(M, M)`\n        * `F²` is `F[F[A]]`\n        * `1` is\n            * for Monoid: `Unit` type\n            * for Monad: identity functor\n\n### IO context\n```\nsealed trait IO[A] {\n  self =\u003e //  lets us refer to this object inside closures\n  def unsafeRunSync: A\n\n  def map[B](f: A =\u003e B): IO[B] =\n    new IO[B] {\n      def run: B = f(self.run)\n    }\n\n  def flatMap[B](f: A =\u003e IO[B]): IO[B] =\n    new IO[B] {\n      def run: B = f(self.run).run\n    }\n}\n```\n* in short: `IO` is a magic thing that says this function depends on something other than its arguments \n* clearly separates pure code from impure code, forcing us to be honest about where \ninteractions with the outside world are occurring\n    * referentially transparent description of a computation with effects\n* IO computations are ordinary values\n    * we can store them in lists, pass them to functions, create them dynamically, and so on\n    * example\n        ```\n        def PrintLine(msg: String): IO = new IO {\n          def unsafeRun = println(msg)\n        }\n\n        def contest(p1: Player, p2: Player): IO = // pure function\n          PrintLine(winnerMsg(winner(p1, p2)))\n        ```\n* given `IO[A]` will overflow the runtime call stack and throw a `StackOverflowError`\n    * solution: encapsulate operations into dedicated case classes and use tail recursion for evaluation\n        * `io.flatMap(f).flatMap(g) = io.flatMap(v =\u003e f(v) flatMap g)`\n        * approach called trampoline\n             \n## appendix\n### higher kinded type\n* represent an ability to abstract over type constructors\n* suppose we have same implementations, different type constructors\n    ```\n    def tuple[A, B](as: List[A], bs: List[B]): List[(A, B)] =\n      as.flatMap{a =\u003e\n        bs.map((a, _))}\n        \n    def tuple[A, B](as: Option[A], bs: Option[B]): Option[(A, B)] =\n      as.flatMap{a =\u003e\n        bs.map((a, _))}\n        \n    def tuple[E, A, B](as: Either[E, A], bs: Either[E, B]): Either[E, (A, B)] =\n      as.flatMap{a =\u003e\n        bs.map((a, _))}\n    ```\n    * in programming, when we encounter such great sameness—not merely similar code, \n    but identical code—we would like the opportunity to parameterize: extract the parts \n    that are different to arguments, and recycle the common code for all situations\n    * we have a way to pass in implementations; that’s just higher-order functions\n    * we need \"type constructor as argument\"\n        ```\n        def tuplef[F[_], A, B](fa: F[A], fb: F[B]): F[(A, B)] = ???      \n        ```\n        * `F[_]` means that `F` may not be a simple type, like `Int` or `String`, but instead \n        a one-argument type constructor, like `List` or `Option`\n* higher-kinded types are sometimes used in libraries\n    * example: standard Scala collections\n* Scala doesn’t allow us to use underscore syntax to simply say `State[Int, _]` to create \nan anonymous type constructor like we create anonymous functions\n    * we have to go through type projections\n        * `({type L[a] = Map[K, a]})#L`\n            * declares an anonymous type\n            * then access its `L` member with the `#` syntax\n            * type constructor declared inline like this is often called a type lambda in Scala\n    * removed in Scala 3\n### for comprehension\n* each line in the expression using the `\u003c-` is translated to a `flatMap` call, except \n    * the last line (`yield`) - it is translated to a concluding `map` call\n    * `x \u003c- c if cond` is translated to `c.filter(x =\u003e cond)`\n* `flatMap` / `map`\n    ```\n    for {\n      bound \u003c- list\n      out \u003c- f(bound)\n    } yield out\n  \n    // is equivalent to (could be desugared with IntelliJ)\n  \n    list.flatMap { bound =\u003e\n      f(bound).map { out =\u003e\n        out\n      }\n    }\n    ```\n* `flatMap` / `map` / `filter`\n    ```\n    for {\n      sl \u003c- l\n      el \u003c- sl if el \u003e 0\n    } yield el.toString.length\n  \n    // is equivalent to\n  \n    l.flatMap(sl =\u003e sl.filter(el =\u003e el \u003e 0).map(el =\u003e el.toString.length))\n    ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtumilowicz%2Fscala213-functional-programming-functor-monoid-monad-workshop/lists"}