{"action":"created","issue":{"url":"https://api.github.com/repos/cyclejs/cyclejs/issues/581","repository_url":"https://api.github.com/repos/cyclejs/cyclejs","labels_url":"https://api.github.com/repos/cyclejs/cyclejs/issues/581/labels{/name}","comments_url":"https://api.github.com/repos/cyclejs/cyclejs/issues/581/comments","events_url":"https://api.github.com/repos/cyclejs/cyclejs/issues/581/events","html_url":"https://github.com/cyclejs/cyclejs/issues/581","id":223648347,"number":581,"title":"Push-Pull proposal: Signals to complement Streams","user":{"login":"staltz","id":90512,"avatar_url":"https://avatars0.githubusercontent.com/u/90512?v=3","gravatar_id":"","url":"https://api.github.com/users/staltz","html_url":"https://github.com/staltz","followers_url":"https://api.github.com/users/staltz/followers","following_url":"https://api.github.com/users/staltz/following{/other_user}","gists_url":"https://api.github.com/users/staltz/gists{/gist_id}","starred_url":"https://api.github.com/users/staltz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/staltz/subscriptions","organizations_url":"https://api.github.com/users/staltz/orgs","repos_url":"https://api.github.com/users/staltz/repos","events_url":"https://api.github.com/users/staltz/events{/privacy}","received_events_url":"https://api.github.com/users/staltz/received_events","type":"User","site_admin":false},"labels":[{"id":145552057,"url":"https://api.github.com/repos/cyclejs/cyclejs/labels/issue%20is%20breaking%20suggestion","name":"issue is breaking suggestion","color":"5319e7","default":false},{"id":331504532,"url":"https://api.github.com/repos/cyclejs/cyclejs/labels/priority%202%20(could)","name":"priority 2 (could)","color":"cccccc","default":false},{"id":413955013,"url":"https://api.github.com/repos/cyclejs/cyclejs/labels/size%20L","name":"size L","color":"7799ee","default":false}],"state":"open","locked":false,"assignee":{"login":"staltz","id":90512,"avatar_url":"https://avatars0.githubusercontent.com/u/90512?v=3","gravatar_id":"","url":"https://api.github.com/users/staltz","html_url":"https://github.com/staltz","followers_url":"https://api.github.com/users/staltz/followers","following_url":"https://api.github.com/users/staltz/following{/other_user}","gists_url":"https://api.github.com/users/staltz/gists{/gist_id}","starred_url":"https://api.github.com/users/staltz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/staltz/subscriptions","organizations_url":"https://api.github.com/users/staltz/orgs","repos_url":"https://api.github.com/users/staltz/repos","events_url":"https://api.github.com/users/staltz/events{/privacy}","received_events_url":"https://api.github.com/users/staltz/received_events","type":"User","site_admin":false},"assignees":[{"login":"staltz","id":90512,"avatar_url":"https://avatars0.githubusercontent.com/u/90512?v=3","gravatar_id":"","url":"https://api.github.com/users/staltz","html_url":"https://github.com/staltz","followers_url":"https://api.github.com/users/staltz/followers","following_url":"https://api.github.com/users/staltz/following{/other_user}","gists_url":"https://api.github.com/users/staltz/gists{/gist_id}","starred_url":"https://api.github.com/users/staltz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/staltz/subscriptions","organizations_url":"https://api.github.com/users/staltz/orgs","repos_url":"https://api.github.com/users/staltz/repos","events_url":"https://api.github.com/users/staltz/events{/privacy}","received_events_url":"https://api.github.com/users/staltz/received_events","type":"User","site_admin":false}],"milestone":null,"comments":17,"created_at":"2017-04-23T16:46:35Z","updated_at":"2017-04-26T10:49:28Z","closed_at":null,"body":"### TL;DR: create a Signals library besides xstream, use it as the expected sink in some drivers\r\n\r\n- - -\r\n\r\n### Problem\r\n\r\nOver the weekend in CycleConf 17 we started discussing how to better accommodate usage of inherently pull values like `Math.random()` or `window.outerHeight` into Cycle.js apps in a way that follows the framework's way of working.\r\n\r\nCurrently you would have to do something nasty like `click$.map(() => Math.random())` which is impure but most importantly, makes testing a bit worse since it's non deterministic and we're not into doing invasive mocking of globals.\r\n\r\nI noticed a similarity between these values, is that they all look like `() => T`, e.g. `Math.random(): number`. Then I drafted this on a paper:\r\n\r\n- PURE: `f(x: A): B`\r\n- PUSH: `f(x: A): void` (used for sending events e.g. `observer.next(ev): void`)\r\n- **PULL**: `f(): B` (used for reading values)\r\n- SINK-ONLY DRIVER: `f(x$: Stream<A>): void`\r\n- SOURCE-ONLY DRIVER: `f(): Stream<B>`\r\n- MAIN (or what @TylorS calls component): `f(x$: Stream<A>): Stream<B>`\r\n\r\nMost of these bullet points are covered in Cycle.js, except PULL. That's how I noticed it was a missing piece. After discussing with others, watching a [talk by Erik Meijer](https://www.youtube.com/watch?v=sTSQlYX5DU0), revising the [Push-pull FRP paper by Conal Elliott](http://conal.net/papers/push-pull-frp/push-pull-frp.pdf), I noticed we just need to have a functional ES6 Iterable library. For a while I investigated [Dominic Tarr's pull streams](https://github.com/pull-stream/pull-stream) and came to the conclusion it's basically AsyncIterable as per Erik Meijer. For a while we also got quite worried about polling given what Conal Elliott described as the biggest problem with pull collections to represent continuous values over time. We thought if there should be a push-and-pull collection (e.g. to represent the virtual DOM over time, which is a \"continuous\" value which emits updates), and noticed that perhaps [Derivable.js](https://ds300.github.io/derivablejs/) by David Sheldrick or [Menrva](https://github.com/phadej/menrva) could fill that gap.\r\n\r\nThen, @Widdershin suggested we might not even need a push-and-pull hybrid for the DOM Driver if we have an entirely pull-driven approach with requestAnimationFrame polling. Nick suspected it would be even more better than the current reactive approach.\r\n\r\nWe started hacking and came with a proof of concept here: https://github.com/staltz/experiment-push-pull-cyclejs\r\n\r\n```js\r\nfunction main(sources: Sources): Sinks {\r\n  const vdomS = sources.windowHeight.map(height =>\r\n    div('.foo', 'Height: ' + height),\r\n  );\r\n\r\n  return {\r\n    DOM: vdomS,\r\n  };\r\n}\r\n\r\nrun(main, {\r\n  windowHeight: () => Signal.from(() => window.outerHeight),\r\n  DOM: makeDOMDriver('#main-container'),\r\n});\r\n```\r\n\r\nNotice the new convention `x$` is a Stream and `xS` is a Signal.\r\n\r\nThe example works with rAF and demonstrates that we could essentially model values-over-time as signals, with the same type of functional composability with operators as in xstream or rx.\r\n\r\n### What next\r\n\r\nHaving Streams and Signals in Cycle.js would be a revolutionary change that requires **a lot of careful planning**. We don't want to find a show-stopper and be embarrassed after launching this change. For now we have no guarantee whatsoever that this will actually be a thing in Cyclejs.\r\n\r\nPublish the signal library as a standalone `ysignal` in release candidate or `v0.x` for wild experimentation. ysignal should be \"hot\" in the same sense as xstream because we need the assumption of \"one execution per stream\" and \"one execution per signal\" so that the DevTools dataflow graph displays something that makes sense in people's mind. A hot Signal (implements Iterable) would mean there is a shared Iterator which is lazily created and destroyed with refCounting.\r\n\r\nWe need to consider should we remove MemoryStreams from xstream because so far it has been used to represent values over time, and with Signals we wouldn't need MemoryStreams anymore. Currently MemoryStreams are *pull on subscribe*, *push afterwards*, so they have a pull ability. With Signals dedicated for pull, we might not need to mix pull and push. We can have Streams only for push, and Signals only for pull.\r\n\r\nxstream and ysignal could be tightly interrelated \"sibling\" libraries if they have conversion operators from one to the other and vice-versa. Not sure if they should do that, or if they should just assume conversion with Iterables and ES Observables.\r\n\r\nOn the topic of conversion, I noticed Push-to-Pull then Pull-to-Push is not an isomorphism. I don't know what to call that in category theory but all I'm saying is that when you go from Push to Pull, the Pull producer can choose **what** and how much data to replay, and when you go from Pull to Push, the Push producer can choose **when** to emit data. This liberty of choice means information is lost during conversion.\r\n\r\nWe need to investigate further if we need a hybrid type in Menrva/DerivableJS style. Related to that, since those libraries do intelligent computation of derived values in the signal graph, we might need that ranking algorithm (maybe we don't need the Push part of being hybrid).\r\n\r\nWe could map what drivers would expect Signal sinks and what drivers would expect Stream sinks.\r\n\r\nDOM Driver isolation and event delegator event Streams based on `Element` Signals may be a challenging problem.\r\n\r\nNeed to also consider how to support stream lib Diversity.\r\n\r\nNick Johnstone could comment on his ideas how to use this with cycle/time."},"comment":{"url":"https://api.github.com/repos/cyclejs/cyclejs/issues/comments/297351837","html_url":"https://github.com/cyclejs/cyclejs/issues/581#issuecomment-297351837","issue_url":"https://api.github.com/repos/cyclejs/cyclejs/issues/581","id":297351837,"user":{"login":"whitecolor","id":736697,"avatar_url":"https://avatars1.githubusercontent.com/u/736697?v=3","gravatar_id":"","url":"https://api.github.com/users/whitecolor","html_url":"https://github.com/whitecolor","followers_url":"https://api.github.com/users/whitecolor/followers","following_url":"https://api.github.com/users/whitecolor/following{/other_user}","gists_url":"https://api.github.com/users/whitecolor/gists{/gist_id}","starred_url":"https://api.github.com/users/whitecolor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/whitecolor/subscriptions","organizations_url":"https://api.github.com/users/whitecolor/orgs","repos_url":"https://api.github.com/users/whitecolor/repos","events_url":"https://api.github.com/users/whitecolor/events{/privacy}","received_events_url":"https://api.github.com/users/whitecolor/received_events","type":"User","site_admin":false},"created_at":"2017-04-26T10:49:28Z","updated_at":"2017-04-26T10:49:28Z","body":"> I really like that HTTP interface, it seems much more explicit, and there is no need for categories to filter the request objects.\r\n\r\nThis API is kind of arguable, while it may seem suitable for `GET` requests if it is used for example for updating something we may lose track and control over this requests as they don't go all the path down/up through the sink and the source.\r\n\r\nThough it is still functional in terms of separation of side effects from pure stuff, but it is not so cyclic."}}
{"actor":{"display_login":"whitecolor"}}