{"action":"created","issue":{"url":"https://api.github.com/repos/cyclejs/core/issues/268","repository_url":"https://api.github.com/repos/cyclejs/core","labels_url":"https://api.github.com/repos/cyclejs/core/issues/268/labels{/name}","comments_url":"https://api.github.com/repos/cyclejs/core/issues/268/comments","events_url":"https://api.github.com/repos/cyclejs/core/issues/268/events","html_url":"https://github.com/cyclejs/core/issues/268","id":140590946,"number":268,"title":"Isolate alternatives with Subject and fractality issues","user":{"login":"edge","id":6856710,"avatar_url":"https://avatars.githubusercontent.com/u/6856710?v=3","gravatar_id":"","url":"https://api.github.com/users/edge","html_url":"https://github.com/edge","followers_url":"https://api.github.com/users/edge/followers","following_url":"https://api.github.com/users/edge/following{/other_user}","gists_url":"https://api.github.com/users/edge/gists{/gist_id}","starred_url":"https://api.github.com/users/edge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edge/subscriptions","organizations_url":"https://api.github.com/users/edge/orgs","repos_url":"https://api.github.com/users/edge/repos","events_url":"https://api.github.com/users/edge/events{/privacy}","received_events_url":"https://api.github.com/users/edge/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/cyclejs/core/labels/issue%20is%20feature%20suggestion","name":"issue is feature suggestion","color":"33eeee"},{"url":"https://api.github.com/repos/cyclejs/core/labels/scope:%20isolate","name":"scope: isolate","color":"bfd4f2"},{"url":"https://api.github.com/repos/cyclejs/core/labels/weight%201%20(maybe)","name":"weight 1 (maybe)","color":"eeeeee"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":13,"created_at":"2016-03-14T06:49:10Z","updated_at":"2016-03-16T21:02:09Z","closed_at":null,"body":"This issue is basically a compilation of stuff said in the [Gitter](https://gitter.im/cyclejs/core). Shoutout to @laszlokorte and @whitecolor for discussing these things with me. There are a lot of details, so feel free to point out and correct any mistakes.\r\n\r\nBefore you read any of this, I want to emphasize that idiomatic Cycle is currently neither truly pure nor truly fractal, but there are several possible solutions.\r\n\r\n### `isolate` is impure\r\n\r\n`isolate` is necessary for ensuring two instances of a component are unique, but it also introduces a great amount of complexity to Cycle, and many drivers need to be aware of it. These circumstances create a lot of controversy over whether we should use `isolate` by default or if there is a better solution (e.g. #259).\r\n\r\nCycle's goal is to be pure and referentially transparent, but as long as we have `isolate`, it cannot be.\r\n\r\n```js\r\nisolate(Component) // impure\r\nisolate(Component, scope) // pure <==> Component is pure\r\n```\r\n\r\nSince we can never be sure that a component is pure, it is safe to pessimistically assume that all components are impure.\r\n\r\nThe minimum requirements for a truly practical and pure `isolate` are threefold:\r\n**(1)** We must **always pass an explicit scope to `isolate`**\r\n**(2)** In order for this to scale, we must **use a scope generator, from the top all the way down** (humans are bound to create duplicates).\r\n\r\nSo far, the code would look like this:\r\n\r\n(cc @theefer)\r\n\r\n```js\r\nfunction Component(sources) {\r\n    const iso1 = sources.isolate;\r\n    const [scope1, iso2] = iso1.next();\r\n    const [scope2, iso3] = iso2.next();\r\n\r\n    const { isolate: iso4 } = Child({ ...sources, isolate: iso3 });\r\n\r\n    // ...\r\n    return {\r\n        // ...\r\n        isolate: iso4\r\n    };\r\n}\r\n```\r\n\r\n**(3)** This behavior is extremely redundant, and this purity criterion also requires us to pass it down as an extra sink on all components. The best way to practically do this is to **use a pure functional language like Elm or Purescript, where we can then hide the `isolate` generator in a state monad.**\r\n\r\n### The Fractal Problem\r\n\r\n@staltz says that Cycle is a Fractal Architecture (http://staltz.com/unidirectional-user-interface-architectures.html). While this is true by interface, certain implementation details prevent it from being true in practice.\r\n\r\nIn Cycle's case, being fractal means that you can take any app's main function and pass it some drivers, or alternatively call a wrapped `run(main, drivers)` function, and expect it to behave normally.\r\n\r\nBut what if it uses `isolate`? We can't be sure that the child (possibly third party) component uses the same instance that we use. In the current source, `isolate` always counts up from zero. This means that two components using different `isolate` instances can clash. We've decidedly refused to use random number generators, since they are never truly safe. Using an incremental id is usually the safest bet, but only in a singular context, which we cannot assume.\r\n\r\nOne possible solution would be to somehow make sure that all components receive the same `isolate`, but that would only increase Cycle's overall complexity.\r\n\r\n### Subjects to the rescue?\r\n\r\nThis idea came with some initial disapproval, but I've thought very much about it and I think it is a possible way forward.\r\n\r\nInstead of using `DOM.select`, we can use an event handler like in rx-recompose: https://github.com/acdlite/recompose/tree/master/src/packages/rx-recompose\r\n\r\n(cc @vladap)\r\n\r\nIn the following code, `DOM.handler` is equivalent to `createEventHandler` in rx-recompose. `createEventHandler` simply returns a function that acts like `Subject`, but proxies calls to `onNext`. Using `$x$` is just a contrived convention, but the notation indicates that it is both an observer and an observable (i.e. subject).\r\n```diff\r\nexport default ({ DOM }) => {\r\n-\tButton(DOM.select('.Home')).forEach(() => {\r\n-\t\twindow.location.href = '/';\r\n-\t});\r\n-\tButton(DOM.select('.Github')).forEach(() => {\r\n-\t\twindow.location.href = 'https://github.com/edge/cyc';\r\n-\t});\r\n+\tconst $goToHome$ = DOM.handler();\r\n+\tconst $goToGithub$ = DOM.handler();\r\n+\t$goToHome$.forEach(() => {\r\n+\t\twindow.location.href = '/';\r\n+\t});\r\n+\t$goToGithub$.forEach(() => {\r\n+\t\twindow.location.href = 'https://github.com/edge/cyc';\r\n+\t});\r\n\treturn {\r\n\t\tDOM: $.just(\r\n\t\t\tdiv('.p2.measure', [\r\n\t\t\t\th2('About'),\r\n\t\t\t\th4([\r\n\t\t\t\t\ti('cyc'), ' is a Cycle.js boilerplate built with convenience and speed in mind.'\r\n\t\t\t\t]),\r\n\t\t\t\tbr(),\r\n-\t\t\t\tbutton('.btn.Home', 'Home'), ' ',\r\n-\t\t\t\tbutton('.btn.Github', 'Github'),\r\n+\t\t\t\tbutton('.btn', { onclick: $goToGithub$ }, 'Home'), ' ',\r\n+\t\t\t\tbutton('.btn', { onclick: $goToGithub$ }, 'Github'),\r\n\t\t\t])\r\n\t\t)\r\n\t};\r\n}\r\n```\r\n\r\nAlthough in the end we have two extra lines, this removes the need for `isolate`, drastically reducing complexity.\r\n\r\nYou might say that this is no longer pure, but `isolate` and Subjects are two sides of the same coin:\r\n![http://i.imgur.com/gEJ2pxu.jpg](http://i.imgur.com/gEJ2pxu.jpg)\r\n\r\nTo the parent, the interface and behavior are the same. This follows two Cycle doctrines:\r\n1. All read effects are from sources. All write effects are to sinks.\r\n++ Subjects are implicit sources and sinks.\r\n2. Parent sources go to Child sources, Child sinks go to Parent sinks.\r\n++ As you can see in the diagram, no read-write arrow crosses a component context (lines in the diagram).\r\n\r\nNot only is it more predictable, using subjects is about as pure as Javascript purity goes.\r\n\r\nThis also solves the fractal problem, because all Subjects are inherently unique.\r\n\r\nWhat using this idiom implies for other drivers is an important topic for discussion, which I'll start off with this dialogue:\r\nQ. How would this work for the HTTP driver?\r\nA. `{ url, response$: $response$ }`\r\nQ. But doesn't that violate the second doctrine? The subject will be passed into a driver, and a read event will bypass the parent sources.\r\nA. Possibly, but if you look at it like metadata, which is more obvious as we are only passing a function containing a reference to the *actual* subject, it is no different from theoretical driver interop. The handler is an implicit source/sink, but we've conflated the two for simplicity. Although it may not seem to follow the doctrine, it is still fractal.\r\n\r\n### But what about cycle-restart?\r\n\r\nUsing a subject for DOM events seems to exclude it from cycle-restart. In this case, we can add some code to the DOM driver to replay the subject's last event. This also solves https://github.com/Widdershin/cycle-restart/issues/41.\r\n\r\n(cc @Widdershin)\r\n\r\nIf this concept becomes idiomatic, it could be standardized and applied to other drivers. For example, an HTTP.handler() could be implemented, mitigating the aforementioned context-crossing.\r\n\r\n### The Fractal Problem (cont.)\r\n\r\nThis discussion does, however, bring up an important issue. Ideally, in an application implementing `cycle-restart`, every driver in an application should be `restartable`. This is the only way for an application to be truly restartable, as child applications with drivers would otherwise be ignored. In order for this to work, we need to explicitly pass down `rerun` to every component.\r\n\r\nBut what if a third party application uses its own instance of cycle-restart? This instance problem concerning cycle-restart, isolate, and other similar implementations *could* be partially solved by wrapping every application's `run(main, drivers)` as follows:\r\n```js\r\nexport default ({ DOM, HTTP }) => {\r\n    run(main, {\r\n        DOM: DOM || restartable(makeDOMDriver('#root')),\r\n        HTTP: HTTP || restartable(makeHTTPDriver())\r\n    });\r\n}\r\n```\r\nor possibly:\r\n```js\r\nexport default availableDrivers => {\r\n    run(main, {\r\n        DOM: restartable(makeDOMDriver('#root')),\r\n        HTTP: restartable(makeHTTPDriver()),\r\n        ...availableDrivers\r\n    });\r\n}\r\n```\r\nThis means any available drivers would be reused, and would automatically be restartable. However, this would still require us to pass down `restart` and `isolate` in every component.\r\n\r\nAnd what if we wanted hydratable drivers (not yet implemented)? Now *every* driver would also need to be wrapped in `hydratable`. This cannot scale.\r\n\r\nCycle seems to require a lot of passing-down-common-objects in order to be more correct as an architecture. While React solves this with `this.context` available to all impure components, we must solve it in a pure way to get closer to our goal of purity.\r\n\r\nMonads are a viable solution; they are great for concisely and efficiently keeping things in a context, as well as reading and writing any necessary information. This implies that we should switch to a language that can facilitate this, like Elm or Purescript.\r\n\r\n### Conclusion\r\n\r\n1. Using Subjects instead of `isolate` would reduce a lot of complexity and make components much more predictable. It should live comfortably in both Javascript and compile-to-js environments.\r\n\r\n2. More importantly, the need for the switch to a functional language like Elm or Purescript to solve certain fundamental problems may be more imminent than we thought.\r\n\r\nGists:\r\n1. https://gist.github.com/edge/5a5be8fed277bce2389e\r\n2. https://gist.github.com/edge/39c1223f8f0e27a14e94\r\n\r\nFurther Reading:\r\n1. https://gitter.im/cyclejs/core?at=56d7436c0bdb886502f6d263\r\n2. https://gitter.im/cyclejs/core?at=56e5cd900055f8f35a82ccce\r\n3. https://gitter.im/cyclejs/core?at=56e6002e89dd3cce10061e42\r\n4. https://gitter.im/cyclejs/core?at=56e62ff13194fbd11096bb33\r\n\r\nrelated: #259 "},"comment":{"url":"https://api.github.com/repos/cyclejs/core/issues/comments/197548175","html_url":"https://github.com/cyclejs/core/issues/268#issuecomment-197548175","issue_url":"https://api.github.com/repos/cyclejs/core/issues/268","id":197548175,"user":{"login":"milankinen","id":1080706,"avatar_url":"https://avatars.githubusercontent.com/u/1080706?v=3","gravatar_id":"","url":"https://api.github.com/users/milankinen","html_url":"https://github.com/milankinen","followers_url":"https://api.github.com/users/milankinen/followers","following_url":"https://api.github.com/users/milankinen/following{/other_user}","gists_url":"https://api.github.com/users/milankinen/gists{/gist_id}","starred_url":"https://api.github.com/users/milankinen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/milankinen/subscriptions","organizations_url":"https://api.github.com/users/milankinen/orgs","repos_url":"https://api.github.com/users/milankinen/repos","events_url":"https://api.github.com/users/milankinen/events{/privacy}","received_events_url":"https://api.github.com/users/milankinen/received_events","type":"User","site_admin":false},"created_at":"2016-03-16T21:02:09Z","updated_at":"2016-03-16T21:02:09Z","body":">Whould each DOM element or component provide observable we could subscribe to? What would be your dream DOM API to run Cycle against?\r\n\r\nI've already told this few times in #259 but saying it once again: DOM events should be derived from the (V)DOM instead of generating the from \"nothing\". I mean something like [this](https://github.com/tsers-js/react#example)."}}