{"id":19733173,"url":"https://github.com/thoughtworksinc/each","last_synced_at":"2025-04-05T04:14:49.297Z","repository":{"id":31520092,"uuid":"35084526","full_name":"ThoughtWorksInc/each","owner":"ThoughtWorksInc","description":"A macro library that converts native imperative syntax to scalaz's monadic expressions","archived":false,"fork":false,"pushed_at":"2024-08-19T21:12:41.000Z","size":622,"stargazers_count":257,"open_issues_count":20,"forks_count":26,"subscribers_count":27,"default_branch":"3.3.x","last_synced_at":"2025-03-29T03:09:51.378Z","etag":null,"topics":["annotations","comprehension","macro-library","monad","scala","scalaz","scalaz-monadic-expressions","thoughtworks"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ThoughtWorksInc.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}},"created_at":"2015-05-05T07:27:00.000Z","updated_at":"2025-01-01T14:48:49.000Z","dependencies_parsed_at":"2023-02-18T19:30:57.142Z","dependency_job_id":"f12809d3-a14c-4ab6-b313-f62898ec8db8","html_url":"https://github.com/ThoughtWorksInc/each","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThoughtWorksInc%2Feach","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThoughtWorksInc%2Feach/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThoughtWorksInc%2Feach/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ThoughtWorksInc%2Feach/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ThoughtWorksInc","download_url":"https://codeload.github.com/ThoughtWorksInc/each/tar.gz/refs/heads/3.3.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247284954,"owners_count":20913704,"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":["annotations","comprehension","macro-library","monad","scala","scalaz","scalaz-monadic-expressions","thoughtworks"],"created_at":"2024-11-12T00:29:27.853Z","updated_at":"2025-04-05T04:14:49.276Z","avatar_url":"https://github.com/ThoughtWorksInc.png","language":"Scala","readme":"# ThoughtWorks Each \u003ca href=\"http://thoughtworks.com/\"\u003e\u003cimg align=\"right\" src=\"https://www.thoughtworks.com/imgs/tw-logo.png\" title=\"ThoughtWorks\" height=\"15\"/\u003e\u003c/a\u003e\n\n[![Join the chat at https://gitter.im/ThoughtWorksInc/each](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ThoughtWorksInc/each?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![Build Status](https://travis-ci.org/ThoughtWorksInc/each.svg?branch=3.3.x)](https://travis-ci.org/ThoughtWorksInc/each)\n[![Latest version](https://index.scala-lang.org/thoughtworksinc/each/each/latest.svg)](https://index.scala-lang.org/thoughtworksinc/each/each)\n\n**ThoughtWorks Each** is a macro library that converts native imperative syntax to [Scalaz](http://scalaz.org/)'s monadic expression. See the [object cats](https://javadoc.io/page/com.thoughtworks.dsl/dsl_2.12/latest/com/thoughtworks/dsl/domains/cats$.html) in [Dsl.scala](https://github.com/ThoughtWorksInc/Dsl.scala/) for the similar feature for [Cats](https://typelevel.org/cats/).\n\n## Motivation\n\nThere is a macro library [Stateless Future](https://github.com/qifun/stateless-future) that provides `await` for asynchronous programming.\n`await` is a mechanism that transform synchronous-like code into asynchronous expressions. C# 5.0, ECMAScript 7 and Python 3.5 also support the mechanism.\n\nThe `await` mechanism in Stateless Future is implemented by an algorithm called [CPS transform](https://en.wikipedia.org/wiki/Continuation-passing_style). When learning [scalaz](https://scalaz.github.io/scalaz/), we found that the same algorithm could be applied for any monadic expression, including `Option` monad, `IO` monad, and `Future` monad. So we started this project, Each.\n\nEach is a superset of `await` syntax. Each supports multiple types of monads, while `await` only works with `Future`. When we perform a CPS transform for monadic expression with the `Future` monad, the use case looks almost the same as the `await` syntax in [Stateless Future](https://github.com/qifun/stateless-future). Each is like F#'s [Computation Expressions](https://msdn.microsoft.com/en-us/library/dd233182.aspx), except Each reuses the normal Scala syntax instead of reinventing new syntax.\n\nFor example:\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.scalaFuture._\n\n// Returns a Future of the sum of the length of each string in each parameter Future,\n// without blocking any thread.\ndef concat(future1: Future[String], future2: Future[String]): Future[Int] = monadic[Future] {\n  future1.each.length + future2.each.length\n}\n```\n\nThe similar code works for monads other than `Future`:\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.option._\n\ndef plusOne(intOption: Option[Int]) = monadic[Option] {\n  intOption.each + 1\n}\nassert(plusOne(None) == None)\nassert(plusOne(Some(15)) == Some(16))\n```\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.list._\n\ndef plusOne(intSeq: List[Int]) = monadic[List] {\n  intSeq.each + 1\n}\nassert(plusOne(Nil) == Nil)\nassert(plusOne(List(15)) == List(16))\nassert(plusOne(List(15, -2, 9)) == List(16, -1, 10))\n```\n\n## Usage\n\n### Step 1: Add the following line in your build.sbt\n\n``` sbt\nlibraryDependencies += \"com.thoughtworks.each\" %% \"each\" % \"latest.release\"\n\naddCompilerPlugin(\"org.scalamacros\" % \"paradise\" % \"2.1.0\" cross CrossVersion.full)\n```\n\nor `%%%` for Scala.js projects:\n\n``` sbt\nlibraryDependencies += \"com.thoughtworks.each\" %%% \"each\" % \"latest.release\"\n\naddCompilerPlugin(\"org.scalamacros\" % \"paradise\" % \"2.1.0\" cross CrossVersion.full)\n```\n\nNote that ThoughtWorks Each requires Scalaz 7.2.x and does not compatible with Scala 7.1.x .\n\nSee https://repo1.maven.org/maven2/com/thoughtworks/each/ for a list of available versions.\n\n### Step 2: In your source file, import `monadic` and `each` method\n\n``` scala\nimport com.thoughtworks.each.Monadic._\n```\n\n### Step 3: Import implicit Monad instances\n\nScalaz has provided `Option` monad, so you just import it.\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.option._\n```\n\nPlease import other monad instances if you need other monads.\n\n### Step 4: Use `monadic[F]` to create a monadic expression\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.option._\nval result: Option[String] = monadic[Option] {\n  \"Hello, Each!\"\n}\n```\n\n### Step 5: In the `monadic` block, use `.each` postfix to extract each element in a `F`\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.option._\nval name = Option(\"Each\")\nval result: Option[String] = monadic[Option] {\n  \"Hello, \" + name.each + \"!\"\n}\n```\n\n## Exception handling\n\n`monadic` blocks do not support `try`, `catch` and `finally`. If you want these expressions, use `throwableMonadic` or `catchIoMonadic` instead, for example:\n\n``` scala\nvar count = 0\nval io = catchIoMonadic[IO] {\n  count += 1                // Evaluates immediately\n  val _ = IO(()).each       // Pauses until io.unsafePerformIO()\n  try {\n    count += 1\n    (null: Array[Int])(0)   // Throws a NullPointerException\n  } catch {\n    case e: NullPointerException =\u003e {\n      count += 1\n      100\n    }\n  } finally {\n    count += 1\n  }\n}\nassertEquals(1, count)\nassertEquals(100, io.unsafePerformIO())\nassertEquals(4, count)\n```\n\nNote that `catchIoMonadic` requires an implicit parameter `scalaz.effect.MonadCatchIO[F]` instead of `Monad[F]`. `scalaz.effect.MonadCatchIO[F]` is only provided for `scalaz.effect.IO` by default.\n\n## `for` loop\n\nEach supports `.each` magic in a `for` loop on any instances that support `Foldable` type class. For example, you could `import scalaz.std.list._` to enable the `Foldable` type class for  `List`.\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.list._\nimport scalaz.std.option._\nval n = Some(10)\n@monadic[Option] val result = {\n  var count = 1\n  for (i \u003c- List(300, 20)) {\n    count += i * n.each\n  }\n  count\n}\nAssert.assertEquals(Some(3201), result)\n```\n\nNote that you need to use `@monadic[Option]` annotation instead of `monadic[Option]` block to in order to enable the `for` loop syntax.\n\n## `for` comprehension\n\nEach also supports `.each` magic in a `for` comprehension on any instances that support `Traverse` and `MonadPlus` type class.\n\n``` scala\nimport com.thoughtworks.each.Monadic._\nimport scalaz.std.list._\nval n = Some(4000)\n@monadic[Option] val result = {\n  for {\n    i \u003c- List(300, 20)\n    (j, k) \u003c- List(50000 -\u003e \"1111\", 600000 -\u003e \"yyy\")\n    if i \u003e n.each - 3900\n    a = i + j\n  } yield {\n    a + n.each * k.length\n  }\n}\nAssert.assertEquals(Some(List(66300, 612300)), result)\n```\n\nNote that you need to use `@monadic[Option]` annotation instead of `monadic[Option]` block to in order to enable the `for` comprehension syntax.\n\n## Limitation\n\nIf a [call-by-name parameter](http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#function-applications) of a method call is a monadic expression, `Each` will transform the monadic expression before the method call. The behavior was discussed at [#37](https://github.com/ThoughtWorksInc/each/issues/37).\n\n```scala\ndef innerFailureFuture = Future.failed(new Exception(\"foo\"))\nval someValue = Some(\"value\")\nval result = monadic[Future] {\n  someValue.getOrElse(innerFailureFuture.each)\n}\n```\n\n`result` will be a future of failure because the above example equals to\n\n```scala\ndef innerFailureFuture = Future.failed(new Exception(\"foo\"))\nval someValue = Some(\"value\")\nval result = innerFailureFuture.map(someValue.getOrElse)\n```\n\n`innerFailureFuture.each` is evaluated before being passed to `getOrElse` method call, even if `getOrElse` accepts a call-by-name parameter.\n\n## Links\n\n * [The API Documentation](https://www.javadoc.io/doc/com.thoughtworks.each/each_2.13/latest/com/thoughtworks/each/Monadic$.html)\n * Utilities\n   * [ComprehensionMonad](https://github.com/ThoughtWorksInc/each/wiki/ComprehensionMonad)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthoughtworksinc%2Feach","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthoughtworksinc%2Feach","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthoughtworksinc%2Feach/lists"}