tonsky / datascript

Immutable database and Datalog query engine for Clojure, ClojureScript and JS
Eclipse Public License 1.0
5.51k stars 308 forks source link

Connection (or ref? transactor?) protocol #150

Open metasoarous opened 8 years ago

metasoarous commented 8 years ago

Such a protocol would make it possible to build custom connection/ref objects with special lifecycle functionality and capabilities. Right now the connections is just an atom, but it would be nice (for example) to be able to implement it as a Reagent r/atom. This could be helpful for some things we're thinking about for Posh (reactive filters and the like).

typeetfunc commented 8 years ago

@metasoarous What do you think about stream(or property) interface(RxJS) for connection? See rxjs datascript integration example (WIP version) There are many good reactive library(Rx, Most, Kefir etc). Integration Datascript with any of these very simple.

metasoarous commented 8 years ago

Yeah; that's exactly the kind of thing I think this would make simpler to implement. @mpdairy has already worked up quite a bit of really great functionality in https://github.com/mpdairy/posh for connecting DataScript to Reagent. So that's not the problem really... It's just that in thinking about additional features for posh (and other libs we're working on, like https://github.com/metasoarous/datsync), we've realized having a more extensible notion of what it means to be a connection would be helpful for implementing certain features.

tonsky commented 8 years ago

You can just extend IDeref (cljs) or implement IDeref (clj) and make sure it has DB inside:

(defn ^:export conn? [conn]
  (and #?(:clj  (instance? clojure.lang.IDeref conn)
          :cljs (satisfies? cljs.core/IDeref conn))
    (db/db? @conn)))

Would that work?

metasoarous commented 8 years ago

Just realized I never responded back on this thread...

The one thing that's not dealt with seamlessly by simply extending/implementing IDeref is the transaction listener functionaliy. Since libraries like posh depend on listeners for doing their thing, it would be nice if a Conn (or whatever) protocol reflected some abstraction around this listener functionality. I'm not sure what that would be yet, and this also isn't super high priority for us right now, so no pressure on our end. For now just wanting to push the conversation forward in case others decide to take up the charge.

tonsky commented 8 years ago

You get me confused. You want to keep listeners functionality but re-implement Conn as a protocol? What's the idea behind this? How will it be different from DataScript's Conn?

metasoarous commented 8 years ago

How will it be different from DataScript's Conn?

That's what setting things up as a protocol leaves up for grabs. It could be different in whatever ways someone might want it to be different. Someone could certainly do that right now (and I did tinker with it at one point), but it requires "copying over" the transaction listener implementation. Which is maybe fine, and there might not be a good/clear way around it (that's worth the trouble anyway).

tonsky commented 8 years ago

Yeah, but protocol cannot carry implementation anyways. So its either protocol or implementation, not both

metasoarous commented 8 years ago

Umm... yes, agreed. I wasn't thinking clearly...

The benefit would be that datascript.core/transact could be based on a protocol method, making it possible to swap different implementations of the Conn without having to resort to using different transact API/function. My ill thought through point about listeners was supposed to be: Just implimenting IDeref isn't enough, because the handling of the listeners isn't orthogonal to swap! in the current implementation, and this forces API consumers to use a separate transact function (not just implementation) if they want to use a different conn implementation. This can potentially complicate things for libraries that would which to abstract over such differences.

You are (of course) correct that the implementations don't carry through if you're using protocols. However, it's also possible that by pulling apart DataScript's implementation properly, you could have some reusable implementations of the transaction report handling side of things. That's roughly the direction I was thinking in, but not thinking through (or at least explaining) clearly.

Again, this issue isn't pressing at the moment. So I'd say until there's the need for this indirection around transactions and conn implementation, we table discussion, since the particulars of what might be needed in this indirection are really dependent on what those needs are as they arise.

venturecommunism commented 8 years ago

I think what you want to do is currying of react containers (higher order components) and passing down the connection as a prop with a subscribe method (rx observable) (for reads) along with a map of actions / event handlers (for writes).

If it can work in this architecture, it advances it significantly down the path to standalone clients with all the functionality of meteor.

https://kadirahq.github.io/mantra/#sec-State-Management

https://kadirahq.github.io/mantra/#sec-Containers

https://github.com/kadirahq/react-komposer#using-with-redux

https://github.com/kadirahq/react-komposer#using-with-rxjs-observables

Right now the rx-datascript example gives me a datascript parse error on datascript-mori's vector.

I think if I could get a query as an rx observable I could emulate the Om.Next architecture in pure javascript with Mantra which is essentially similar.

venturecommunism commented 8 years ago

This is basically what I was talking about:

https://github.com/venturecommunism/stack/blob/6ab35c5f8d9bd16032dd81b7ababa96df73b1fe9/client/src/configs/context.js

and then we can transact inside commands like:

https://github.com/venturecommunism/stack/blob/6ab35c5f8d9bd16032dd81b7ababa96df73b1fe9/client/src/modules/cartscape/commands/togglefullscreen.js

pure javascript / react