This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-01-18
Channels
- # admin-announcements (90)
- # alda (1)
- # aws (23)
- # beginners (60)
- # boot (217)
- # cljs-dev (20)
- # cljsjs (23)
- # cljsrn (85)
- # clojars (28)
- # clojure (101)
- # clojure-art (1)
- # clojure-berlin (10)
- # clojure-dev (16)
- # clojure-my (2)
- # clojure-russia (194)
- # clojure-sg (7)
- # clojure-ukraine (1)
- # clojured (1)
- # clojurescript (99)
- # clojurex (1)
- # community-development (6)
- # core-matrix (11)
- # cursive (26)
- # datomic (51)
- # euroclojure (30)
- # hoplon (560)
- # jobs (44)
- # ldnclj (34)
- # mount (23)
- # music (3)
- # off-topic (10)
- # om (145)
- # onyx (3)
- # perun (38)
- # portland-or (2)
- # proton (55)
- # re-frame (64)
- # reagent (26)
- # ring-swagger (3)
- # spacemacs (21)
- # sydney (3)
- # yada (1)
@alandipert: isn't that what cycle + rest/ first does?
Yeah but the explicit rotate would make super clean I think
(defc things ["one" "two" "three"])
(defc= slides (cycle things))
(defc= slide (first slides))
(with-interval 1000 (swap! slides rest))
My rebuttal is quite elegant but can't fit in the margin of the iPad
I leave it as an exercise
@alandipert: pardon my intrusion—while the rotate would make it super clean, afaik there’s no rotate function in clojure. Wouldn’t you have to code it in manually?
hey, if i'm not planning to use cell=
, is there an advantage to cell
over atom
(or vice versa)?
oh also
is there a way to respond to a Hoplon element being rendered?
i got a 3rd party library sticking something in the DOM, which gets blown away by Hoplon
would be good to have it do it's thing after Hoplon builds the page
ah, so the actual issue is that anything i pull in as a dependency with cljsjs, that does something to the page as it is pulled in
will get blown away when Hoplon does its thing
quick question on Hoplon and Server Sent Events (SSE) … I know that the architecture supports one way data updates / CQRS and SSE is a very natural way to achieve this from a READ perspective … do you have any thoughts on how the RPC mechanism could be hooked into SSE data rather than a web socket?
perhaps I’m missing something on how state changes from client X are distributed to client Y and Z without Y and Z needing to poll for the state changes
The fact that hoplon/notify uses polling (and not even long poll) to receive notifications (events) from the server is an embarrassing testimony to my ignorance. But I'm a back-end java dev with minimal web-server experience and a clojure newbie. @raymcdermott: I'd love to move notify to more appropriate tech!
Now you may have noticed that in notify we use registered functions to process notifications. This is because notifications can be changes which require updates to client state rather than being updated client state. The problem with tying notifications directly to hoplon/javelin input cells is that multiple identical notifications (add 1 to the page counter) would be deduped, resulting in the loss of significant notifications. Fortunately it is very easy in hoplon to write a function which adds 1 to an input cell using swap!
Really, I am thinking that the ideal solution here would be to have alternatives to notify which use the same API but different transports.
I agree - let’s hear what the giants have to say when they awake from their slumber 😉
I added Hoplon here - would love to see a presentation at Clojure/west 2016 https://github.com/clojurewest/clojurewest2016/wiki/Suggested-Topics
As a non-web dev, I find hoplon very hot. But the biggest problem is being limited to just castra. Easy enough to integrate other transports into hoplon, except it hits on my personal weakness as a non-web dev. Clojurescript is a boon too, as I'm weakest when it comes to JS. But I especially love hoplon's lightweight approach to reactive web pages. (Though the demos seem especially weak in this regard!)
[ANN] notify 0.2.0-- Notify now includes the timestamp of when a notification was created on the server. Functions registered to process notifications on the client now take that timestamp as a second argument. The castra-notify-random and castra-notify-chat demos have both been updated. No further changes are planned for notify at this time.
@thedavidmeister: can you describe what you're wanting to do a little more?
hoplon can coexist very well with other js libraries, but there may be things you need to do, depending on the assumptions those libraries make
@micha What do you think of @raymcdermott 's idea of using SSE in a notify variant? https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
I just know that @micha and @alandipert are brilliant developers
@laforge49: to do full event-driven backend i think we'd need to go a completely different direction
i would like to make some middleware that sits between the castra middleware and the castra client, that uses SSE or websockets
it would still be request-response, i.e. the client makes a request and gets a response
For now, I've got what I need to start working on some aatree demos. I really should let someone else run with the ball re transport.
Nothing fancy to start with, though I still very much want to work towards db change notification subscriptions for clients.
Another thing I keep thinking about is reworking aatree, immature as it is, to run in clojurescript as well. My todo list then is way too big, which is why I wanted to stabilize notify for now and start using it.
i wonder if it would make sense to implement it in clojurescript first, so you can make demos to see how it works in applications
I see aatree as having 3 layers. The first layer is done for clojure. Layer 2 is very much like datomic. Layers 2 and 3 are implemented in Java, where layer 3 contains things like access control.
no, the lowest layer. It gets hidden by the higher layers. But it is also usable independent of them, at least the clojure version is.
With layer two I do time, entities and entity relationships. And time is complets, whereas datomic only covers the smallest part of time.
So you can see changes to an entity or relationship over time, and navigate freely between those views and datomic-style time-travel.
But I really want to get layer 1 finished. It has use cases which are not supported by the other layers.
What is currently lacking in layer 1 of aatree is journaling. Every update needs to first create a journal entry which can be archived and then processed. This facilitates fast recoveries, hot backups and data mining.
Success only comes from the cross-pollination of different perspectives and different skill sets. 😄
I am also quite excited about a deep integration between a database and reactive web clients.
@micha: in my experience business front-ends can also benefit from shared real-time updates. Stock level or booking system are the pretty common examples. Dashboards and other visualisations greatly benefit from push style events.
polling works really well there, since the individual updates are essentially stateless
I was thinking of how to use http://docs.datomic.com/clojure/index.html#datomic.api/tx-report-queue
and with castra for instance, you can wrap it in middleware that keeps track of the client's state, either by keeping a websocket reference or by request sequence numbers (related to tx ids in datomic)
to optimize you can add middleware in the server that polls the castra endpoint, so the client doesn't need to send requests over the wire
with that optimization the client still polls, but the responses are not returned to the client until the state changes on the server
so client calls endpoint the first time. the middleware calls the castra endpoint and collects the result. it then compares to the last result it has seen for that client/endpoint pair. since there are no previous requests that will be nil. it then computes the diff of previous and current, and if there is any difference sends the diff across the wire to the client
now the client is polling, so it makes a second request, with a sequence number that corresponds to its previous state
the middleware on the server does the same thing it did before, calling the castra endpoint
but suppose nothing changed on the backend, i.e. the castra rpc function returns the same thing it returned last time
periodically calling the castra endpoint and computing a diff of the result with the stored previous state of the client
this kind of serverside polling should be pretty scalable for databases like datomic where reads are cheap and local to the peer
basically the client can send a request to a castra endpoint with some immutable value that describes its current state
and the server delays responding until the endpoint returns something other than the client's current state
this allows you to preserve the vastly simpler stateless architecture for your backend application
it's a huge simplification to organize your backend as a namespace of functions that the client can call
if you have to declare that relationship, which you would, then you've defeated most of the benefits of the lisp abstractions
i mean a normal clojure program has functions that compute things, referring to various vars and whatnot to perform their computations
ok I like the idea of Datomic clients sending the database value as that is trivial to compare with the current value so making queries against databases that are different will usually have the chance to obtain novelty
yes, it leaks out that we’re using Datomic but I don’t think that’s a huge issue … hiding this stuff has wasted 20 years of enterprise computing
what would be a good entry point in the Castra middleware for that …. or it just another defrpc?
and in the client you'd need some middleware between the castra client and the ajax call
i don't think anything would need to change in your serverside or clientside code really
I guess there isn’t anything like that on "the market” so let me have a think and a play … great ideas and as you say, solves for most of the problems without binding the client into the low level schema
an interesting thing we have been thinking about is the "s3c", the ServerSide Stem Cell
like if you have a function that only makes a datomic query, there must be a way to make that like a formula cell
that would still allow encapsulation etc, because it would be a general solution involving analysis of the datalog query perhaps
that would hook up to the tx queue and reset!
the state-cell
whenever the result of that query changes
some query that is automatically recalculated on the server and patches pushed out to clients
like with simple serverside polling you wake up every now and then and make a datomic query
i suppose if you did the query analysis the reactive query could register itself with a global tx queue processor
just taking the query and running it is cheap on the Peer or at least its cost is understandable
I see being able to qualify what constitutes an interesting change, and then filtering the transactions for changes of interest to all the clients. Only when there is an interesting change do you do the underlying query to see if it is really a change of interest and what the change is from the client's perspective.
@laforge49: I think we are struggling with working out how could that be achieved
Also, I tend to look at things from the perspective of a db internals dev, which is not at all useful in many cases as the overall architecture should generally be db agnostic. 😄
But yeah, we all share the same excitement. A closer integration between reactive web apps and databases via a new framework.
I just think there are advantages to using the db journal entry stream to trigger activities, like a kind of data mining.
yes, datomic tx-queue, mongo oplog, rethinkdb queries are all pushing on the same problem
And having things like distributed datomic peers helps keep you from dying of query poll overhead.
yes, I know - I used to be a DB engineer too so we probably share many of the same perspectives
the main assumption of the castra model is that interesting changes occur as a result of some action by the user, not spontaneously
but talking it through, I’m convinced that polling - at some layer - is going to get us out of jail
I mean the way that you have so many different navigation choices and looks all mashed together
you might have hundreds of pages of something, so you can't even do that the simple way
in this distributed system kind of world i think polling might actually be the best option
once you have to visualise 1000s of things pagination makes no sense anymore. You hit a google search problem, nobody goes past the first page
as long as you can pass it a query to resolve, it could be fine - like you say it has the job to calculate whether the client needs updating
i'm pretty convinced though that we need to have a menagerie of state machines in the client
exposing the knobs as functions that operate on it and cells by which it exposes its current state
ok yes, so some all reacting inside the client cage and some being allowed to interact with external entities like the server
but like you say you are mainly just focussed on the thin pipe between the client server and not the general problem of scheduling distributed computing
we didn't do any optimization of the deeper things, we just did the thin pipeline state machine stuff
also fancy things like clicking outside the modal makes the modal go away, but only if you don't have unsaved changes in the form
various hoplon elements are implemented to get default attribute values from dynamic vars if they're not explicitly provided
and you don't need to pass through the varibales it needs, or explicitly set attributes
and various elements in the body (possibly deeply nested) will be able to access the form machine to do their work
this is why we don't need to explicitly configure the text inputs with where their values go
how do you link fields together (so for example password must be the same in both fields)?
the form machine will not enable as-you-type validation until you've submitted the form once
maybe this is a philosophy thing but I think clients should be smart enough to help the user immediately rather than deferring to the server. It’s wise to distrust the client but that doesn’t mean that the code should mistreat them 😉
that way you don't have the ui telling the user that everything is fine, and the server rejecting the request
but we've found that there isn't much to be gained from clientside validation in our app really
classic example I had (and is the canonical reactive form) is the user registration form
this way if validation fails (validation is implemented as :rpc/pre expressions) you get an exception in the client
@micha: I could talk all night about this stuff but I have to go off and do a few family things
@raymcdermott: the form machine: https://gist.github.com/622a62d6a52ff419e5ac
I did a desktop calculator in QML using a state machine: https://github.com/pkobrien/calculator-qml/blob/master/src/qml/StateMachine.qml
like my form machine thing seems like it could be factored into a few smaller machines that are composed to form the whole
the formalization lets you be confident that you've covered all the possible states, is one benefit?
well, it goes back to good design in the end - most of the examples online and/or in the HSM standards are awful
Donald Knuth said something to the effect that it took him 6 tries to get the state machine for the elevator in his building defined correctly and even then he basically gave up for fear that he would never get it finished
I find documentation in general is essential. And the more the better. Be it state machines or anything else. It lets you develop a different perspective on the code and that is when the bugs start popping out.
if you have the kind of problem that a state machine is good for then a state machine is awesome - better than all the unit tests in the world
But I find in general that it is difficult to abstract reasonably simple state machines. And trying to be complete is just too overwhelming!
take a look at my calculator code example and you will see, imnsho, a good example of a fully functioning trivial/non-trivial system
What I dislike about statemachine ish software is that it generally composes badly--the whole becomes quite fragile. This is how I characterize actor systems. As soon as you use state to filter message processing, you've got intimately coupled state machines.
@laforge49: I agree and felt like developing the state machine was a very fragile process
@laforge49: the form-machine thing has been a huge simplification in our application
in terms of being able to understand complex stateful stuff, like any real world application will have
like we code all the things that interact with the server to delegate to a state machine
That's where I prefer synthesis to analysis--keep it simple so you can understand it
DSM.SignalTransition {
signal: keyManager.addSubPressed
targetState: operatorState
onTriggered: processor.calculateAll(false);
}
A good semantic model wins hands down. This is why naming is key in software development.
function calculateAll(updateExpression) {
var num = operandBuffer.number;
var operator;
if (operators.length === 2) {
operand = operands.pop();
operator = operators.pop();
switch (operator) {
case "*":
operand *= num;
break;
case "/":
operand /= num;
break;
}
num = operand;
if (equalKeyRepeatsLastOperation) {
operandBuffer.update(operand);
}
}
if (operators.length === 1) {
operand = operands.pop();
operator = operators.pop();
switch (operator) {
case "+":
operand += num;
break;
case "-":
operand -= num;
break;
case "*":
operand *= num;
break;
case "/":
operand /= num;
break;
}
} else {
operands.pop();
operand = operandBuffer.number;
}
operands.push(operand);
calculationResult = operand;
if (updateExpression) {
expressionBuilder.push("=");
expressionBuilder.push(calculationResult);
}
operators = operators; // To trigger any bindings to this list.
}
state machines do result in pretty clean code... when you only have one. But I think it is because the semantic model is so clear.
ok maybe you could tell me what you think of the form-machine i made, in relation to the formal DSM approach
it implements IFormMachine, which are methods that can be called on the form machine instance, these are the "signals"
in the specific case of the FormMachine there are three signals: reset
, validate
, and submit
and when you construct a new form machine with the form-machine
function, you provide an action
(a function to call that will do the ajax, following a certain convention for reporting error or success)
you also provide a schema
which is describing which things could have errors related to them (eg. form fields or whatever)
and a cell that represents the current value of the form and a callback to call when form has been successfully submitted
so for instance if my schema is like {:foo nil}
then there will be a cell you can get via (get-in the-machine [:error :foo])
you create the form machine with some configuration that includes functions to call when different states are entered or exited
then you can call the reset
, submit
, or validate
methods on the machine, and things will happen
the form machine also can trigger state changes when it's own internal formula cells change
and if there is an error it could attach {:username "must not be blank"}
to the exception it throws
one thing that became clear to me working on the calculator is that the import part is the behavior
what does that mean when the =
key is pressed, what behavior intent does that represent?
I will not be humble about it - the code might suck and it sure isn't clojure but I modeled a calculator better than any examples I found online and I think I saw them all
all the theory is here: https://www.w3.org/TR/scxml/
@meow: What you said is key, about focusing on the meaning and intent. It is like deriving proofs in physics rather than memorizing them. Always go back to first principles: What am I doing?
look in that doc for G.4 Calculator Example
for an example of a really poorly thought out example - or a good example of how not to do a real state machine
@laforge49: yes, or like letting go of your OO instincts and adopting a functional approach
But I learned oo in C++, where using functions instead of methods was encouraged--keep the objects simple and reusable!
no, you've got that wrong. I'm just full time playing with clojure. I'm very very slow on the uptake--ask micha! I'm just tenacious.
@meow: sent you an invitation. I just created the aatree organization to replace the laforge49/aatree project. I want to include more clojurescript among other things.