{"id":13800259,"url":"https://github.com/outr/reactify","last_synced_at":"2025-04-05T15:08:57.163Z","repository":{"id":13116806,"uuid":"73644305","full_name":"outr/reactify","owner":"outr","description":"The first and only true Functional Reactive Programming framework for Scala.","archived":false,"fork":false,"pushed_at":"2025-03-23T00:16:45.000Z","size":388,"stargazers_count":88,"open_issues_count":5,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-29T14:11:44.566Z","etag":null,"topics":["frp","frp-library","reactive","rx","scala"],"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/outr.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":"2016-11-13T21:31:10.000Z","updated_at":"2025-01-31T00:52:59.000Z","dependencies_parsed_at":"2024-04-18T17:33:33.466Z","dependency_job_id":"0c08fd7e-8856-41d2-bb54-405ba99b8e7d","html_url":"https://github.com/outr/reactify","commit_stats":null,"previous_names":[],"tags_count":58,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Freactify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Freactify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Freactify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outr%2Freactify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outr","download_url":"https://codeload.github.com/outr/reactify/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247353746,"owners_count":20925329,"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":["frp","frp-library","reactive","rx","scala"],"created_at":"2024-08-04T00:01:10.872Z","updated_at":"2025-04-05T15:08:57.142Z","avatar_url":"https://github.com/outr.png","language":"Scala","funding_links":[],"categories":["Functional Programming"],"sub_categories":[],"readme":"# reactify\n\n[![CI](https://github.com/outr/reactify/actions/workflows/ci.yml/badge.svg)](https://github.com/outr/reactify/actions/workflows/ci.yml)\n[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/outr/reactify)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.outr/reactify_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.outr/reactify_2.12)\n[![Latest version](https://index.scala-lang.org/outr/reactify/reactify/latest.svg)](https://index.scala-lang.org/outr/reactify)\n[![Javadocs](https://javadoc.io/badge/com.outr/reactify_2.12.svg)](https://javadoc.io/doc/com.outr/reactify_2.12)\n\nThe first and only true Functional Reactive Programming framework for Scala.\n\n## Justification\n\nHow can we say it's the first true FRP framework for Scala? In all other frameworks they add special framework-specific\nfunctions to do things like math (ex. adding two variables together with a special `+` method), collection building (ex. a\nspecial implementation of `:::` to concatenate two variables containing lists), or similar mechanisms to Scala's built-in\ncollection manipulation (ex. `map`). These are great and mostly fill in the gaps necessary to solve your problems. But\nthe goal for Reactify was a bit loftier. We set out to create a system that actually allows you to use ANY Scala\nfunctionality just like you would normally without any special magic (like Scala.rx's special operations require:\nhttps://github.com/lihaoyi/scala.rx#additional-operations).\n\nIn Reactify you just write code like you normally would and as used `Var`s and `Val`s change the reactive properties they\nhave been assigned to will update as well. If you need a bit more clarification on just what the heck we mean, jump ahead\nto the [More AdvancedExamples](https://github.com/outr/reactify#more-advanced-examples).\n\n## Setup\n\nreactify is published to Sonatype OSS and Maven Central currently supporting:\n- Scala, Scala.js, and Scala Native (2.11, 2.12, 2.13, 3.x)\n\nConfiguring the dependency in SBT simply requires:\n\n```\nlibraryDependencies += \"com.outr\" %% \"reactify\" % \"4.1.3\"\n```\n\nor, for Scala.js / Scala Native / cross-building:\n\n```\nlibraryDependencies += \"com.outr\" %%% \"reactify\" % \"4.1.3\"\n```\n\n## Concepts\n\nThis framework is intentionally meant to be a simplistic take on properties and functional reactive concepts. There are\nonly four specific classes that really need be understood to take advantage of the framework:\n\n- Reactive - As the name suggests it is a simple trait that fires values that may be reacted to by `Reaction`s.\n- Channel - The most simplistic representation of a `Reactive`, simply provides a public `:=` to fire events. No state\nis maintained.\n- Val - Exactly as it is in Scala, this is a final variable. What is defined at construction is immutable. However, the\ncontents of the value, if they are `Reactive` may change the ultimate value of this, so it is is `Reactive` itself\nand holds state.\n- Var - Similar to `Val` except it is mutable as it mixes in `Channel` to allow setting of the current value.\n\n`Val` and `Var` may hold formulas with `Reactive`s. These `Reactive`s are listened to when assigned so the wrapping\n`Val` or `Var` will also fire an appropriate event. This allows complex values to be built off of other variables.\n\n## Using\n\n### Imports\n\nProps is a very simple framework and though you'll likely want access to some of the implicit conversions made available\nin the package, everything can be had with a single import:\n\n```\nimport reactify._\n```\n\n### Creating\n\nAs discussed in the concepts there are only four major classes in Props (`Reactive`, `Channel`, `Val`, and `Var`). Of\nthose classes, unless you are creating a custom `Reactive` you will probably only deal with the latter three.\n\nCreating instances is incredibly simple:\n\n```\nval myChannel = Channel[String]           // Creates a Channel that receives Strings\nval myVar = Var[Int](5)                   // Creates a Var containing the explicit value `5`\nval myVal = Val[Int](myVar + 5)           // Create a Val containing the sum of `myVar` + `5`\n```\n\n### Listening for Changes\n\nThis would all be pretty pointless if we didn't have the capacity to listen to changes on the values. Here we're going\nto listen to `myVal` and `println` the new value when it changes:\n\n```\nmyVal.attach { newValue =\u003e\n  println(s\"myVal = $newValue\")\n}\n```\n\n### Modifying the Value\n\nSince `myVal` is a `Val` it is immutable itself, but its value is derived from the formula `myVar + 5`. This means that\na change to `myVar` will cause the value of `myVal` to change as a result:\n\n```\nmyVar := 10\n```\n\nThe above code modifies `myVar` to have the new value of `10`. This will also cause `myVal` to re-evaluate and have the\nnew value of `15` (`myVar + 5`). As a result, the observer we attached above will output:\n\n```\nmyVal = 15\n```\n\n### Derived Values\n\nYou can do clever things like define a value that is derived from other values:\n\n```\nval a = Var(\"One\")\nval b = Var(\"Two\")\nval c = Var(\"Three\")\n\nval list = Val(List(a, b, c))\nlist()      // Outputs List(\"One\", \"Two\", \"Three\")\n\na := \"Uno\"\nb := \"Dos\"\nc := \"Tres\"\n\nlist()      // Outputs List(\"Uno\", \"Dos\", \"Tres\")\n```\n\n### More Advanced Examples\n\nThis is all pretty neat, but it's the more complex scenarios that show the power of what you can do with Reactify:\n\n#### Complex Function with Conditional\n\n```scala\nval v1 = Var(10)\nval v2 = Var(\"Yes\")\nval v3 = Var(\"No\")\nval complex = Val[String] {\n    if (v1 \u003e 10) {\n      v2\n    } else {\n      v3\n    }\n}\n```\n\nAny changes to `v1`, `v2`, or `v3` will fire a change on `complex` and the entire inlined function will be re-evaluated.\n\n#### Multi-Level Reactive\n\nA much more advanced scenario is when you have a `Var` that contains a `class` that has a `Var` and you want to keep track\nof the resulting value no matter what the first `Var`'s instance is currently set to.\n\nConsider the following two classes:\n\n```scala\nclass Foo {\n  val active: Var[Boolean] = Var(false)\n}\nclass Bar {\n  val foo: Var[Option[Foo]] = Var[Option[Foo]](None)\n}\n```\n\nA `Bar` has a `Var` `foo` that holds an `Option[Foo]`. Now, say I have a `Var[Option[Bar]]`:\n\n```scala\nval bar: Var[Option[Bar]] = Var[Option[Bar]](None)\n```\n\nIf we want to determine `active` on `Foo` we have several layers of mutability between the optional `bar` `Var`, the\noptional `foo` `Var`, and then the ultimate `active` `Var` in `Foo`. For a one-off we could do something like:\n\n```scala\nval active: Boolean = bar().flatMap(_.foo().map(_.active())).getOrElse(false)\n```\n\nThis would give us `true` only if there is a `Bar`, `Bar` has a `Foo`, and `active` is true. But, if we want to listen\nfor when that changes at any level (`Bar`, `Foo`, and `active`) it should be just as easy. Fortunately with Reactify it\nis:\n\n```scala\nval active: Val[Boolean] = Val(bar().flatMap(_.foo().map(_.active())).getOrElse(false))\n```\n\nYep, it's that easy. Now if I set `bar` to `Some(new Bar)` then `foo := Some(new Foo)` on that, and finally set `active`\nto true on `Foo` my `active` `Val` will fire that it has changed. Reactify will monitor every level of the `Var`s and\nautomatically update itself and fire when the resulting value from the function above has changed.\n\n```scala\n// Monitor the value change\nactive.attach { b =\u003e\n  ... do something ...    \n}\n\n// Set Bar\nval b = new Bar\nbar := Some(b)\n\n// Set Foo\nval f = new Foo\nb.foo := Some(f)\n\n// Set active\nf.active := true\n```\n\nWith Reactify you don't have to do any magic in your code, you just write Scala code the way you always have and let\nReactify handle the magic.\n\n### Channels\n\nAs we saw above, `Var` and `Val` retain the state of the value assigned to them. A `Channel` on the other hand is like a\n`Var` (in that you can set values to it), but no state is retained. This is useful for representing firing of events or\nsome other action that is meant to be observed but not stored.\n\n## Nifty Features\n\n### Dependency Variables\n\nThe core functionality is complete and useful, but we can build upon it for numeric values that are dependent on other\nnumeric values or numeric values that may have multiple representations. For example, consider a graphical element on\nscreen. It may have a `left` position for the X value originating on the left side of the element, but if we want to\nright-align something we have to make sure we account for the width in doing so and vice-versa for determining the right\nedge. We can simplify things by leveraging a `Dep` instance to represent it:\n\n```\nval width: Var[Double] = Var(0.0)\n\nval left: Var[Double] = Var(0.0)\nval center: Dep[Double, Double] = Dep(left)(_ + (width / 2.0), _ - (width / 2.0))\nval right: Dep[Double, Double] = Dep(left)(_ + width, _ - width)\n```\n\nNotice we've even added a `center` representation. These are dependent on `left` but their value is derived from a\nformula based on `left` and `width`. Of course, if representing the value alone were all we care about then a simple\n`Val(left + width)` could be used as our `right` value, but we also want to be able to modify `center` or `right` and\nhave it properly reflect in `left`. Any changes made to `Dep` will properly update the variable it depends on `left` in\nthis case. See `DepsSpec` for more detailed examples.\n\n`Dep` also supports conversions between different types as well.\n\n### Binding\n\nAs of 1.6 you can now do two-way binding incredibly easily:\n\n```scala\nval a = Var[String](\"Hello\")\nval b = Var[String](\"World\")\nval binding = a bind b\n```\n\nBy default, this will assign the value of `a` to `b` and then changes to either will propagate to the other. If you want\nto detach the binding:\n\n```scala\nbinding.detach()\n```\n\nThis will disconnect the two to operate independently of each other again.\n\nYou can also do different typed binding:\n\n```scala\nimplicit val s2i: String =\u003e Int = (s: String) =\u003e Integer.parseInt(s)\nimplicit val i2s: Int =\u003e String = (i: Int) =\u003e i.toString\n\nval a = Var[String](\"5\")\nval b = Var[Int](10)\na bind b\n```\n\nWe need implicits to be able to convert between the two, but now changes to one will propagate to the other.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutr%2Freactify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutr%2Freactify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutr%2Freactify/lists"}