{"id":13906369,"url":"https://github.com/aztek/scala-workflow","last_synced_at":"2025-07-31T20:31:20.231Z","repository":{"id":7294649,"uuid":"8610567","full_name":"aztek/scala-workflow","owner":"aztek","description":"Boilerplate-free syntax for computations with effects","archived":false,"fork":false,"pushed_at":"2014-06-24T12:44:35.000Z","size":582,"stargazers_count":176,"open_issues_count":1,"forks_count":16,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-11-25T14:48:28.692Z","etag":null,"topics":["applicative-functors","functional-programming","functors","monads","scala","scala-macros"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aztek.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-03-06T18:56:21.000Z","updated_at":"2024-08-28T17:45:04.000Z","dependencies_parsed_at":"2022-08-31T22:31:01.084Z","dependency_job_id":null,"html_url":"https://github.com/aztek/scala-workflow","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aztek%2Fscala-workflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aztek%2Fscala-workflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aztek%2Fscala-workflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aztek%2Fscala-workflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aztek","download_url":"https://codeload.github.com/aztek/scala-workflow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228295605,"owners_count":17897596,"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":["applicative-functors","functional-programming","functors","monads","scala","scala-macros"],"created_at":"2024-08-06T23:01:34.382Z","updated_at":"2024-12-05T12:07:48.998Z","avatar_url":"https://github.com/aztek.png","language":"Scala","funding_links":[],"categories":["Scala"],"sub_categories":[],"readme":"Scala workflow [![Travis CI Status](https://api.travis-ci.org/aztek/scala-workflow.png)](https://travis-ci.org/aztek/scala-workflow)\n==============\n`scala-workflow` helps to nicely organize applicative and monadic computations\nin Scala with 2.11 macros, resembling _`for`-comprehension_ and some enhanced\nversion of _idiom brackets_.\n\n`scala-workflow` only requires [untyped macros](http://docs.scala-lang.org/overviews/macros/untypedmacros.html)\nthat is an experimental feature of [Macro Paradise](http://docs.scala-lang.org/overviews/macros/paradise.html).\n\n```\n$ git clone https://github.com/aztek/scala-workflow\n$ cd scala-workflow\n$ sbt console\nimport scala.workflow._\nWelcome to Scala version 2.11.0\nType in expressions to have them evaluated.\nType :help for more information.\n\nscala\u003e $[List](List(1, 2) * List(4, 5))\nres0: List[Int] = List(4, 5, 8, 10)\n```\n\nContents\n--------\n*   [Quick start](#quick-start)\n*   [Workflows](#workflows)\n    *   [Hierarchy of workflows](#hierarchy-of-workflows)\n    *   [Rewriting rules](#rewriting-rules)\n    *   [Context definition](#context-definition)\n    *   [Composing workflows](#composing-workflows)\n*   [Examples](#examples)\n    *   [Evaluator for a language of expressions](#evaluator-for-a-language-of-expressions)\n    *   [Functional reactive programming](#functional-reactive-programming)\n    *   [Monadic interpreter for stack programming language](#monadic-interpreter-for-stack-programming-language)\n    *   [Point-free notation](#point-free-notation)\n    *   [Purely functional logging](#purely-functional-logging)\n*   [Disclaimer](#disclaimer)\n\nQuick start\n-----------\nImport `workflow` interface.\n\n```scala\nimport workflow._\n```\n\nYou will now have three macros at hand — `context`, `$` and `workflow`. Wrap a\nblock of code in `context` and the argument of `$` will begin to act funny.\n\n```scala\ncontext[Option] {\n  $(Some(42) + 1) should equal (Some(43))\n  $(Some(10) + Some(5) * Some(2)) should equal (Some(20))\n}\n\ncontext[List] {\n  $(List(1, 2, 3) * 2) should equal (List(2, 4, 6))\n  $(List(\"a\", \"b\") + List(\"x\", \"y\")) should equal (List(\"ax\", \"ay\", \"bx\", \"by\"))\n}\n```\n\nEnclosing `context` macro takes type constructor `F[_]` as an argument and\nmakes all the `$`s inside evaluate their arguments as if everything of type\n`F[T]` there was of type `T`, except it retains _computational effects_,\nassociated with `F`.\n\nThe exact rules of evaluation are defined in an object, that extends special\n`scala.workflow.Workflow[F[_]]` trait. Such objects can either be implicitly\nsummoned by type constructor name (like the ones shown above), or passed\nexplicitly, like `zipList` below.\n\n```scala\ncontext(zipList) {\n  $(List(1, 2, 3, 4) * List(2, 3, 4)) should equal (List(2, 6, 12))\n}\n```\n\nThere are numerous of `Workflow[F[_]]` objects you can find in `instances.scala`\nfile. All of them are instances of _functors_, _applicative functors_ (also\ncalled _idioms_), _monads_ and a couple of other intermediate algebraic\nstructures.\n\n```scala\ncontext(map[String]) {\n  $(Map(\"foo\" → 10, \"bar\" → 5) * 2) should equal (Map(\"foo\" → 20, \"bar\" → 10))\n}\n\ncontext(function[String]) {\n  val chars   = (s: String) ⇒ s.length\n  val letters = (s: String) ⇒ s.count(_.isLetter)\n  val nonletters = $(chars - letters)\n  nonletters(\"R2-D2\") should equal (3)\n}\n```\n\nYou can pass complex blocks of code to `$`.\n\n```scala\ndef divide(x: Double, y: Double) = if (y == 0) None else Some(x / y)\n\ncontext[Option] {\n  $ {\n    val x = divide(1, 2)\n    val y = divide(4, x)\n    divide(y, x)\n  } should equal (Some(16))\n}\n```\n\nNested `context` and `$` calls might look awkward, so instead you can use\nspecial syntactic sugar, called `workflow`.\n\n```scala\nworkflow[Option] {\n  val x = divide(1, 2)\n  val y = divide(4, x)\n  divide(y, x)\n} should equal (Some(16))\n```\n\nJust like in `context`, you can pass either type constructor or workflow\nobject.\n\nWorkflows\n---------\nThe goal of `scala-workflow` is to provide boilerplate-free syntax for\ncomputations with effects, encoded with monads and idioms. _Workflow_\nabstracts the concept of computation in effectful context.\n\nInstances of `Workflow` trait provide methods, that are used for desugaring\ncode in correspondent effectful contexts. The more methods an instance has,\nthe more powerful it is, and the richer language features can be used.\n\nThe ultimate goal is to support the whole set of Scala language features. For\nnow, however, only literals, function applications and `val` definitions are\nsupported. But development of the project is still in progress and you are very\nwelcome to contribute.\n\n### Hierarchy of workflows\nThe hierarchy of workflows is built around an empty `Workflow[F[_]]` trait and\nseveral derived traits, that add methods to it. So far there are four of them:\n\n```scala\ntrait Pointing[F[_]] extends Workflow[F] {\n  def point[A](a: ⇒ A): F[A]\n}\n\ntrait Mapping[F[_]] extends Workflow[F] {\n  def map[A, B](f: A ⇒ B): F[A] ⇒ F[B]\n}\n\ntrait Applying[F[_]] extends Workflow[F] with Mapping[F] {\n  def app[A, B](f: F[A ⇒ B]): F[A] ⇒ F[B]\n}\n\ntrait Binding[F[_]] extends Workflow[F] {\n  def bind[A, B](f: A ⇒ F[B]): F[A] ⇒ F[B]\n}\n```\n\nEach method corresponds to a particular feature of workflow context.\n- `point` allows to put pure value inside the workflow. It is only generated\n  when you call `$(a)` for some pure `a`.\n\n- `map` is used to map over one lifted value in an expression. This is the\n  same `map` you can find in Scalas `List`, `Option` and other classes.  \n\n- `app` is used to map over more that one independently lifted values within a\n  context. \"Independently\" means that you can evaluate lifted arguments in any\n  order. Example of an expression with lifted values that depend on each other:\n  `$(divide(divide(1, 2), 3))` (with `divide` definition taken from \"Quick start\"\n  section). Both `divide` calls are lifted, but we can only evaluate outmost\n  `divide` after the inner one has been evaluated. Note, that `app` cannot be used\n  without `map`, hence the inheritance of the traits.\n\n- `bind` is used to desugar expressions with arbitrary many arbitrary dependent\n  lifted values.\n\nYou can define workflow instances simply by mixing above-mentioned traits, or\nusing one of predefined shortcuts, representing commonly used algebraic\nstructures.\n\n```scala\ntrait Functor[F[_]] extends Mapping[F]\n\ntrait SemiIdiom[F[_]] extends Functor[F] with Applying[F]\n\ntrait Idiom[F[_]] extends SemiIdiom[F] with Pointing[F] {\n  def map[A, B](f: A ⇒ B) = app(point(f))\n}\n\ntrait SemiMonad[F[_]] extends SemiIdiom[F] with Binding[F]\n\ntrait Monad[F[_]] extends Idiom[F] with Binding[F] {\n  def app[A, B](f: F[A ⇒ B]) = bind(a ⇒ bind((g: A ⇒ B) ⇒ point(g(a)))(f))\n}\n```\n\nNote, that `Functor`/`Idiom`/`Monad` is merely a shortcut. You are not required\nto implement any of it particularly to be able to use workflow contexts. They\nare mostly convenient, because have some of the methods already implemented and\ncan be [composed](#composing-workflows).\n\n### Rewriting rules\nOne important difference of `scala-workflow` from similar syntactic extension\nis that it always require the least powerful interface of a workflow instance\nfor generated code. That means, that you can have idiom brackets kind of syntax\nfor functors (such as `Map[A, B]`) and `for`/`do`-notation kind of syntax for\nmonads without `return` (they are called `SemiMonad`s here).  \n\nCurrent implementation uses untyped macros and takes untyped Scala AST as an\nargument. Then we start by eagerly typechecking all the subexpressions (i.e.,\nstarting with the most nested subexpressions) and find, which of them\ntypechecks successfully with the result type corresponding to the type of the\nworkflow. If those are found, they are being replaces with their non-lifted\ncounterparts, and the whole thing starts over again, until the whole expression\ntypechecks correctly and there is a list of lifted values at hand.\n\nConsider the example below.\n\n```scala\ncontext(option) {\n  $(2 * 3 + Some(10) * Some(5))\n}\n```\n\nAll the numbers typecheck and are not inside `Option`, so they are left as is.\n`2 * 3` also typechecks to `Int` and is left as is. `Some(10)` and `Some(5)`\nboth typechecks to `Option[Int]` (well, technically, `Some[Int]`, but we can\nhandle that), so both arguments are lifted.\n\nGenerated code will look like this.\n\n```scala\noption.app(\n  option.map(\n    (x$1: Int) ⇒ (x$2: Int) ⇒\n      2 * 3 + x$1 * x$2\n  )(Some(10))\n)(Some(5))\n```\n\nSpecial analysis takes care of dependencies between lifted values to be sure to\nproduce `bind` instead of `app` where needed.\n\nHere are some of other examples of code rewriting within `Option` context.\n\n#### Simple expressions\n\u003ctable\u003e\n   \u003ctr\u003e\n      \u003cth\u003eInside the \u003ccode\u003e$\u003c/code\u003e\u003c/th\u003e\n      \u003cth\u003eGenerated code\u003c/th\u003e\n      \u003cth\u003ePure Scala counterpart\u003c/th\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003ccode\u003e$(42)\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.point(42)\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eSome(42)\u003c/pre\u003e\u003c/th\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003ccode\u003e$(Some(42) + 1)\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.map(\n  (x$1: Int) ⇒\n    x$1 + 1\n)(Some(42))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(42)\n} yield x + 1\u003c/pre\u003e      \n      \u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003ccode\u003e$(Some(2) * Some(3))\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.app(\n  option.map(\n    (x$1: Int) ⇒\n      (x$2: Int) ⇒\n        x$1 * x$2\n  )(Some(2))\n)(Some(3))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(2)\n  y ← Some(3)\n} yield x * y\u003c/pre\u003e      \n      \u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003ccode\u003e$(divide(1, 2))\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003edivide(1, 2)\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003edivide(1, 2)\u003c/pre\u003e\u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003ccode\u003e$(divide(Some(1.5), 2))\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.bind(\n  (x$1: Double) ⇒\n    divide(x$1, 2)\n)(Some(1.5))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(1.5)\n  y ← divide(x, 2)\n} yield y\u003c/pre\u003e      \n      \u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003ccode\u003e$(divide(Some(1.5), Some(2)))\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.bind(\n  (x$1: Double) ⇒\n    option.bind(\n      (x$2: Int) ⇒\n        divide(x$1, x$2)\n    )(Some(2))\n)(Some(1.5))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(1.5)\n  y ← Some(2)\n  z ← divide(x, y)\n} yield z\u003c/pre\u003e      \n      \u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003ccode\u003e$(divide(Some(1.5), 2) + 1)\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.bind(\n  (x$1: Double) ⇒\n    option.map(\n      (x$2: Double) ⇒\n        x$2 + 1\n    )(divide(x$1, 2))\n)(Some(1.5))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(1.5)\n  y ← divide(x, 2)\n} yield y + 1\u003c/pre\u003e      \n      \u003c/td\u003e\n   \u003c/tr\u003e\n\u003c/table\u003e\n\n#### Blocks of code\n\u003ctable\u003e\n   \u003ctr\u003e\n      \u003cth\u003eInside the \u003ccode\u003e$\u003c/code\u003e\u003c/th\u003e\n      \u003cth\u003eGenerated code\u003c/th\u003e\n      \u003cth\u003ePure Scala counterpart\u003c/th\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003cpre\u003e$ {\n  val x = Some(10)\n  x + 2\n}\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.map(\n  (x$1: Int) ⇒\n    x$1 + 2\n)(Some(10))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(10)\n} yield x + 2\u003c/pre\u003e\u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003cpre\u003e$ {\n  val x = Some(10)\n  val y = Some(5)\n  x + y\n}\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.bind(\n  (x$1: Int) ⇒\n    option.map(\n      (x$2: Int) ⇒\n        x$1 + x$2\n    )(Some(5))\n)(Some(10))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(10)\n  y ← Some(5)\n} yield x + y\u003c/pre\u003e\u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003cpre\u003e$ {\n  val x = Some(10)\n  val y = x − 3\n  x * y\n}\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.map(\n  (x$1: Int) ⇒\n    val y = x$1 − 3\n    x$1 * y\n)(Some(10))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  x ← Some(10)\n  y = x - 3\n} yield x * y\u003c/pre\u003e\u003c/td\u003e\n   \u003c/tr\u003e\n   \u003ctr\u003e\n      \u003ctd\u003e\u003cpre\u003e$(2 + {\n  val x = Some(10)\n  x * 2\n})\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003eoption.map(\n  (x$1: Int) ⇒\n    2 + x$1\n)(option.map(\n  (x$2: Int) ⇒\n    x$2 * 2\n)(Some(10)))\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003cpre\u003efor {\n  y ← for {\n    x ← Some(10)\n  } yield x * 2\n} yield 2 + y\u003c/pre\u003e\u003c/td\u003e\n   \u003c/tr\u003e\n\u003c/table\u003e\n\n### Context definition\nWorkflow context is defined with `context` macro that either takes a workflow\ninstance as an argument, or a type constructor `F[_]`, such that there is some\nworkflow instance defined somewhere in the implicits scope.\n\nThe following examples are equivalent.\n\n```scala\ncontext[List] {\n  $(List(2, 5) * List(3, 7))\n}\n\ncontext(list) {\n  $(List(2, 5) * List(3, 7))\n}\n```\n\nMacro `$` takes workflow context from the closest `context` block.\nAlternatively, you can provide type constructor, whose workflow instance will\nbe taken from the implicits scope.\n\n```scala\n$[List](List(2, 5) * List(3, 7))\n```\n\nThat way, `$` will disregard any enclosing `context` block and will work within\n`Workflow[List]` context.\n\nNested applications of `context` and `$` can be replaced with `workflow` macro.\nYou are encouraged to do so for complex blocks of code. You are discourage to\nuse `$` inside. `workflow` either takes a workflow instance as an argument, or\na type constructor `F[_]` and rewrites the block of code in the same way as `$`.\n\n```scala\nworkflow(list) { List(2, 5) * List(3, 7) }\n```\n\nThere are plenty of built-in workflow instances in traits `FunctorInstances`,\n`SemiIdiomInstances`, `IdiomInstances` and `MonadInstances`. They are all\nmixed to the package object of `scala.workflow`, so once you have `workflow._`\nimported, you get access to all of them. Alternatively, you can import just the\nmacros `import workflow.{context, workflow, $}` and access workflow instances\nfrom `Functor`, `SemiIdiom`, `Idiom` and `Monad` objects.\n\n### Composing workflows\nAny pair of functors, semi-idioms or idioms can be composed. That is, for any\ntype constructors `F[_]` and `G[_]` one can build instances of `Functor[F[G[_]]]`\nand `Functor[G[F[_]]]` with instances of `Functor[F]` and `Functor[G]` (the same\nfor semi-idioms and idioms).\n\nThe syntax for workflow composition is `f $ g`, where `f` and `g` are functor,\nsemi-idiom or idiom instances of type constructors `F[_]` and `G[_]`. The result\nwould be, correspondingly, functor, semi-idiom or idiom of type constructor\n`F[G[_]]`.\n\n```scala\ncontext(list $ option) {\n  $(List(Some(2), Some(3), None) * 10) should equal (List(Some(20), Some(30), None))\n}\n```\n\nYou can also combine workflows of different classes with the same syntax, the\nresult workflow will implement the weaker interface of the two. For instance,\n`map[String] $ option` will implement `Functor`, because `map`s `Functor` is\nweaker than `option`s `Monad`.\n\n`$` method has a counterpart method `\u0026`, that produces `G[F[_]]` workflow\ninstance. Naturally, `f $ g = g \u0026 f`.\n\nMonads and semi-monads in general cannot be composed. Instead, some particular\nmonads can provide their own specific implementation of `$` or `\u0026` method.\nHowever, even having, say, `$` method defined, monad might not be able to\ndefine `\u0026`. That is, `Monad[F]` can either construct `Monad[F[G[_]]` or\n`Monad[G[F[_]]]` with arbitrary `Monad[G]`.\n\nTo capture this notion, `Monad[F]` can be extended to either\n`LeftComposableMonad[F]` or `RightComposableMonad[F]`. In the first case it is\nsupposed to be able to produce `Monad[F[G[_]]]` and therefore implement `$`\nmethod. In the second case it is supposed to be able to produce `Monad[G[F[_]]]`\nand therefore implement `$` method.\n\nFor example, `Option` is _right-composable_, i.e. can implement\n`def \u0026 [G[_]](g: Monad[G]): Monad[G[Option[_]]]`, whereas\nfunction monad `R ⇒ _` is _left-composable_ and can implement\n`def $ [G[_]](g: Monad[G]): Monad[R ⇒ G[_]]`.\n\nSo, for monads `f` and `g` their composition `f $ g` will be a monad either\nwhen `f` is left-composable or `g` is right-composable or visa versa for `\u0026`.\nWhen in the expression `f $ g` `f` is left-composable and `g` is right-composable,\n`f`'s `$` method will be used to construct a composition. When in the expression\n`g \u0026 f` `f` is left-composable and `g` is right-composable, `g`'s `\u0026` method will\nbe used to construct a composition.\n\nThe same holds for semi-monads.\n\nCheck [`instances.scala`](https://github.com/aztek/scala-workflow/blob/master/src/main/scala/scala/workflow/instances.scala)\nto ensure, which monads are left or right composable and also [composition specs](https://github.com/aztek/scala-workflow/blob/master/src/test/scala/scala/workflow/CompositionSpec.scala)\nfor exact rules of composition of different workflow classes.\n\nMethods `$` and `\u0026` are called _monad transformer_ elsewhere, although\nseparation of left and right composability is usually not introduced.\n\nExamples\n--------\n### Evaluator for a language of expressions\n[Original paper](http://strictlypositive.org/IdiomLite.pdf) by McBride and\nPatterson, that introduced idiom brackets, describes a simple evaluator for a\nlanguage of expressions. Following that example, here's how it would look like\nhere.\n\nWe start with the definition of abstract syntax tree of a language with\nintegers, variables and a plus.\n\n```scala\nsealed trait Expr\ncase class Var(id: String) extends Expr\ncase class Val(value: Int) extends Expr\ncase class Add(lhs: Expr, rhs: Expr) extends Expr\n```\n\nVariables are fetched from the environment of type `Env`.\n\n```scala\ntype Env = Map[String, Int]\ndef fetch(x: String)(env: Env): Option[Int] = env.get(x)\n```\n\nThe evaluator itself is a function of type `Expr ⇒ Env ⇒ Option[Int]`.\n\n```scala\ndef eval(expr: Expr)(env: Env): Option[Int] =\n  expr match {\n    case Var(x) ⇒ fetch(x)(env)\n    case Val(value) ⇒ Some(value)\n    case Add(x, y) ⇒ for {\n      lhs ← eval(x)(env)\n      rhs ← eval(y)(env)\n    } yield lhs + rhs\n  }\n```\n\nNote, that one have to explicitly pass the environment around and to have\nrather clumsy syntax to compute the addition. This can be simplified, once\nwrapped into the workflow `Env ⇒ Option[_]`, which can either be constructed\nby hand, or composed of `Env ⇒ _` and `Option` workflows.\n\n```scala\ndef eval: Expr ⇒ Env ⇒ Option[Int] =\n  context(function[Env] $ option) {\n    case Var(x) ⇒ fetch(x)\n    case Val(value) ⇒ $(value)\n    case Add(x, y) ⇒ $(eval(x) + eval(y))\n  }\n```\n\n### Functional reactive programming\n`scala-workflow` can be used as syntactic sugar for external libraries and\nprogramming paradigms. In this example, a very simple version of [Functional\nreactive programming](http://en.wikipedia.org/wiki/Functional_reactive_programming)\nframework is implemented as `Idiom` instance.\n\n`Cell` trait defines a unit of data, that can be assigned with `:=` and fetched\nwith `!`. Cells can depend on each others values, much like they do in\nspreadsheets.\n\n```scala\ntrait Cell[T] {\n  def ! : T\n  def := (value: T) { throw new UnsupportedOperationException }\n}\n```\n\nWorkflow instance, implemented as `Idiom`, defines cells, that either contain\natomic value that can be reassign or dependent cells, that take value of some\nother cell to compute their own (reassigning them doesn't make sense, hence\nthe exception).\n\n```scala\nval frp = new Idiom[Cell] {\n  def point[A](a: ⇒ A) = new Cell[A] {\n    private var value = a\n    override def := (a: A) { value = a }\n    def ! = value\n  }\n  def app[A, B](f: Cell[A ⇒ B]) = a ⇒ new Cell[B] {\n    def ! = f!(a!)\n  }\n}\n```\n\nWith that instance we can organize reactive computations with simple syntax.\n\n```scala\ncontext(frp) {\n  val a = $(10)\n  val b = $(5)\n\n  val c = $(a + b * 2)\n\n  (c!) should equal (20)\n\n  b := 7\n\n  (c!) should equal (24)\n}\n```\n\n### Monadic interpreter for stack programming language\nIf you enjoy embedding monadic domain-specific languages in your Scala programs,\nyou might like syntactical perks `scala-workflow` could offer. Consider a\nlittle embedded stack programming language.\n\nWe represent stack as a regular `List`, and the result of a program, that\nmanipulates with a stack, as either a modified stack or an error message\n(such as \"stack underflow\").\n\n```scala\ntype Stack = List[Int]\ntype State = Either[String, Stack]\n```\n\nThe evaluation of the program will use `state` monad, that will disregard\nthe result of any command, but preserve the state of the stack.\n\n```scala\nval stackLang = state[State]\n```\n\nUnlike State monad in Haskell or `scalaz`, `state` doesn't use any specific\nwrappers to represent the result of the computation, but rather works with bare\nfunctions.\n\nWe would like to define stack operators as `Stack ⇒ State` functions. To be\nable to use them within some `workflow`, we need to lift them into the monad.\nIn this example we are only interested in the modified state of the computation,\nso the result is always set to `()`.\n\n```scala\ndef command(f: Stack ⇒ State) = (st: State) ⇒ ((), either[String].bind(f)(st))\n```\n\nWith `command` helper we can now define a bunch of commands, working with stack.\n\n```scala\ndef put(value: Int) = command {\n  case stack ⇒ Right(value :: stack)\n}\n\ndef dup = command {\n  case a :: stack ⇒ Right(a :: a :: stack)\n  case _ ⇒ Left(\"Stack underflow while executing `dup`\")\n}\n\ndef rot = command {\n  case a :: b :: stack ⇒ Right(b :: a :: stack)\n  case _ ⇒ Left(\"Stack underflow while executing `rot`\")\n}\n\ndef sub = command {\n  case a :: b :: stack ⇒ Right((b - a) :: stack)\n  case _ ⇒ Left(\"Stack underflow while executing `sub`\")\n}\n```\n\nExecution of the program on the empty stack simply takes the modified stack.\n\n```scala\ndef execute(program: State ⇒ (Unit, State)) = {\n  val (_, state) = program(Right(Nil))\n  state\n}\n```\n\nNow, working inside `stackLang` context we can write programs as sequences of\nstack commands and execute them to get modified state of the stack.\n\n```scala\ncontext(stackLang) {\n  val programA = $ { put(5); dup; put(7); rot; sub }\n  execute(programA) should equal(Right(List(2, 5)))\n\n  val programB = $ { put(5); dup; sub; rot; dup }\n  execute(programB) should equal(Left(\"Stack underflow while executing `rot`\"))\n}\n```\n\n### Point-free notation\nIf you're familiar with [SKI-calculus](http://en.wikipedia.org/wiki/SKI_combinator_calculus),\nyou might notice, that `point` and `app` methods of `function[R]` workflow \nare in fact `K` and `S` combinators. This means that you can construct any\nclosed lambda-term (in other words, any function) with just those two methods.\n\nFor instance, here's how you can define `I`-combinator (the identity function):\n\n```scala\n// I = S K K\ndef id[T] = function[T].app(function[T ⇒ T].point)(function[T].point)\n```\n\nOr `B`-combinator (Scalas `Function.compose` method or Haskells `(.)`):\n\n```scala\n// B = S (K S) K\ndef b[A, B, C] = function[A ⇒ B].app(function[A ⇒ B].point(function[C].app[A, B]))(function[C].point)\n// More concisely with map\ndef b[A, B, C] = function[A ⇒ B].map(function[C].app[A, B])(function[C].point)\n```\n\nAside from mind-boggling examples like that, there's actually a useful\napplication for these workflows — they can be used in point-free notation,\ni.e. for construction of complex functions with no function arguments\nspecified.\n\nPoint-free notation support is rather limited in Scala, compared to Haskell,\nbut some things still could be done. Here are some examples to get you inspired.\n\n```scala\ncontext(function[Char]) {\n  val isLetter: Char ⇒ Boolean = _.isLetter\n  val isDigit:  Char ⇒ Boolean = _.isDigit\n\n  // Traditionally\n  val isLetterOrDigit = (ch: Char) ⇒ isLetter(ch) || isDigit(ch)\n\n  // Combinatorially\n  val isLetterOrDigit = $(isLetter || isDigit)\n}\n```\n\nScalas `Function.compose` and `Function.andThen` also come in handy.\n\n```scala\ncontext(function[Double]) {\n  val sqrt: Double ⇒ Double = x ⇒ math.sqrt(x)\n  val sqr:  Double ⇒ Double = x ⇒ x * x\n  val log:  Double ⇒ Double = x ⇒ math.log(x)\n\n  // Traditionally\n  val f = (x: Double) ⇒ sqrt((sqr(x) - 1) / (sqr(x) + 1))\n\n  // Combinatorially\n  val f = sqrt compose $((sqr - 1) / (sqr + 1))\n\n  // Traditionally\n  val g = (x: Double) ⇒ (sqr(log(x)) - 1) / (sqr(log(x)) + 1)\n\n  // Combinatorially\n  val g = log andThen $((sqr - 1) / (sqr + 1))\n}\n```\n\n### Purely functional logging\nIt is no secret for a functional programmer that monads are extremely powerful.\nIn fact, most of the features of imperative programming languages, that are\nusually implemented with variations of mutable state and uncontrolled side\neffects can be expressed with monads. However, functional purity often comes\nwith the price of rather cumbersome syntax, compared to equivalent imperative\nconstructs.\n\n`scala-workflow` among other things tries to bridge this gap. In this example\nit is used to allow snippets of imperative-looking code to mix pure computations\nand logging in purely functional manner.\n\nWorkflow instance for this example will be `accumulator`.\n\n```scala\nval logging = accumulator[List[String]]\n```\n\nAccumulator monad (called `Writer` monad elsewhere) captures a computation that\naside from producing the result, accumulates some auxiliary output. In this case\nit's message in a log, represented as plain list of strings. In general case,\ntype with defined [`Monoid`](https://github.com/aztek/scala-workflow/blob/master/src/main/scala/scala/workflow/auxiliary.scala) instance is expected.\n\nRegular functions naturally don't produce any log messages in `accumulator`\nmonad sense. To produce log message, a function must return a tuple of result\nof the computation and a list.\n\n```scala\ndef mult(x: Int, y: Int) = (x * y, List(s\"Calculating $x * $y\"))\n```\n\nFunctions, that produce no result and some log messages should return a tuple\nwith unit result.\n\n```scala\ndef info(message: String) = (Unit, List(message))\n```\n\nHaving a snippet of code wrapped in `workflow(logging)`, we can intersperse\npure computations with writing to a log, much like we would do with `log4j` or\nsimilar framework.\n\n```scala\nval (result, log) = workflow(logging) {\n  info(\"Lets define a variable\")\n  val x = 2\n\n  info(\"And calculate a square of it\")\n  val square = mult(x, x)\n\n  info(\"Also a cube and add them together\")\n  val cube = mult(mult(x, x), x)\n  val sum = square + cube\n\n  info(\"This is all so silly\")\n  sum / 2\n}\n```\n\nThe result of computation is now stored in `result` and the log is in\n`log`. Note, that no side effects or mutable state whatsoever were\ninvolved in this example.\n\n```scala\nresult should equal (6)\nlog should equal (List(\"Lets define a variable\",\n                       \"And calculate a square of it\",\n                       \"Calculating 2 * 2\",\n                       \"Also a cube and add them together\",\n                       \"Calculating 2 * 2\",\n                       \"Calculating 4 * 2\",\n                       \"This is all so silly\"))\n```\n\nDisclaimer\n----------\nThis project is very experimental and your comments and suggestions are highly\nappreciated. Drop me a line [on twitter](http://twitter.com/aztek) or\n[by email](mailto:evgeny.kotelnikov@gmail.com), or [open an issue](./issues/new)\nhere on GitHub. I'm also occasionally on #scala IRC channel on Freenode.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faztek%2Fscala-workflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faztek%2Fscala-workflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faztek%2Fscala-workflow/lists"}