-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clojure support? #25
Comments
There is a Clojure port kicking around: https://github.com/hoplon/javelin/blob/master/src/javelin/core_clj.clj It's prototype-quality and we've never used it for anything, but if you have an idea about using it and want to contribute, we're happy to accept PRs. |
I saw that and had a feeling that's what that was, but wasn't 100% sure. I'll take a look at it |
When this question comes up, @micha and @alandipert usually conclude the discussion by saying they haven't seen a really compelling case yet for using cells on the backend. I would be very interested to see a really characteristic use case, so @bsima if you have one, please share! |
@onetom, well I'm currently working on a streaming data analysis service, and I thought having the reactive cell graph would be useful, especially since I'll be working with a lot of graph data structures. A few discussions on SO about Haskell lenses seem to corroborate my assumption (1, 2). I have yet to get to the implementation of the service, still reading and designing the system, so we'll see where my research leads me. |
Seems like anyone doing pub-sub to long-polling streams would like this kind of thing on the server. Basically anyone implementing event-push to web socket or long-polling web clients needs to distribute events and record who has received what. All that state-full book-keeping is a bother, and easy to write bugs into. Mix in an async implementation (which is the only solution to having any kind of scale with long-polling) and the bugs tend to multiply. |
just dumping some notes here for later...
It looks like this kind of cell abstraction, in some form or another, was used primarily for UI programming. E.g. Garnet, which predated even Tilton's Cells. However, the case of using cells on the backend is getting stronger the more I read. |
More notes...
A big problem that Rich and Stuart bring up is transactional order. From the first link, a message from Rich:
I guess core.async was created to get around this problem. I don't know if Javelin has this problem or not, but if it does, a cell abstraction built on top of core.async would be cool. Alternatively, there is the FrTime language, which is mentioned in the second email thread.
A first scan of the papers seems to indicate that FrTime influenced Javelin, since paper 1 talks about a "lift" function and here we see a lift implemented in Javelin. I'll read FrTime more closely this weekend. There is also an old dataflow api and source, and an SO thread. |
@bsima Excellent stuff! I'll throw this in: This is the paper and FRP library that most directly influenced Javelin: http://cs.brown.edu/~sk/Publications/Papers/Published/mgbcgbk-flapjax/paper.pdf |
Yeah, the FlapJax paper cites the FrTime papers, and the FrTime author Gregory Cooper mentions it on his website and is an author on the FlapJax paper. So that's the basic historic path from Conal Elliot's invention circa 1997, all the way up to the current Javelin implementation. But there are really two (or three?) historical lines; one for FRP (and/or "dataflow" which is a rather ambiguous term), and one for cells. They aren't really compatible, because cells update synchronously, whereas "dataflow" is typically concerned with managing concurrency, and FRP is concerned with managing asynchronous dataflow. So we have 3 concepts that are being tangled together and confused. This really helps me understand why the old dataflow API was dropped, and the cell discussions killed, they all mention the problems of concurrency/asynchronicity without unpacking (decomplecting) the problem. Looking at these implementations from 6 years ago, they're rather naive, basically just simplified ports of Tilton's cells. Trying to build cells on top of agents is interesting as it attempts to make synchronous cells work with concurrent processing, but ultimately fails because there is no time constraint, so values could arrive at cells in any order -- cells are meant for sychronicity after all. So, the two relevant solutions to this concurrency problem are:
Of course CSP isn't actually reactive, it's just passing messages. If we want reactivity then we need FrTime/FlapJax. (Or it might be possible to implement FrTime-like semanatics using CSP, but that's an experiment for another time.) The brilliance of Javelin is that it takes the FrTime semantics and wraps it in the cell abstraction, which is like painting a simple interface over the academic lingo of FRP. I think micha mentioned in his 20-minute talk a few weeks ago that Javelin cells are kinda alternative to core.async, but that's not strictly true. Maybe it is in ClojureScript where you don't have true concurrency, but really cells and CSP are complementary and solving two slightly different problems. Okay, after all this long-winded thinking-out-loud, I'm concluding that Javelin would absolutely be useful in Clojure. I'm gonna finish reading the FrTime papers closely and also read the Javelin code again more closely, and then I'll work on experimenting with Javelin on the JVM next week or so. |
I've got an in-progress branch here: bsima/javelin/jvm I haven't worked much with custom Clojure data structures so this took quite a bit of research, but so far it's going well. I can construct a cell, but The CLJS code is really dense and undocumented so it takes me a while to figure out exactly what it's doing. But a lot of the code is shared between CLJ and CLJS, so once I get the CLJ working we could consolidate using .cljc My goal is to get this working by the end of the month. Thinking toward the future: I like the idea of specifying protocols for the cells to implement, because (as I said in the above analyses) the cell abstraction is useful outside of FRP, so perhaps an ICell (or similar) protocol could be used to implement cells with different backends - cells with core.async, or threaded cells, for example. |
Really looking forward to this! |
@bsima @sjmackenzie would you be interested in a code walkthrough via Hangout or similar? Javelin/cells come from a handful of ideas that code mashes together and obfuscates. I'll have some time this weekend, can probably enlist @micha also. |
Ah the cells manifesto. That was my introduction to reactive programming. BTW @bsima the name is Tilton not Triton. Also, I'd be interested in this hangout too. If you decide to go ahead with it, will you put a link in here? |
@cddr will do, and will mention anyone else who expressed interest. |
Yes indeed it would be quite cool to listen in to that talk. |
@alandipert yes please! The Thanksgiving holiday and general work distracted me from working on this, but a code walkthrough would be super helpful and just generally interesting. Saturday morning or early afternoon would be perfect, I'm on East Coast time. @cddr oops! Not sure how I made that typo.. thanks :) |
I'd love to join as well if you choose a good time. |
I'll likely be on a plane, but would love to review a recording of the walk through if possible. Sent from my iPhone
|
@xificurC @sjmackenzie @cddr @bsima I made a doodle, please select the time that works best for you: http://doodle.com/poll/z55yyt2cx6s5nuia I'll try to set up a Hangout on Air and drop the link here when it's ready so we can save the video. |
I have no idea what timezone the doodle times are using. Once decided please put the time + tz here and I'll convert. |
@sjmackenzie timezone is EDT |
For those joining us this weekend for the code walkthrough, http://cs.brown.edu/~sk/Publications/Papers/Published/mgbcgbk-flapjax/paper.pdf Section 3.4 is a concise description of what we'll be looking at. The rest of the paper also has a lot of useful context. Worth a read, and don't worry, you won't be tested :-) |
I remember it was also mentioned that cells and data-flow in general is the most useful when it backs UI. |
OK the time is set, Sat 12/5/15 3:00 PM EDT. Here is the Hangout on Air link: https://plus.google.com/events/ccnkeqnkgfhv8933s3ff1l2obd0 |
Hey I'm here. Watching. But there's nothing I can type in the google video :-) |
@cddr change of plans, we're here now: https://unhangout.media.mit.edu/h/TurboComputingFortress |
Just a heads up, I'm still working on this. Today reading about Elm's concurrent FRP ideas for possible use in training a neural network. As for the Javelin code, I think it would be prudent to start with a few minor refactorings:
I could do all these refactorings over the next few weeks, and it would help me understand the codebase besides. Let me know what you think |
I'm very heartened to see this discussion. I've been wondering if something like this might not be very useful for data analysis. When doing ETL, it's a bit of a bother in the REPL to go back and fix mistakes or assumptions and then re-execute everything (and error prone at that). I actually though of this the other day and couldn't remember if Javelin was ClojureScript only...and found this. |
Hopefully you'll forgive the tangential nature of this comment but one thing I found really useful about Common Lisp when I was doing lots of ETL stuff was the debugger. It is beyond anything in any other language I've used. You could be near the end of an hour long ETL job, and some exception is thrown due to unexpected data. The debugger would allow you to navigate the stack trace to find the offending data, redefine the function that processes it to include a fix for the bug, then re-commence the job where it left off rather than having to start again. |
Here's a good academic paper to hang our hat on: https://arxiv.org/abs/1406.2063v1 |
I'd like to use javelin for a JavaFX project, could anyone point me to the work in progress so that I can study it and possibly pick it up from where it was left? @bsima ? |
@stathissideris I haven't been working on it, I'm using Kafka on the backend for dataflow-like programming. You might also want to look at pulsar as it has support for dataflow programming http://docs.paralleluniverse.co/pulsar/#dataflow-reactive-programming |
@stathissideris You also might want to look at Onyx; You can use it for dataflow style programming, it has a very data-driven API, and you can execute your workflows in the browser, on the backend, or even distributed over a zookeeper cluster. The fact that it's built primarily for the latter most of these cases means that the setup is a little more involved than simple cell computing usage in the ballpark of javelin, but I'd bet one could wrap the functionality up in some macros that make it more buttery for this purpose. |
@bsima @metasoarous Thanks for the suggestions! I like javelin's small footprint, so I think as a first step I'll do a spike to see how easy it would be to port properly to clojure. If I fail, I will investigate pulsar and onyx (which I thought couldn't run without zookeeper!) |
Wow, hitting a bit of a wall because it of Cljs/Clj differences when it comes to (deftype foobar [field])
(def foo (foobar. 5))
(set! (.-field foo) 100) Will throw:
This is a way to override this, but it's starting to show that I may need to take this to a slightly different direction for Clojure. |
@stathissideris When I was working on it, I had to set |
@bsima thanks for the link, I'll have a look. volatileMutable makes the fields of the type mutable but also removes any thread safety that you get with Clojure's "regular" data structures. It's very likely that the weird errors you were getting were due to race conditions or other similar problems. |
6 months ago I made a lot of progress on the cljc branch. I can't remember exactly how far I got, but I think... kinda far? I used refs for the mutable fields. I ended up pretty disgusted by the way it came out. I think it's much better to have separate Clojure and ClojureScript namespaces, or perhaps even different Clojars artifacts. So, the cljc work wouldn't be helpful as-is, but if you can figure out a way to spit out .clj from the .cljc, you'd have a decent starting point that's more up-to-date than the existing |
@alandipert thanks, I had a look at that branch and started uncommenting some of the tests to see how far you got (most of them did pass!). I have a question about |
Self hosted javelin would be really awesome and also affects the cljc thing |
@stathissideris oh yeah: that's one of the things I handwaved. Reflecting on it now, I'm not sure Javelin needs to have its own Reasoning chain: On ClojureScript, ClojureScript doesn't have But in Clojure, |
So I've been doing some reading and decided to have a go at implementing cells in Clojure from scratch. Here is my effort so far (be warned, this is work in progress!): And here are some tests to show how it behaves: This implementation is closer to how I think Kenny Tilton's rube works (although I've yet to read his actual code). Tilton is the original developer of cells in CL. The main differences of my implementation to Javelin are:
Check the propagate function, it's very similar to Javelin's! I'm planning to use this as part of my java-fx project and see if it's enough for my needs. For now, I'm glad that it's a simpler implementation than Javelin. Any feedback welcome! |
@stathissideris cool! Re: 1, when you say that Javelin does static code analysis, are you talking about the Basically, it transforms expressions like If that differs from your perception about what Re: 4, interesting choice! I can see the appeal of visually demarcating references to cells in formulas. Javelin gained a macro for doing something similar recently, Anyway you're doing really cool work and I enjoy keeping up with it. Keep us posted 😄 |
@stathissideris forgot to mention, clojure has an IAtom interface now, which you could extend instead of having your own swap! |
Maybe static analysis is too grandiose a term, I meant the fancy code walking necessary for the hoisting of the args for About I'm aware of |
@stathissideris Hm, I don't see what you do about the relationship between code-walking and cell attachment. In the expression Re: The other required aspect for Anyway, it seems like it should work... but you've inspired me to experiment a little because I'm not sure. I'll post whatever I find 👍 |
@alandipert I realize that About propagation: I'm confused, it seems that About dosync: in a multithreaded environment, I think it's an advantage to have I'm glad you're feeling inspired! :-) |
@alandipert Just thought to give you a little demo of the debugging facilities: (def a (cell 100))
(def b (cell 2))
(def c (formula (partial * 2) a b))
(def d (formula (partial * 10) c))
(print-cells (all-cells)) Prints:
|
Re: synchronization, I think I understand now, thanks for clarifying. Threads: they change it all! Re "effect cells", if I understand them as you do, they seem very much like watches, but with the added semantic that they're only called when the dependent cell changes value. As opposed to cells, and more like watches, they wouldn't contain a value and couldn't be depended on directly. If you wanted to retain a value from the effect, you'd So, when these watches are called, the old/new value arguments would always be different. That way you'd have cell-like value dedupe, but all the other properties would be those of watches as they exist already. Is all of that in-line with what you are thinking? Re: cell debug - love the table! I can't think of a downside to keeping a list of the cells. An even cooler thing you could do on the JVM is pop a real data grid in a frame, with the ability to edit cell values. I'm working in a similar area for a work project, based on something I made years ago: http://tailrecursion.com/~alan/ttt/basic.html We have thousands of cells in the project and it's getting hard to know what's going on, so I'm going to revive this visualization, but include cell names & source file locations when known. I can imagine those would be useful to see in your table also. |
Oops, one difference between cells and watches I overlooked is an obvious one, that cells can depend on many other cells, but watches can depend on only one reference type. So the other feature an "effect cell" would need that watches don't do, is the ability to depend on multiple cells. I suppose one way to do this would be just to make a formula cell, and then add a watch to it, but maybe there are limitations to that I'm missing. It's definitely less convenient. |
@alandipert Sorry for the delay, didn't get an email notification and I thought that the thread had died off. About effect cells: I think you're right in how you've described them. About the debug table: my interest in cells is because I'd like to make a small ETL/Excel-like JavaFX app, and anything Excel-like needs cells by definition! So I'm thinking of using cells as the overarching metaphor for data processing and the UI as well. Because of this uniformity, I should be able to use the UI both for processing data and for debugging/developing the app itself, so the interactive table that you described is one of the early targets. Very cool graph visualisation, the fact that it's interactive caught me off guard :-) I got my cells to the point where all the operations are done in an immutable way (so you can have a graph of cells that's backed by an immutable data structure) and then there are a few thin convenience functions that use the immutable API to mutate the Immutable API shown here: I'm starting to think I should extract this from the app and release it as a separate library. |
hey, I've done some work on https://github.com/dm3/javelin/tree/macros-split-clj. It now has a Clojure implementation based loosely on Plumbata. The implementation is not concurrency-safe. All the cell's fields are stored in The tests also pass in self-host Clojurescript mode. To try that install Planck or Lumo and run: planck -c src/:test/:~/.m2/repository/net/cgrand/macrovich/0.2.0/macrovich-0.2.0.jar
> (require '[javelin.core-test :as ct]) The big problem is I can’t figure out how to make the codebase work under normal JVM Clojurescript again! The only significant change I made was splitting macros out into a separate namespace - |
@dm3 you probably want something like this for the cljs macros
|
A bit more info on this "macros from cljs" topic:
https://github.com/clojure/clojurescript/wiki/Differences-from-Clojure#namespaces
In latest versions of the CLJS compiler, you can :require-macros within a
cljs NS
and when that NS is required by another cljs NS, macros are implicitly
available for
:referring.
…--
tom
|
@flyboarder @onetom thanks, I'm well aware of the basics. The problems I was struggling with are a bit more nuanced. I think it's the compilation of macros in |
@onetom I saw you asked for a potential use case of implementing cells in pure clojure (cljc for that matter). I'm not sure if i'm being redundant or this has been mentioned elsewhere, but here it comes: I've been trying to implement a simple version of https://beta.observablehq.com/ in clojurescript. They literally mention the use of cells in their (javascript) implementation. I also had to implement something like cells (i didn't think about Javelin unfortunately) and I found it very convenient to have this cell implementation in pure Clojure so I could run an autotest process on my "cell" logic. So my argument for a pure Clojure version is having a nice test environment! :) |
There's an implementation of cells in Clojure now by the original
author. It's called matrix.
https://github.com/kennytilton/matrix
…On Fri, Sep 14, 2018 at 11:20 AM Jeroen van Dijk ***@***.***> wrote:
@onetom I saw you asked for a potential use case of implementing cells in pure clojure (cljc for that matter). I'm not sure if i'm being redundant or this has been mentioned elsewhere, but here it comes:
I've been trying to implement a simple version of https://beta.observablehq.com/ in clojurescript. They literally mention the use of cells in their (javascript) implementation. I also had to implement something like cells (i didn't think about Javelin unfortunately) and I found it very convenient to have this cell implementation in pure Clojure so I could run an autotest process on my "cell" logic. So my argument for a pure Clojure version is having a nice test environment! :)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
@cddr Thanks! I see the cljs/matrix subdir has a project that only has cljc files :) https://github.com/kennytilton/matrix/tree/master/cljs/matrix |
I have been working on a JavaFX app and decided to take a stab at porting Javelin and Hoplon to the JVM. I have pretty much everything working now. All the tests pass except for the |
Are there any plans or interest in using Javelin from Clojure? I was thinking earlier today that it would be nice to have cells on the backend too.
Yeah, core.async works for most of the same problems as Javelin, but the cell abstraction is often easier and simpler to use than channels.
The text was updated successfully, but these errors were encountered: