{"id":13550729,"url":"https://github.com/funkia/turbine","last_synced_at":"2025-05-16T14:06:07.958Z","repository":{"id":38428506,"uuid":"67220463","full_name":"funkia/turbine","owner":"funkia","description":"Purely functional frontend framework for building web applications","archived":false,"fork":false,"pushed_at":"2023-01-07T02:23:08.000Z","size":4716,"stargazers_count":688,"open_issues_count":64,"forks_count":27,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-04-12T14:56:32.208Z","etag":null,"topics":["framework","frp","functional-reactive-programming","javascript","pure","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/funkia.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}},"created_at":"2016-09-02T12:23:51.000Z","updated_at":"2025-03-25T07:32:40.000Z","dependencies_parsed_at":"2023-02-06T10:45:45.100Z","dependency_job_id":null,"html_url":"https://github.com/funkia/turbine","commit_stats":null,"previous_names":["funkia/funnel"],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/funkia%2Fturbine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/funkia%2Fturbine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/funkia%2Fturbine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/funkia%2Fturbine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/funkia","download_url":"https://codeload.github.com/funkia/turbine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254544146,"owners_count":22088807,"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":["framework","frp","functional-reactive-programming","javascript","pure","typescript"],"created_at":"2024-08-01T12:01:36.740Z","updated_at":"2025-05-16T14:06:07.905Z","avatar_url":"https://github.com/funkia.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","typescript"],"sub_categories":[],"readme":"\u003cimg align=\"right\" src=\"https://avatars0.githubusercontent.com/u/21360882?v=3\u0026s=200\"\u003e\n\n# Turbine\n\nA purely functional frontend framework based on functional reactive\nprogramming. Experimental.\n\n[![Gitter](https://img.shields.io/gitter/room/funkia/General.svg)](https://gitter.im/funkia/General)\n[![Build Status](https://travis-ci.org/funkia/turbine.svg?branch=master)](https://travis-ci.org/funkia/turbine)\n[![codecov](https://codecov.io/gh/funkia/turbine/branch/master/graph/badge.svg)](https://codecov.io/gh/funkia/turbine)\n\n# Table of contents\n\n- [Why Turbine?](#why-turbine)\n- [Examples](#examples)\n- [High-level overview](#high-level-overview)\n- [Principles](#principles)\n- [Installation](#installation)\n- [More examples](#more-examples)\n- [Tutorial](#tutorial)\n- [API](#api)\n- [Contributing](#contributing)\n\n## Why Turbine?\n\nThe JavaScript world is full of frameworks. So why another one?\nBecause we want something _different_. We want something that is\npurely functional without compromises. Something that takes the best\nlessons from existing JavaScript frameworks and couples them with the\npowerful techniques found in functional languages like Haskell. We\nwant a framework that is highly expressive. Because when functional\nprogramming is at its best it gives you more power, not less. Turbine\nis supposed to be approachable for typical JavaScript developers while\nstill preserving the benefits that comes from embracing purely\nfunctional programming.\n\nWe have done our best to realize our goal. But we are not done yet. We\nhope you will find Turbine interesting, try it and maybe even help us\nmaking it even better.\n\n## Examples\n\n### Email validator\n\n[See the example live and editable here.](https://codesandbox.io/s/xenodochial-williams-xok6q)\n\n```js\nconst isValidEmail = (s: string) =\u003e /.+@.+\\..+/i.test(s);\n\nconst app = component((on) =\u003e {\n  const isValid = on.email.map(isValidEmail);\n  return [\n    span(\"Please enter an email address: \"),\n    input().use({ email: \"value\" }),\n    div([\"The address is \", isValid.map((b) =\u003e (b ? \"valid\" : \"invalid\"))])\n  ];\n});\n\n// `runComponent` is the only impure function in application code\nrunComponent(\"#mount\", main);\n```\n\n### Counter\n\n[See the example live and editable here.](https://codesandbox.io/s/pedantic-frog-beisw)\n\n```js\nconst counter = component((on, start) =\u003e {\n  const count = start(accum((n, m) =\u003e n + m, 0, on.delta));\n  return E.div([\n    \"Counter \",\n    count,\n    E.button({ class: \"btn btn-default\" }, \" + \").use(o =\u003e ({\n      delta: o.click.mapTo(1)\n    })),\n    E.button({ class: \"btn btn-default\" }, \" - \").use(o =\u003e ({\n      delta: o.click.mapTo(-1)\n    }))\n  ]);\n});\n\n// `runComponent` is the only impure function in application code\nrunComponent(\"#mount\", counter);\n```\n\n[See more examples here](#more-examples).\n\n## High-level overview\n\nHere our some of our key features.\n\n- Purely functional. A Turbine app is made up of only pure functions.\n- Leverage TypeScript and runtime checking to improve the developing\n  experience.\n- Based on classic FRP. Behaviors represents values that change over\n  time and streams provide reactivity. Turbine uses the FRP\n  library [Hareactive](https://github.com/funkia/hareactive).\n- A component-based architecture. Components are immutable,\n  encapsulated and composable. Components are monads and are typically\n  used and composed with do-notation (we implement do-notation with\n  generators).\n- Constructed DOM elements reacts directly to behaviors and streams.\n  This avoids the overhead of using virtual DOM and should lead to\n  great performance.\n- Side-effects are expressed with a declarative IO monad. This allows\n  for easy testing of code with side-effects. Furthermore, the\n  IO-monad is integrated with FRP.\n- The entire data flow through applications is explicit and easy to\n  follow.\n- Our libraries are available both as CommonJS and ES2015 modules.\n  This allows for tree-shaking.\n\nHere are some of the features we want to implement and goals we're\nworking towards.\n\n- Declarative and concise testing of time-dependent FRP code.\n- Performance. We think Turbine can be made very efficient. But we are\n  not yet at a point where we focus on performance.\n- Support for server side rendering.\n- Browser devtools for easier development and debugging.\n- Hot-module replacement (if possible given our design).\n\n## Principles\n\nThis section describes some of the key principles and ideas underlying\nthe design of Turbine.\n\n### Purely functional\n\nTurbine is purely functional. We mean that in the most strict sense of\nthe term. In a Turbine app, every single expression is pure. This\ngives a huge benefit in how easy it is to understand and maintain a\nTurbine app is.\n\nOne benefit of the complete purity is that every function in Turbine\nsupports what is called \"referential transparency\". This means that an\nexpression can always be replaced with its value.\n\nAs a simple example, say you have the following code:\n\n```js\nconst view = div([\n  myComponent({ foo: \"bar\", something: 12 }),\n  myComponent({ foo: \"bar\", something: 12 })\n]);\n```\n\nOne may notice that `myComponent` is called twice with the exact same\narguments. Since all functions in a Turbine app are pure `myComponent`\nis no exception. Hence, we can make the following simple refactoring.\n\n```js\nconst component = myComponent({foo: \"bar\", something: 12}),\nconst view = div([\n  component,\n  component\n]);\n```\n\nSuch refactorings can always be safely done in Turbine.\n\n### Completely explicit data flow\n\nOne significant challenge when writing an interactive frontend\napplication is how to manage the data flow through an application.\n\nIn Turbine we have strived to create an architecture where the data\nflow is easy to follow and understand. For us, this means that when\nlooking at any piece of code it should be possible to see what other\nparts of the application it affects and what other parts it is\naffected by.\n\nOne manifestation of this principle is that in Turbine it is very\nsimple to see how the model affects the view and how the view affects\nthe model. The figure below illustrates this.\n\n![modelView figure](https://rawgit.com/funkia/turbine/master/figures/explicit-dataflow.png)\n\nThe arrows represent data flow between the model and the view. Note\nhow these \"conceptual arrows\" are clearly expressed in the code. For\ninstance, by looking at the buttons we can see exactly what output\nthey produce.\n\n### Declarative models\n\nImperative programming is about _doing_. Functional programming is\nabout _being_. This mean that ideally a functional program should be\nabout defining what things are. That property is what makes functional\nprograms declarative.\n\nBelow is a model from the [counters example](/examples/counters).\nNotice how the model consists of nothing but a series of `const`\nstatements.\n\n```js\nfunction* counterModel({ incrementClick, decrementClick, deleteClick }) {\n  const increment = incrementClick.mapTo(1);\n  const decrement = decrementClick.mapTo(-1);\n  const deleteS = deleteClick.mapTo(id);\n  const count = yield accum(add, 0, combine(increment, decrement));\n  return { count, deleteS };\n}\n```\n\nEach line is a declaration of a piece of the state. All models in\nTurbine follows this pattern. This makes state in a Turbine app very\neasy to understand. One can look at a single definition and be certain\nthat it tells everything there is to know about that specific piece of\nstate.\n\nThis is in sharp contrast to frameworks that mutate state or\nframeworks where state is stepped forward by reducer functions. With\nsuch approaches a single piece of state can potentially be affected\nand changed in several places. That can make it hard to understand how\nthe state evolves. The benefits of having a definition as a\nsingle source of truth is lost.\n\n## Installation\n\n```sh\nnpm install @funkia/turbine @funkia/hareactive\n```\n\n[Hareactive](https://github.com/funkia/hareactive) is a peer\ndependency. It is the FRP library that that Turbine is based upon.\n\nAlternatively, for quickly trying out Turbine you may want to see our\n[Turbine starter kit](https://github.com/funkia/turbine-starter).\n\n## More examples\n\nHere is a series of examples that demonstrate how to use Turbine.\nApproximately listed in order of increasing complexity.\n\n- [Email validator](/examples/email-validator) — Very simple example of an email\n  validator.\n- [Fahrenheit celsius](/examples/fahrenheit-celsius) — A converter\n  between fahrenheit and celsius.\n- [Zip codes](/examples/zip-codes) — A zip code validator. Shows one\n  way of doing HTTP-requests with the IO-monad.\n- [Continuous time](/examples/continuous-time) — Shows how to utilize\n  continuous time.\n- [Counters](/examples/counters) — A list of counters. Demonstrates\n  nested components, managing a list of components and how child\n  components can communicate with parent components.\n- [Todo](/examples/todo) — An implementation of the classic\n  TodoMVC application.\n\n## Tutorial\n\nIn this tutorial, we will build a simple application with a list of\ncounters. The application will be simple but not completely trivial.\nAlong the way, most of the key concepts in Turbine will be explained.\nWe will see how to create HTML, how to create custom components, how a\ncomponent can be nested and how it can share state with its parent.\n\nPlease open an issue if you have questions regarding the tutorial or\nideas for improvements.\n\nThe final result and the intermediate states can be seen by cloning\nthis git repository, going into the directory with the counters\nexample and running webpack to serve the application.\n\n```\ngit clone https://github.com/funkia/turbine/\ncd turbine/examples/counters\nnpm run start\n```\n\n### FRP\n\nTurbine builds on top of the FRP library Hareactive. The two key\nconcepts from FRP are _behavior_ and _stream_. They are documented in\nmore detail in the [Hareactive\nreadme](https://github.com/funkia/hareactive). But the most important\nthings to understand are behavior and stream.\n\n- `Behavior` represents values that change over time. For instance,\n  the position of the mouse or the number of times a user has clicked\n  a button.\n- `Stream` represents discrete events that happen over time. For\n  instance click events.\n\n### What is `Component`\n\nOn top of the FRP primitives Turbine adds `Component`. Component is the\nkey concept in Turbine. Once you understand `Component`—and how to use\nit—you understand Turbine. A Turbine app is just one big component.\n\nHere is a high-level overview of what a component is.\n\n- Components can **contain logic** expressed through operations on\n  behaviors and streams.\n- Components are **encapsulated** and have completely private state.\n- Components **contain output** through which they selectively decide\n  what state they share with their parent.\n- Components **write DOM elements** as children to their parent. They\n  can write zero, one or more DOM elements.\n- Components can **declare side-effects** expressed as `IO`-actions.\n- Components are **composable**—one component can be combined with\n  another component and the result is a third component.\n\nA `Component` in Turbine is pure and immutable. A `Component` can be\nthought of as a huge description of all of the above mentioned things.\nFor instance, a `Component` contains a description about what its DOM\nlook like. That part is a bit like virtual DOM. But, on top op that\nthe description also explain how the DOM changes over time. The\ndescription also tells what output the `Component` contains. More on\nthat later.\n\n### Creating HTML-elements\n\nTurbine includes functions for creating components that represent\nstandard HTML-elements. When you create your own components they will\nbe made of these.\n\nThe element functions accept two arguments, both of which are\noptional. The first is an object describing various things like\nattributes, classes, etc. The second argument is a child component.\nFor instance, to create a div with a span child we would write.\n\n```typescript\nconst myDiv = div({ class: \"foo\" }, span(\"Some text\"));\n```\n\nThe element functions are overloaded. So instead of giving `span` a\ncomponent as child we can give it a string. The element functions also\naccept an array of child elements like this.\n\n```typescript\nconst myDiv = div({ class: \"foo\" }, [h1(\"A header\"), p(\"Some text\")]);\n```\n\nUsing this we can build arbitrarily complex HTML. As an example we\nwill build a simple view for a counter in our counter-application.\n\n```ts\nimport { elements, runComponent } from \"@funkia/turbine\";\nconst { br, div, button } = elements;\n\n// Counter\nconst counterView = div([\"Counter \", 1, \" \", button(\"+\"), \" \", button(\"-\")]);\n\nrunComponent(\"body\", counterView);\n```\n\nWe define `counterView` as div-element with some text and two buttons\ninside. Since `div` returns a component `counterView` is a component.\nAnd a Turbine application is just a component so we have a complete\napplication. We run the application on the last line when we call\n`runComponent`. It is an impure function that takes a selector, a\ncomponent and runs the component with the found element as parent. You\ncan view the entire code in `version1.ts`.\n\n### Dynamic HTML\n\nThe `counterView` above is completely static. The buttons do nothing\nand we hard-coded the value `1` into the view. Our next task is to\nmake the program interactive.\n\nAnywhere where we can give the element functions a constant value of a\ncertain type we can alternatively give them a behavior with a value of\nthat type. For instance, if we have a string-valued behavior we can\nuse it like this\n\n```ts\nconst mySpan = span(stringBehavior);\n```\n\nThis will construct a component representing a span element with text\ncontent that is kept up to date with the value of the behavior.\n\nTo make the count in our counter view dynamic we turn it into a\nfunction that takes a behavior of a number and inserts it into the\nview.\n\n```ts\nconst counterView = ({ count }: CounterViewInput) =\u003e\n  div([\"Counter \", count, \" \", button(\"+\"), \" \", button(\"-\")]);\n```\n\nBecause it will be easier going forward `counterView` takes an object\nwith a `count` property.\n\n### Output from components\n\nThe above covers the _input_ to the counter view. We now need to get\n_output_ from it.\n\nRemember that we mentioned how a Turbine component is a description\nabout what the component will behave and look like. Part of that\ndescription also explains what output will come from the component.\n\nTo get a feel for what \"output\" means it may be helpful to mention a\nfew examples.\n\n- A button outputs, among other things, a stream of click events. So\n  part of its output is a stream of the type `Stream\u003cClickEvent\u003e\u003e`.\n- An input box's output includes a behavior of the text inside the\n  input. The type would be `Behavior\u003cstring\u003e`.\n- A checkbox might output a behavior representing whether it is\n  checked or not. It would have type `Behavior\u003cboolean\u003e`.\n\nOne way of looking at the output is that it is the information we\nwould like to get from the view.\n\nIn practice a component will almost always output more than a single\nstream or behavior. By convention the output is therefore almost alway\nan object.\n\nComponents are represented by a generic type `Component\u003cO, A\u003e`. The\n`A` represents the _available_ output of the component and the `O`\nrepresents the _selected_ out of the component. The difference\nbetween selected and available output is highlighted in the example\nbelow.\n\nConstructing an input element looks like this\n\n```ts\nconst usernameInput = input({ placeholder: \"Username\" });\n```\n\nThe type of the component constructed above is as follows ( the `...`\nrefer to the fact that we have omitted a lot of the output to keep\nthings simple).\n\n```ts\nComponent\u003c{}, { value: Behavior\u003cstring\u003e, click: Stream\u003cClickEvent\u003e, ... }\u003e\n```\n\nAmong its available output an `input` element produces a string valued\nbehavior named `value` that contains the current content of the\n`input` element.\n\nLike this input component a newly constructed component always have\n`{}` as its selected output. This means that initially no output is\nselected. We can move output from the available output into the\nselected output by using the `output` method on components.\n\n```ts\nconst usernameInput = input({\n  attrs: { placeholder: \"Username\" }\n}).output({ username: \"value\" });\n```\n\nHere `usernameInput` has the type\n\n```ts\nComponent\u003c{ username: Behavior\u003cstring\u003e }, ...\u003e\n```\n\nIn the above code the invocation to `output` means: from the object of\navailable output take the `value` property and add it to the object of\nselected output with the property name `username`.\n\nThe difference between available output and selected output matters\nwhen components are combined. In most cases, when components are\ncomposed or combined all their available output is discarded and only\nthe selected output becomes part of the combined component.\n\nFor instance, in the code below the `div` is given two children.\n\n```ts\ndiv([\n  button(\"Click me\").output({ firstButtonClick: \"click\" }),\n  button(\"Don't click me\")\n]);\n```\n\nThe `div` element composes the two buttons. When doing so all output\nfrom the buttons except for the `click` stream from the first button\nis discarded.\n\nUsing the `output` method is a bit like adding event handlers in other\nUI frameworks. There are many events that one can add handlers to but\non any given element only a few events are actually of interest and\nfor these one will add event handlers. Similarly, in Turbine\ncomponents have a lot of available output but only the piece of it\nthat gets selected will be output in the end.\n\nBack to the counters app. We want our counter view to produce two\nstreams as output. One stream should be from whenever the first button\nis clicked and the other stream should contain clicks from the second\nbutton. That is, the view's output should have the type\n\n```ts\n{\n  incrementClick: Stream\u003cClickEvent\u003e,\n  decrementClick: Stream\u003cClickEvent\u003e\n}\n```\n\nWe can achieve that by using the `output` method in each button.\n\n```ts\nconst counterView = ({ count }) =\u003e\n  div([\n    \"Counter \",\n    count,\n    \" \",\n    button(\"+\").output({ incrementClick: \"click\" }),\n    \" \",\n    button(\"-\").output({ decrementClick: \"click\" })\n  ]);\n```\n\nThe call to `output` on each `button` tells them what output we are\ninterested in. The first buttons selected output is then object with a\nstream named `incrementClick` and the later and object with one named\n`decrementClick`.\n\nThe `div` function then combines the selected output from the\ncomponents in the array passed to it and output that as its own\nselected output. The result is that `counterView` returns a component\nthat produces two streams as its output.\n\n### An analogy with promises\n\nAs mentioned above using the `output` method is a bit like adding\nevent listeners in other frameworks. However, there are fundamental\ndifferences between the two things. If you are familiar with how\nasynchronous functions that takes callbacks differ from asynchronous\nfunction that returns promises then the following analogy may help\nunderstand this difference.\n\nAn asynchronous function for reading a file may look like this\n\n```js\nreadFileCallback(\"foo.txt\", (file) =\u003e ...)\n```\n\nA similar function based on promises looks like\nthis.\n\n```js\nreadFilePromise(\"foo.txt\").then((file) =\u003e ...)\n```\n\nNotice that the `readFileCallback` function does not return the file\nthat it reads. The file is instead passed to a callback that it gets\nas an argument. The `readFilePromise` function on the other hand\nreturns the file wrapped in a promise of the type `Promise\u003cFile\u003e`.\n\nMost UI frameworks are similar to the `readFileCallback` function. In\norder to know when a button is pressed you do something like this.\n\n```tsx\n\u003cbutton onClick={(clickEvent) =\u003e ...}\u003eClick me\u003c/button\u003e\n```\n\nThe click events on the button are not returned from the `button`\nfunction. Instead they are passed to a callback (or event handler)\nthat the `button` function gets as an argumen.\n\nThe same thing in Turbine looks like this.\n\n```ts\nbutton(\"Click me\").output({ click: \"click\" });\n```\n\nThis is similar to the `readFilePromise` function. The `button`\nfunction does not take any callbacks but returns a stream of clicks\nwrapped in a component of the type `Component\u003c{ click: Stream\u003cClickEvent\u003e }, ...\u003e`.\n\nThis example should give some intuition about how Turbine differs from\nmost other frameworks. Other frameworks handle events similar to doing\nasynchronous computations with callbacks but Turbine handle events\nsimilarly to doing asynchronous computations with promises. In\nparticular when creating components the output is _returned_ as part\nof the component.\n\n### Adding a model\n\nWe now need to add a model with some logic to our counter view. The\nmodel needs to handle the increment and decrement stream and turn them\ninto a behavior that represents the current count.\n\nTurbine offers the function `modelView` for creating components with\nlogic. `modelView` takes two arguments. The first describes the logic\nand the second the view. This keeps the logic neatly separated from\nthe view.\n\nThe second argument to `modelView`, the view, is a function that\nreturns a component. We already have such a function: `counterView`.\n\nThe first argument is a function that returns a `Now`-computation. You\ndon't have to fully understand `Now`. One of the things it does is to\nmake it possible to create stateful behaviors. The model function will\nas input receive the output from the component that the view function\nreturns. The result of the `Now`-computation will be passed on to the\nview function and will be the output of the component that `modelView`\nreturns. Here is how we use to create our counter component.\n\n```ts\nfunction* counterModel({ incrementClick, decrementClick }: CounterModelInput) {\n  const increment = incrementClick.mapTo(1);\n  const decrement = decrementClick.mapTo(-1);\n  const changes = combine(increment, decrement);\n  const count = yield accum((n, m) =\u003e n + m, 0, changes);\n  return { count };\n}\n\nconst counter = modelView(counterModel, counterView)();\n```\n\nNote that there is a cyclic dependency between the model and the view.\nThe figure below illustrates this.\n\n![modelView figure](https://rawgit.com/funkia/turbine/master/figures/model-view.svg)\n\nWe now have a fully functional counter. You have now seen how to\ncreate a simple component with encapsulated state and logic. The\ncurrent code can be seen in `version2.ts`.\n\n### Creating a list of counters\n\nOur next step is to create a list of counters. To do that we will\ncreate a new component called `counterList`. The component will\ncontain a list of `counter` components as well as a button for adding\ncounters to the list.\n\nLet's begin by defining a view function that creates a header and a\nbutton.\n\n```js\nfunction* counterListView() {\n  yield h1(\"Counters\");\n  const { click: addCounter } = yield button(\n    { class: \"btn btn-primary\" },\n    \"Add counter\"\n  );\n  return { addCounter };\n}\n```\n\nWe hook the view up to a model using `modelView`. Again, the model\nfunction receives the return value from the view function.\n\n```js\nconst counterList = modelView(counterListModel, counterListView);\n\nconst counterListModel = fgo(function*({ addCounter, listOut }) {\n  const nextId = yield scan(add, 2, addCounter.mapTo(1));\n  const appendCounterFn = map(\n    (id) =\u003e (ids: number[]) =\u003e ids.concat([id]),\n    nextId\n  );\n  const counterIds = yield accum(apply, [0], appendCounterFn);\n  return { counterIds };\n});\n\nconst counterListView = ({ sum, counterIds }) =\u003e [\n  h1(\"Counters\"),\n  button({ class: \"btn btn-primary\" }, \"Add counter\").output({\n    addCounter: \"click\"\n  }),\n  ul(list(counter, counterIds).output((o) =\u003e ({ listOut: o })))\n];\n\nconst counterList = modelView(counterListModel, counterListView);\n```\n\nTo create a dynamic list of counters we have to use the `list` function.\n\n## Documentation\n\n### Understanding generator functions\n\nTurbine's use of generator functions may seem a bit puzzling at first.\nFor instance, it may seem like generator functions serve two different\npurposes. One when they're used in the model and another when they're\nused in the view\n\nBut, what they do under the hood is exactly the same in both cases.\nThe key to understand is that generator functions is just sugar for\ncalling [`chain`](#componentchain) several times in succession.\n\nWhen we use `chain` on components we can combine elements and pipe\noutput from one component into the next. The code below combines two\n`input` elements with a `span` element that shows the concatenation of\nthe text in the two input fields.\n\n```typescript\ninput({ attrs: { placeholder: \"foo\" } }).chain(({ value: aValue }) =\u003e\n  input().chain(({ value: bValue }) =\u003e {\n    const concatenated = lift((a, b) =\u003e a + b, aValue, bValue);\n    return span([\"Concatenated text: \", concatenated]).mapTo({ concatenated });\n  })\n);\n```\n\nHowever, the above code is very awkward as each invocation of `chain`\nadds an extra layer of nesting. To solve this problem we use\ngenerators.\n\n```typescript\ngo(function*() {\n  const { value: aValue } = yield input();\n  const { value: bValue } = yield input();\n  const concatenated = lift((a, b) =\u003e a + b, aValue, bValue);\n  yield span([\"Concatenated text: \", concatenated]);\n  return { concatenated };\n});\n```\n\nThe above code does exactly the same as the previous example. But it\nis a lot easier to read!\n\nThe `go` function works like this. We yield a value with a `chain`\nmethod. `go` then calls `chain` on the yielded value. `go` calls\n`chain` with a function that continues the generator function with the\nvalue that `chain` passes it. The end result is a value of the same\ntype that we yield inside the generator function. When we `yield` a\n`Component\u003cA\u003e` we will get an `A` back inside the generator function.\n\nFinally we `return` a value and that value will be the output of the\ncomponent that `go` returns.\n\nHere is another example. The following code uses `chain` explicitly.\n\n```ts\nconst view = button(\"Accept\").chain(({ click: acceptClick }) =\u003e\n  button(\"Reject\").map(({ click: rejectClick }) =\u003e ({\n    acceptClick,\n    rejectClick\n  }))\n);\n```\n\nThe above code is equivalent to the following.\n\n```ts\nconst view = go(function*() {\n  const { click: acceptClick } = yield button(\"Accept\");\n  const { click: rejectClick } = yield button(\"Reject\");\n  return { acceptClick, rejectClick };\n});\n```\n\nAgain, the code that uses generator functions is a lot easier to read.\nThis is why they're useful in Turbine.\n\n`Component` is not the only type in Turbine that has a `chain` method.\n`Now` and `Behavior` does as well. And since `go` is only sugar for\ncalling `chain` it works with these types as well.\n\n## API\n\nThe API documentation is incomplete. See also the\n[examples](#examples), the [tutorial](#tutorial), the [Hareactive\ndocumentation](https://github.com/funkia/hareactive) and this tutorial\nabout [IO](https://github.com/funkia/io).\n\n### Component\n\n#### `Component#map`\n\nMapping over a component is a way of applying a function to the output\nof a component. If a component has output of type `A` then we can map\na function from `A` to `B` over the component and get a new component\nwhose output is of type `B`.\n\nIn the example below `input` creates a component with an object as\noutput. The object contains a behavior named `value`. The\nfunction given to `map` receives the output from the component.\n\nWe then call `map` on the behavior `value` and take the length of\nthe string. The result is that `usernameInput` has the type\n`Component\u003cBehavior\u003cnumber\u003e\u003e` because it's mapped output is a\nnumber-valued behavior whose value is the current length of the text\nin the input element.\n\n```ts\nconst usernameInput = input({ class: \"form-control\" }).map((output) =\u003e\n  output.value.map((s) =\u003e s.length)\n);\n```\n\n#### `Component#chain`\n\n`map` makes it possible to transform and change the output from a\ncomponent. However, it does not make it possible to take output from\none component and pipe it into another component. That is where\n`chain` enters the picture. The type of the `chain` method is as\nfollows.\n\n```typescript\nchain((output: Output) =\u003e Component\u003cNewOutput\u003e): Component\u003cNewOutput\u003e;\n```\n\nThe `chain` method on a components with output `Output` takes a\nfunction that takes `Output` as argument and returns a new component.\nHere is an example. An invocation `component.chain(fn)` returns a new\ncomponent that works like this:\n\n- The output from `component` is passed to `fn`.\n- `fn` returns a new component, let's call it `component2`\n- The DOM-elements from `component` and `component2` are both added to\n  the parent.\n- The output is the output from `component2`.\n\nHere is an example.\n\n```typescript\ninput().chain((inputOutput) =\u003e span(inputOutput.value));\n```\n\nThe above example boils down to this:\n\n```typescript\nCreate input component   Create span component with text content\n  ↓                             ↓\ninput().chain((inputOutput) =\u003e span(inputOutput.value));\n                   ↑                              ↑\n      Output from input-element       Behavior of text in input-element\n```\n\nThe result is an input element followed by a span element. When\nsomething is written in the input the text in the span element is\nupdated accordingly.\n\n### `loop`\n\nSometimes situations arise where there is a cyclic dependency between\ntwo components.\n\nFor instance, you may have a function that creates a component that\nshows the value of an input string-value behavior and outputs a\nstring-valued behavior.\n\n```typescript\nconst myComponent = (b: Behavior\u003cstring\u003e) =\u003e span(b).chain((_) =\u003e input());\n```\n\nNow we'd have a cyclic dependency if we wanted to construct two of\nthese views so that the first showed the output from the second and\nthe second showed output from the first. With `loop` we can do it like\nthis:\n\n```typescript\nloop(({ output1, output2 }) =\u003e\n  go(function*() {\n    const output1_ = yield myComponent(output2);\n    const output2_ = yield myComponent(output1);\n    return { output1: output1_, output2: output2_ };\n  })\n);\n```\n\nThe `loop` functional seems pretty magical. It has the following\nsignature (slightly simplified):\n\n```ts\nloop\u003cA extends ReactiveObject\u003e(f: (a: A) =\u003e Component\u003cA\u003e): Component\u003cA\u003e\n```\n\nI.e. `loop` takes a function that returns a component whose output has\nthe same type as the argument to the function. `loop` then passes the\noutput in as argument to the function. That is, `f` will as argument\nreceive the output from the component it returns. The only restriction\nis that the output from the component must be an object with streams\nand/or behaviors as values.\n\nVisually it looks like this.\n\n![loop figure](https://rawgit.com/funkia/turbine/master/figures/component-loop.svg)\n\n### `modelView`\n\nThe `modelView` functions makes it possible to create components where\nthe view is decoupled from the model and its logic.\n\n`modelView` takes two arguments:\n\n- The model which is a function that returns a `Now` computation. The\n  `Now` computation is run when the component is being created.\n- The view which is a function that returns a `Component`.\n\n`modelView` establishes a circular dependency between the model and\nthe view. The model returns a `Now` computation and the result of this\ncomputation is passed into the view function. The view function then\nreturns a component. The output of the component is passed to the\nmodel function.\n\nVisually the circular dependency looks like this.\n\n![modelView figure](https://rawgit.com/funkia/turbine/master/figures/model-view.svg)\n\n`modelView` returns a _function_ that returns a component. The\narguments given to this function will be passed along to both the\nmodel and the view functions. This makes it easy to create components\nthat take input.\n\n```js\nconst myComponent = modelView(\n  (outputFromView, arg1, arg2) =\u003e ...,\n  (outputFromModel, arg1, arg2) =\u003e ...\n);\n\nmyComponent(\"foo\", \"bar\");\n```\n\n### `list`\n\nThe `list` function is used to create _dynamic_ lists in the UI.\n\n\u003e **Note:** If you are familiar with frameworks like Angular or Vue then you can\n\u003e think of `list` as being similar to `ngRepeat` in Angular 1, `ngFor` in\n\u003e Angular 2, and `v-for` in Vue.\n\nThe list function has the following type.\n\n```ts\nfunction list\u003cA, O\u003e(\n  componentCreator: (a: A) =\u003e Component\u003cO, any\u003e,\n  listB: Behavior\u003cA[]\u003e,\n  getKey: (a: A, index: number) =\u003e number | string = id\n): Component\u003c{}, Behavior\u003cO[]\u003e\u003e;\n```\n\nThe first parameter, `componentCreator`, is a function that takes a value of\ntype `A` and returns a component. This function will be invoked to create the\nelements of the dynamic list. The second argument, `listB`, is a behavior of an\narray where the elements in the array are of some type `A`.\n\nThe `list` function will return a component that at any given point is time is\nequivalent to applying `componentCreator` to the current array in `listB` and\nthen showing the resulting components one after another.\n\nWhenever `listB` changes the component returned by `list` will react to those\nchanges and keep the displayed list up-to-date. To do this, the last argument,\nthe `getKey` function, is used to figure out how elements are moved, removed, or\nadded. Therefore `getKey` should return a value that is _unique_ for each element.\n\nThe following example illustrates the above. Let us say we have a list of users\nwhere each user is an object with an `id` and a `username`:\n\n```ts\ntype User = {\n  id: number;\n  username: string;\n};\n```\n\nThe current list of users is represented by a behavior `users: Behavior\u003cUser[]\u003e`. We want to display the users in a list with their username\nbeing editable. This can be achieved with the list function.\n\n```ts\nlist((user) =\u003e input({ value: user.username }), users, (user) =\u003e user.id);\n```\n\nIf the `users` behavior starts out with the value\n\n```js\n[{ username: \"foo\", id: 1 }, { username: \"bar\", id: 2 }];\n```\n\nThen the component created by calling `list` will produce HTML like this\n\n```html\n\u003cinput value=\"foo\" /\u003e \u003cinput value=\"bar\" /\u003e\n```\n\nNow, if the value of `users` changes into\n\n```js\n[\n  { username: \"baz\", id: 3}\n  { username: \"bar\", id: 2 }\n  { username: \"foo\", id: 1 },\n]\n```\n\nThen `list` will _reorder_ the two existing `input` elements and insert a new\n`input` element in the beginning. Thanks to the `getKey` function `list` can\nefficiently do this by applying `getKey` to the old and the current value of the\nlist and figure out how the elements have moved around.\n\n### SVG\n\nYou can use embed SVG in Turbine in much the same way you'd embed it in HTML:\n\n```js\nsvg({ height: \"100\", width: \"100\" }, [\n  circle({\n    cx: \"50\",\n    cy: \"50\",\n    r: \"40\",\n    fill: \"red\"\n  }),\n  svgText({ x: 100, y: 30 }, \"Hello SVG!\")\n]);\n```\n\nThe only element with a different name is `svgText` because `text` in Turbine is an HTML Text Node.\n\n## Contributing\n\nTurbine is developed by Funkia. We write functional libraries. You can\nbe a part of it too. Share your feedback and ideas. We also love PRs.\n\nRun tests once with the below command. It will additionally generate\nan HTML coverage report in `./coverage`.\n\n```sh\nnpm test\n```\n\nContinuously run the tests with\n\n```sh\nnpm run test-watch\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffunkia%2Fturbine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffunkia%2Fturbine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffunkia%2Fturbine/lists"}