Using a state machine to design/program UI ?

2,099 views
Skip to first unread message

Khalid Jebbari

unread,
May 13, 2015, 5:54:42 AM5/13/15
to clojur...@googlegroups.com
Hello everyone,

As a Javascript web developer, I'm thinking more and more about a good way to design interface so that I don't create a mess. Because I think the current "state of the art" of web UI development and frameworks is still a big mess.

React.js and the CLJS wrapper around them help a bit but not that much I think.

My goals are the following :

- construct the complete UI logic outside of anything web related, so that it's testable without a browser and usable in other contexts (CLI, back-end, scripts, whatever)
- being able to *visualize* the logic without having to read all the code, through diagrams and other means.

I've heard in several places that state machines are a good way to handle such cases, since they're inherently event-driven and the web is too.

So I start researching. The wikipedia page (https://en.wikipedia.org/wiki/Finite-state_machine) and the various linked pages are very instructive and indeed explain that a FSM can be used to model UI interaction. I found this 3-parts article series from IBM (http://www.ibm.com/developerworks/library/wa-finitemach1/index.html) that implements a tooltip using a state machine.

Then I found 2 clojure libs that helps design a state machine : reduce-fsm (https://github.com/cdorrat/reduce-fsm) and automat (https://github.com/ztellman/automat). Both have the *very* nice feature of being able to generate a state diagram from the code, but only automat provides support for CLJS.

I think a state machine fits very well with React.js and as so the various CLJS wrappers, since :
- in an event-driven state machine, there's a loop listening to events. This event-loop is naturally provided by web browsers
- defining strictly and formally the available states helps reduce bugs and maintain the application
- the output is always defined by the combination of the input and the current state, which is *exactly* what the render function of React component are about : displaying DOM based only on the state of the component.

Since this state is purely data, and CLJ/CLJS are kings when it comes to data, the benefits would be to be able to test the logic a component outside of the DOM and have components that simply emit events (with the associated payload) to the state machines.

The various libs in the CLJ/CLJS ecosystem can help greatly :
- the aforementioned automat lib to design and visualize a state machine
- Prismatic's Schema (https://github.com/Prismatic/schema), Herbert (https://github.com/miner/herbert) and the likes to formally specify data types/shapes that come in and out of the state machine
- test.check to generate lots of input to the state machine and check the output. This can't replace UI testing, but can complement it a lot. Note that Herbert is de facto compatible with test.check
- The various React.js wrappers to take this state and simply project it to the DOM.

This post is basically a reflection on the subject that I wanted to share and not lose in my mind since I'm new to state machines, and a question to the community : did you already use a state machine to program a web UI ? Successfully ? With which tools ? What do you think of the various libs (aka "the stack" lol) proposed above ?

I know that a simple state machine trivially implemented with no libs at all and may seem often overkill. This post from Spotify (https://www.shopify.com/technology/3383012-why-developers-should-be-force-fed-state-machines) and this response (http://www.skorks.com/2011/09/why-developers-never-use-state-machines/) are really interested real world examples of usage (or not) of state machines.

Phew ! It was long, I hope I wasn't boring and I'm looking forward to your answers. Let's discuss !

Khalid Jebbari

unread,
May 13, 2015, 6:59:30 AM5/13/15
to clojur...@googlegroups.com

Colin Yates

unread,
May 13, 2015, 7:14:29 AM5/13/15
to clojur...@googlegroups.com
On a related note, there is a lot of sympathy between this line of
thought and event-sourcing/CQRS on the server side. It _completely_
transformed my way of thinking about these problems and how I
implement things but the benefits are huge, including the fact that
time changing is really just another event (actually a command, 'time
changed' would be the event but hey :)).

Another sort-of related point - unless it causes a lot of friction I
tend to reach for clix when I implement logic as the combination of
CQRS, events-everywhere, re-frame and web sockets (sente for the win)
means I end up putting logic in different places than I might
initially expect.



On 13 May 2015 at 11:59, Khalid Jebbari <khalid....@gmail.com> wrote:
> I forgot to add another useful resources, it's "Constructing the User Interface with statecharts" by Ian Horrocks. You can find it in Scribd https://www.scribd.com/doc/235638010/Ian-Horrocks-Constructing-the-User-Interface-Wit-BookZZ-org and Amazon http://www.amazon.com/Constructing-User-Interface-Statecharts-Horrocks/dp/0201342782/ref=sr_1_1?ie=UTF8&qid=1431514704&sr=8-1&keywords=constructing+user+interface+statechart
>
> --
> Note that posts from new members are moderated - please be patient with your first post.
> ---
> You received this message because you are subscribed to the Google Groups "ClojureScript" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
> To post to this group, send email to clojur...@googlegroups.com.
> Visit this group at http://groups.google.com/group/clojurescript.

Khalid Jebbari

unread,
May 13, 2015, 8:06:37 AM5/13/15
to clojur...@googlegroups.com
I understand the relationship between state machines and event-log/CQRS. A program could just translate the UI events into events stored in an event log, and let them be consumed continually by a state machine. You would basically reduce the events log using the state machine as a reduction function.

When you say logic end up in lot of different places, do you mean logic that should be related ? I'd like to understand more about that point.

Colin Yates

unread,
May 13, 2015, 8:30:25 AM5/13/15
to clojur...@googlegroups.com
Yeah, I didn't explain that second point well. Because of the lack of
(my experience with) web based tooling I typically have most of the
logic on the back end leaving the UI to be pure rendering only. With
the symmetry of CLJ and CLJS that technical barrier has disappeared.

Case in point, I am just now coding logic which summarises the
criteria used to select the data you are seeing. Imagine a table with
a set of tabs, each tab allowing you to restrict some criteria and the
table shows you matching results. The logic to produce something like
"Viewing 132 out of 3423 results for location 1 and location 2, across
all woogies and non-active wibblies, ..." is less trivial then it
might sound. Previously I would have returned this as part of the
results returned from the server {:results [..] :total-count 3423
:context "for location 1 ..."} where as now I am less nervous about
that logic living on the web tier. Of course, there are other concerns
for this case in point around data staleness (you don't want the
context being updated before the results are updated etc.).

tldr - the symmetry of CLJ and CLJS make it easier (for me at least)
to put non-trivial logic on the front end


On 13 May 2015 at 13:06, Khalid Jebbari <khalid....@gmail.com> wrote:
> I understand the relationship between state machines and event-log/CQRS. A program could just translate the UI events into events stored in an event log, and let them be consumed continually by a state machine. You would basically reduce the events log using the state machine as a reduction function.
>
> When you say logic end up in lot of different places, do you mean logic that should be related ? I'd like to understand more about that point.
>

Khalid Jebbari

unread,
May 13, 2015, 9:36:22 AM5/13/15
to clojur...@googlegroups.com
Ok, you talk about logic being on the server and the client. I thought you were saying that related logic was spread across different points from an architecture point of view, like "some in the event-log consumers", "some in the components", "some in the request handlers" etc. (these are just examples).

Khalid aka DjebbZ
@Dj3bbZ

You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/7STtgK5QiIc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Kevin Lynagh

unread,
May 13, 2015, 6:00:09 PM5/13/15
to clojur...@googlegroups.com
In addition to the Horrocks book, take a look at "Practical UML Statecharts in C / C++".
This book also describes Harel Statecharts, but goes into much more depth about how one can implement them on embedded systems.

I've been experimenting with using Harel Statecharts as an architecture for ClojureScript applications, and it has been working out very well.

This video from last year describes that work:

https://www.youtube.com/watch?v=57wJpfMcfnU

(I haven't open sourced any of these tools, though since I recorded that video Figwheel came out and David has put a ton of work into the cljs compiler, so you should be able to get up and running with those tools.)

Khalid Jebbari

unread,
May 13, 2015, 6:18:05 PM5/13/15
to clojur...@googlegroups.com
I have a question for you Kevin : when you modeled your logic with a state machine, were you able to unit test it outside of the browser ? (Sorry if the answer is in the video, I haven't watched it yet)

Khalid Jebbari

unread,
May 13, 2015, 6:50:43 PM5/13/15
to clojur...@googlegroups.com
I just watched the video. Wow. Having a live statechart is even better than what I imagined. It reminds me somehow of the NoFlo JavaScript project. This would be an awesome lib to have... (Am I suggesting you should open source this statechart stuff ? :D). Seriously thank you for the pointers. I'll definitely study this methodology.

> Le 14 mai 2015 à 00:00, Kevin Lynagh <ke...@keminglabs.com> a écrit :
>

Erik Price

unread,
May 13, 2015, 11:39:02 PM5/13/15
to clojur...@googlegroups.com
Finite state machines are a useful modeling tool, but when implemented in code they can involve a lot of boilerplate and complexity. These days I prefer Rx-like paradigms for sophisticated handling of asynchronous events.

e

--
Note that posts from new members are moderated - please be patient with your first post.
---
You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.

Ruslan Prokopchuk

unread,
May 14, 2015, 9:45:36 AM5/14/15
to clojur...@googlegroups.com
And that is why they should be compiled, not implemented directly in main language of the project. I've made experimental yEd diagrams → CLJ(S) FSM compiler and executor (https://github.com/ul/vfsm). Simple example of its usage could be found here https://github.com/ul/ampere/tree/master/examples/simple — open resources/example.graphml with yEd (http://www.yworks.com/en/products/yfiles/yed/) to see how control logic of handler is defined. What is the real fun, that it is easy to spot an error in the logic looking on that graph. If you rewrite this state machine in textual code, you will get something harder to inspect.

четверг, 14 мая 2015 г., 6:39:02 UTC+3 пользователь Erik Price написал:

Khalid Jebbari

unread,
May 14, 2015, 11:06:33 AM5/14/15
to clojur...@googlegroups.com
Ruslan, I haven't look at the code or tried your example. But being able to draw the program's logic and have it executed is even more than awesome.
I'm not sure whether graph-to-code like what you did, or code-to-graph like what automat or Kevin's (from this thread) work do is better, but simply knowing that both are possible in the CLJ(S) world... I'm happy I started this thread.

Khalid aka DjebbZ
@Dj3bbZ

You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/7STtgK5QiIc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Jamie Orchard-Hays

unread,
May 14, 2015, 11:23:00 AM5/14/15
to clojur...@googlegroups.com
Ruslan, ampere looks interesting. You've taken the handler/middleware code from re-frame and combined with javelin to make ampere. Is that a fair assessment? I'll have to have a look at javelin.

Jamie

Kevin Lynagh

unread,
May 14, 2015, 11:29:54 AM5/14/15
to clojur...@googlegroups.com
On Wednesday, May 13, 2015 at 3:18:05 PM UTC-7, Khalid Jebbari wrote:
> I have a question for you Kevin : when you modeled your logic with a state machine, were you able to unit test it outside of the browser ?

Yes, the statecharts are defined within a Datomic (server) or DataScript (browser) database and the transition function (basically: (transition statechart event) ;;=> new-statechart) is written in CLJX and runs on both ClojureScript and Clojure.

Ruslan Prokopchuk

unread,
May 14, 2015, 2:26:12 PM5/14/15
to clojur...@googlegroups.com
Jamie, exactly, I took re-frame (it's awesome!) and replaced subscriptions mechanism with Javelin cells. I like Javelin, it allows elegant and succinct data coordination. See todomvc example in the amper and re-frame repos for comparison.

Also I've replaced Reagent with Om because of my internal needs, but re-frame architecture is View-agnostic in its heart, and I've implemented it in ampere. Now it includes only Om adapter, but more to come with examples (I plan to make todomvc views.cljs port for every supported View library). Hoplon does not require any adapter at all, for example ;-)

Jamie Orchard-Hays

unread,
May 14, 2015, 2:38:59 PM5/14/15
to clojur...@googlegroups.com
This is really interesting stuff. I'd looked over Hoplon a year ago and didn't use it as it wasn't React-based. I really liked the spread-sheet/cell metaphor. I wish I had more time to explore all of these libs! :) CLJS is enjoying quite a Cambrian explosion of interesting libraries.

Jamie

On May 14, 2015, at 2:26 PM, Ruslan Prokopchuk <fer....@gmail.com> wrote:

> Jamie, exactly, I took re-frame (it's awesome!) and replaced subscriptions mechanism with Javelin cells. I like Javelin, it allows elegant and succinct data coordination. See todomvc example in the amper and re-frame repos for comparison.
>
> Also I've replaced Reagent with Om because of my internal needs, but re-frame architecture is View-agnostic in its heart, and I've implemented it in ampere. Now it includes only Om adapter, but more to come with examples (I plan to make todomvc views.cljs port for every supported View library). Hoplon does not require any adapter at all, for example ;-)
>

Colin Yates

unread,
May 14, 2015, 3:35:10 PM5/14/15
to clojur...@googlegroups.com

+1 I keep thinking "yeah, this is the stack I will use, let's invest in this" then something new comes along. Not good for those of use affected with "grassisalwaysgreeneritus" :).

Jamie Orchard-Hays

unread,
May 14, 2015, 4:20:50 PM5/14/15
to clojur...@googlegroups.com
No kidding. I have this long blog post germinating in my head about my experiences with Om and re-frame now that I've developed a reasonably-sized app in each. Problem is, I have no time to write it. One thing I've come to appreciate about Om over Reagent is that despite it being more verbose, it's always clear where you are WRT the React lifecycle and state. Reagent, being less formal, lends itself to some confusion over what's happening where.

In general, I agree with some comments I've seen in this group recently that we really have a long way to go with rich client web apps. It's still way too time-consuming, painful and not formalized enough, even with the awesome tools we have around already. Simple *and* easy is the brass ring.

Khalid Jebbari

unread,
May 14, 2015, 4:45:23 PM5/14/15
to clojur...@googlegroups.com
Generating a functional state machine from a diagram is awesome. Generating a diagram from the code of a state machine is awesome too.
But state diagram are only 1 of the 2 visual representations of state machines AFAIK. The other is the state transition table : https://en.wikipedia.org/wiki/State_transition_table
I already opened an issue to the automat repo, hoping Zach Tellman will consider generating the table from the code being a good idea.  And also in Ruslan's vfsm to generate a state machine from a table. Visual programming for the win, really.

Khalid aka DjebbZ
@Dj3bbZ

You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/7STtgK5QiIc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Marc Fawzi

unread,
May 14, 2015, 5:01:30 PM5/14/15
to clojur...@googlegroups.com
Reagent's create class allows normal React lifecycle callbacks without any verboseness. 

However, the big surprise for me was finding out that both Reagent and Om use async updates for everything when React has problems with it in some cases, like controlled inputs. 


Dan Homlsand (Reagent's author) is looking into it, so we'll see what he'll come back with. I might be naive here but I'm hoping that we can use async setState for all updates except for controlled inputs.


Sent from my iPhone

Jamie Orchard-Hays

unread,
May 14, 2015, 5:28:26 PM5/14/15
to clojur...@googlegroups.com
Interesting article. Pondering if some of my struggles have been around that issue, but not sure off the top of my head.

I've definitely used Reagent's create-class on a couple of complex components. 

Jamie

Jamie Orchard-Hays

unread,
May 14, 2015, 5:34:03 PM5/14/15
to clojur...@googlegroups.com
I'm still in the early stages of digesting Javelin, but one idea I keep having is using it locally in components to make subscriptions that are otherwise global using reframe. 

Daniel Kersten

unread,
May 14, 2015, 8:27:46 PM5/14/15
to clojur...@googlegroups.com

Personally I find that moving state out of components as re-frame's subscriptions and handlers encourage is a desirable trait and would be cautious about reintroducing local state.
Keeping my data in one place (and handling updates and queries through a centralised place) has made it a lot easier for me to manage complex data and logic.

I've played with javelin in the past and it's a fantastic library. I quite like the idea of using it as a  replacement for (or perhaps together with?) re-frames subscriptions (so reagents ratoms, really), but in my opinion reliance on local state is a mistake.

Having said that, I'd love to hear counterpoints.

I'm quite interested in the topic of using state machines too. As re-frames readme mentions, app-db updates can be thought of as state transitions, but I think having well defined named states is a good idea as it's very difficult to determine what "state" your application is in by looking at it's data for any non trivial application. I also like the idea of knowing in advance what the valid transitions from any given state are as it's useful for generative testing and debugging and overall understanding of supplication logic.

I'm currently mulling over the idea of combining re-frames app-db with a state machine (perhaps using automat). I feel like maybe a hybrid approach could work well, but an unsure how it would look.

Jamie Orchard-Hays

unread,
May 14, 2015, 8:54:42 PM5/14/15
to clojur...@googlegroups.com
Actually, I'm interested in local transitions (cell=) rather than local state. That is, a view may be interested in transitions that only apply to itself. I like the idea of encapsulating those transitions into the view itself. re-frame's subscriptions are inherently global, at least to whichever namespaces have required the subscriptions. This offers better encapsulation as I understand them. 

Jamie

Jamie Orchard-Hays

unread,
May 14, 2015, 9:12:01 PM5/14/15
to clojur...@googlegroups.com
Speaking of Cambrian Explosion, I saw in the latest LispCast a link to a new React CLJS lib from weavejester called Brutha: https://github.com/weavejester/brutha

Jamie

Marc Fawzi

unread,
May 14, 2015, 9:22:46 PM5/14/15
to clojur...@googlegroups.com
Thanks Brutha! :)

Sent from my iPhone

Khalid Jebbari

unread,
May 15, 2015, 12:47:14 AM5/15/15
to clojur...@googlegroups.com
In the case of global app state combined with a state machine, where would the URL be stored ? Should it be considered a trigger/event of the state machine ? Should it be stored in the state as any other data ?
You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/7STtgK5QiIc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Mike Thompson

unread,
May 15, 2015, 1:08:32 AM5/15/15
to clojur...@googlegroups.com
On Friday, May 15, 2015 at 10:27:46 AM UTC+10, Daniel Kersten wrote:
> Personally I find that moving state out of components as re-frame's subscriptions and handlers encourage is a desirable trait and would be cautious about reintroducing local state.
>
> Keeping my data in one place (and handling updates and queries through a centralised place) has made it a lot easier for me to manage complex data and logic.
>
> I've played with javelin in the past and it's a fantastic library. I quite like the idea of using it as a  replacement for (or perhaps together with?) re-frames subscriptions (so reagents ratoms, really), but in my opinion reliance on local state is a mistake.


I'd like to violently agree with you. :-)

State in the one place simplifies so much. The moment you have state in multiple places, and that state needs synchronization, you have a problem.

The strategy for solving that problem will involve either (1) things watching other things for changes or (2) things telling other things they have changed. The OO paradigm encourages a lot of distributed, synchronized state, and the "The Observer pattern" is used to handle it.

Those that use component local state with OM, have the same issues as the OO paradigm. Sometimes they use a global bus to achieve synconization (things telling other things that something has changed) which is a similar pattern to various OO framemworks, like PureMVC.

In the functional programming space, the movement seems to be towards FRP. Data flows into functions and out again, in something of a pipeline. The structure of those flows is more declarative WRT time.

The thing is this: synchronization of state is a pain. Have as little of it going on as you can. Putting all your data in the one place certainly saves you from a certain class of problem -- reintroducing distributed state WILL cause you some grief. I'm not saying "never do it", but I am saying "there'd want to be a big payoff to warrant the pain".

--
Mike

Jamie Orchard-Hays

unread,
May 15, 2015, 8:57:51 AM5/15/15
to clojur...@googlegroups.com
Here's where I find local state useful and I am curious how advocates of centralized state handle it:

A view component that allows editing. For example, click on the text and it changes to an input element. To me that is local state: "I'm in reading mode. Now I'm in editing mode." In my own app, I don't allow other components to change to editing mode when any other is in editing mode. This is a global state. So there is coordination, but it's not terrible as the component sets its local state and dispatches the handler for the app state at the same time.

How do you all do it?

Jamie

Daniel Kersten

unread,
May 15, 2015, 10:31:05 AM5/15/15
to clojur...@googlegroups.com
Am I correct that what you want is a temporary "scratchpad" where you make edits which can then be committed to the global state, or discarded, based on user action?

There are two ways that I've used to do this:
The first is to take a snapshot of the state before making changes (perhaps using something like re-frame's undo feature). This way, the application can react to the changes immediately and you don't need any special logic to pull/push data from/to global state and components can read data as they normally would and updates can be made through handlers same as everywhere else. To commit, you don't need to do anything[1], to revert, you throw away the changes by reverting back to the "before edit" snapshot.

The second technique I've used is to store the "edit mode" data elsewhere in your global data (I've got a :scratchpad key in my app-db for things like this, and other things like "what item in a list is selected"). You would have to copy data to/from this the same way as with global state, so you don't win anything in the "keeping data synchronised" department, but the benefit over local state is that your components are dumb[2], your update logic happens in one place and in a consistent way[3] and edits can take part in whatever dataflow logic is applied to global state (eg re-frame's undo; other components can subscribe to it etc).

Now, with that said, there are times when there's no way around using local state, in which case I DO use it. But I treat this is a technical detail (either a performance optimisation or to get around the React controlled input "glitch" where cursor position is lost if updating an inputs value async).

This is how I handle it anyway. I do agree that the coordination isn't a big deal in this case and perhaps something like Javelin would actually make it a non issue, but I do feel that there are significant advantages to keeping data global and isolated from view logic and update logic.

[1] Realistically, you probably still trigger a commit event to sync with the server or clear the "is editing" flag and throw away the snapshot. 
[2] Dumb components are less coupled, easier to understand, easier to test.
[3] This makes it easier (for me at least) to understand my code/data, makes it easier to debug and easier to test. It also makes it easier to build development tools (for example, I've got a (currently unreleased) app-db viewer and handler event logger - if I store data locally, then updates are invisible to these tools).

Colin Yates

unread,
May 15, 2015, 10:36:02 AM5/15/15
to clojur...@googlegroups.com
For me, I can't use the 'snapshot app-db and discard' as the app-db is
synchronised with the server periodically. As you mention, I have a
number of roots in my app, one for 'ui' entries and one for 'views'
which are populated by the server, even if I want to discard the 'ui'
root I really don't want to discard the 'views'.

I avoid local state as well simply because the benefit of being able
to capture the whole app-db and attach it to a bug report is awesome.

Mike Thompson

unread,
May 15, 2015, 10:56:27 AM5/15/15
to clojur...@googlegroups.com
On Saturday, May 16, 2015 at 12:36:02 AM UTC+10, Colin Yates wrote:
> For me, I can't use the 'snapshot app-db and discard' as the app-db is
> synchronised with the server periodically. As you mention, I have a
> number of roots in my app, one for 'ui' entries and one for 'views'
> which are populated by the server, even if I want to discard the 'ui'
> root I really don't want to discard the 'views'.
>
> I avoid local state as well simply because the benefit of being able
> to capture the whole app-db and attach it to a bug report is awesome.


We've now hooked window.onerror and in production, and when there's an unhandled exception, we automatically do an "undo", to take the user back to the previous known sane state. It works a treat. (Not that we get many UHE, you understand :-))

It means the user still has a working program (with error message), and hasn't lost anything because of the problem.

AND, as you say, we serialise the app-db and the event which caused the UHE, to form the perfect, reproducible bug report. (We also have a log of all previous events too ... because they are pure data).

It brings a tear to my eye its so damn beautiful.

That all comes almost for free because the data is all in the one place and because events are pure data to boot.


--
Mike

Colin Yates

unread,
May 15, 2015, 11:03:49 AM5/15/15
to clojur...@googlegroups.com
Data for the win.

I remember in the olden days when AOP reared its head and people were
talking about retrying updates against stale data/exceptions. I was
always/still am a bit cynical because I just don't see that many
exceptions where the exception is transient and the second attempt
would work.

I would be very interested to know how many UHEs are genuinely
'transient', in that they don't happen again when the user tries to do
the same thing - do you have any metrics?

Daniel Kersten

unread,
May 15, 2015, 11:12:08 AM5/15/15
to clojur...@googlegroups.com

I would assume you're right that very few are transient. At least with Mikes approach, you can log the error, notify the user and continue working from a clean state (but I assume if the user does the same thing again the error would happen again - that is, they can continue doing "other" things).

Mike Thompson

unread,
May 15, 2015, 7:59:15 PM5/15/15
to clojur...@googlegroups.com
On Saturday, May 16, 2015 at 1:03:49 AM UTC+10, Colin Yates wrote:
> Data for the win.
>
> I remember in the olden days when AOP reared its head and people were
> talking about retrying updates against stale data/exceptions. I was
> always/still am a bit cynical because I just don't see that many
> exceptions where the exception is transient and the second attempt
> would work.
>
> I would be very interested to know how many UHEs are genuinely
> 'transient', in that they don't happen again when the user tries to do
> the same thing - do you have any metrics?


None of ours have been transient (small sample size). But that's because our apps are quite self contained and don't do a lot of chatting with outside servers. Ie. their state is fairly self contained.

--
Mike


Ruslan Prokopchuk

unread,
May 17, 2015, 6:15:56 AM5/17/15
to clojur...@googlegroups.com
Re: Javelin & subscriptions.

Code to mimic subscriptions with cells and to use them as ratoms in Reagent code is pretty straightforward:

Definition: https://github.com/ul/ampere/blob/master/src/ampere/adapters/reagent.cljs#L5

Usage in Reagent view: https://github.com/ul/ampere/blob/master/examples/todomvc/src/todomvc/views/reagent.cljs#L30

Mike Thompson

unread,
May 17, 2015, 9:19:54 AM5/17/15
to clojur...@googlegroups.com
Hey, good to see re-frame port, and also work on applying FSM. I really think there's a lot of potential in that direction.

But I can see 3 potential problems ...

First, when the UI is a bit dynamic and Reagent components are being created and destroyed, I think that use of add-watch in subscriptions might cause a memory leak ... worse because they don't go away, you'll end up with old unused ones firing and slowing things down. At least, it seems like that on first look (claim untested).

Second, for larger apps, you want your subscriptions to be created only when needed. When you have "page 10" open, you don't want "page 2" subscriptions firing because that page isn't even visible (rendered). Your approach appears to require that "cells" and "formulas" for all possible subscriptions are created up-front (and are then used by subscriptions). Again, on first glance that seems like it would work for a simple case, but not scale up.

Third, subscriptions sometimes need to be parametrized. I wonder how that would be done?

There might well be solutions to all these issues, and perhaps I missing something, but I thought I'd jot these notes down quickly.

--
Mike

Marc Fawzi

unread,
May 17, 2015, 5:18:51 PM5/17/15
to clojur...@googlegroups.com
Don' mean to hamper enthusiasm but here is why code generation from FSM charts can at most be a purely educational exercise. This guy captures it well:

http://www.skorks.com/2011/09/why-developers-never-use-state-machines/

Doing it the other way around, i.e. Generate FSM charts from code (execution traces) offers all the debugging and visualization benefits without the catch-22 mentioned in the article.



Sent from my iPhone

Dave Sann

unread,
May 17, 2015, 5:50:14 PM5/17/15
to clojur...@googlegroups.com
The conclusion at the end of his article is very different from the catch-22 suggested to stop people using this at the beginning

Dave

Daniel Kersten

unread,
May 17, 2015, 5:55:46 PM5/17/15
to clojur...@googlegroups.com
I think it boils down to familiarity. Anything unfamiliar will fall under YAGNI (until you do) because there is a learning curve involved. Once you learn it, though, you may find it worth reaching for much sooner.

For example, in some domains, FSM's are very common (embedded systems and game AI spring to mind).

On Sun, 17 May 2015 at 22:50 Dave Sann <dave...@gmail.com> wrote:
The conclusion at the end of his article is very different from the catch-22 suggested to stop people using this at the beginning

Dave

Marc Fawzi

unread,
May 17, 2015, 6:02:44 PM5/17/15
to clojur...@googlegroups.com
So the reason i haven't used them for code generation or worked with anyone that has is  just circumstantial and does not imply obscurity? 

I think they have they're niche and digital hardware design and game AI are the hot spots?

Sent from my iPhone

Daniel Kersten

unread,
May 17, 2015, 6:12:59 PM5/17/15
to clojur...@googlegroups.com
Probably, although I wouldn't blame you - I've not used them myself outside of toy projects (and that was some time ago now).

Not necessarily hardware design - they're apparently quite common in microcontroller programming too.

They seem like a good natural fit for these kinds of applications (the hardware is in "waiting for a command" state; the game character is in "search for player" state, ...) and probably not such a neat fit for, say, serving templated HTML and what the article says does feel rather true to me (and probably why I haven't tried it yet either): it is a lot of work to get started. But, as is being discussed in this thread, it does seem like it may be worthwhile and its definitely worth exploring more, especially if we can figure out a way to reduce the initial learning curve.

Marc Fawzi

unread,
May 17, 2015, 6:44:06 PM5/17/15
to clojur...@googlegroups.com
Not "stop" but just consider the reality of it in the general case. I just found that Unity3D had many FSM tools as plugins and they are very popular for game AI along with behavior trees etc.

Sent from my iPhone

> On May 17, 2015, at 2:50 PM, Dave Sann <dave...@gmail.com> wrote:
>
> The conclusion at the end of his article is very different from the catch-22 suggested to stop people using this at the beginning
>
> Dave
>

Colin Fleming

unread,
May 17, 2015, 7:18:28 PM5/17/15
to clojur...@googlegroups.com
I don't agree that the conclusion to be drawn from that article is that FSMs can be at most an educational exercise - quite the opposite. He argues at the start that no-one would ever retrofit a FSM to existing code, but then describes a case where they did exactly that (albeit painfully) and said it was great. He seems to be arguing quite the opposite - that we should be using state machines more.

I've used them a lot, although mostly in "classical" applications like lexing and parsing. But I've used them occasionally for logic too, and it's always worked very well. I'm not a web developer but it definitely seems like they would be excellent for modelling webapp state.

Zed Shaw has a great article about specifying servers with state charts here: http://zedshaw.com/archive/ragel-state-charts/. For those interested in FSMs, Ragel is definitely worth a look, it's a really lovely piece of software.

Daniel Kersten

unread,
May 17, 2015, 7:22:32 PM5/17/15
to clojur...@googlegroups.com

I agree, Colin. I read his conclusion that FSM's are great and not so hard I once you're used to them but that until you're well enough versed they seem too much effort (YAGNI) and when the code is complex enough that it's no longer the case, it's to expensive to change.

I'll definitely check out Ragel.

Marc Fawzi

unread,
May 17, 2015, 7:56:15 PM5/17/15
to clojur...@googlegroups.com
Ha! Their use for logic synthesis outside of digital circuits is a niche case but maybe not as not niche as i thought

I would consider them for visualization and debugging, not code synthesis as i just cant see how that would work out for me

Sure for parsing there is even the option of auto generating regexes from fsm

Interesting that you have user them for logic synthesis

Sent from my iPhone

TP

unread,
May 18, 2015, 4:29:40 AM5/18/15
to clojur...@googlegroups.com
On Sun, May 17, 2015 at 2:15 PM, Marc Fawzi <marc....@gmail.com> wrote:
> Don' mean to hamper enthusiasm but here is why code generation from FSM charts can at most be a purely educational exercise. This guy captures it well:
>
> http://www.skorks.com/2011/09/why-developers-never-use-state-machines/
>
> Doing it the other way around, i.e. Generate FSM charts from code (execution traces) offers all the debugging and visualization benefits without the catch-22 mentioned in the article.

Scott Wlaschin enthusiatically describes creating a F# FSM in
"Calculator Walkthrough: Part 4 - Designing using a state machine" [1]

[1] http://fsharpforfunandprofit.com/posts/calculator-complete-v2/

Khalid Jebbari

unread,
May 18, 2015, 6:07:22 AM5/18/15
to clojur...@googlegroups.com
Trying to push forward the discussion about Web UI with state machines. I came up with the following decomposition of the core components of a web application :

- application state
- application data
- business logic
- ui logic
- event processing
- presentation layer
- routing

In this schema, I think the application state is the real core, because every other components is directly related to it, at least if you use a state machine. I came up with the following model.

- application data : related to application state because both can easily represented as data. If we want a web app that is completely state-driven (I want this, for debugging, testing and time-travel capabilities), simply merge the data and the state in the same data entity.

- business logic/ui logic : in a state machine there's the notion of "actions" executed with each transition (where necessary). So the logic could just be executed by the state machine itself.

- event processing : a state machine can be event-driven, and this a perfect match with a web app since the web (and any UI for that matter) is inherently event driven. So the event/input of the state machine could just match the event triggered by the user, as well as custom events if necessary.

- presentation layer : simply display the current app-state as HTML/CSS. In the React.js model, it would simply mean updating the app state and letting React render everything.

- routing : this is where stuff gets complicated in my mind. In a proper application, lot of state is derived from the URLs. But not all state, for instance whether a modal is displayed or not, or whether a form is validated client side or not isn't tied to a URL. Which tend to let me think that there's some kind of hierarchy in the state machine. The URLs could be represented as events as well in the state machine, but could happen at anytime, whereas other events and related transition depend on the current state in a state machine. So it's like you have a top-level state machine for URLs, and each URL has its own state machine for all interactions in the page. Maybe page-state machine could be refined in multiple levels state machines too, not sure about that. It seems like Hierarchical State Machine may help here, but I haven't studied the subject yet at all.

What do you think ?

Sean Tempesta

unread,
May 18, 2015, 9:57:44 AM5/18/15
to clojur...@googlegroups.com
Hi Khalid. I found your topic interesting so I thought I'd chime in. Regarding your comments on routing:

So, under normal conditions, the initial URL sets the FSM in motion (as an event). We could call this entry point a routing state. Afterward, the state transitions are controlling the urls (not the other way around), right?

Outside of normal conditions (ie. people copying and pasting links into random parts of the system), you also just send the url to the routing state and then switch to a new state based on whatever rules and definitions you've set.

Or maybe I'm missing something. I haven't built an FSM in a while. :)

Sean

Marc Fawzi

unread,
May 18, 2015, 11:55:52 AM5/18/15
to clojur...@googlegroups.com
Games are ideal candidate for straight-forward FSM implementation since you normally download the data at game load time and from there on you have a *relatively* small set of states that can be transitioned between based in user input. You can even apply state minimization techniques to reduce the total number of states.

But in a web app you are continuously grabbing data from the server and that data is generated based on not only user input but also the state of the server side database and that server generated data would modify UI side app state and you have to account for all possibilities so the total number of states could grow wildly if your UI is data driven (where the state of the UI depends on the data in non-trivial ways) but even if your UI state dependence on server data was a trivial relationship you could still end up with a huge state diagram for the simplest viable business app if you include templating the view as part of the UI FSM on top of business logic. You could segment your app into micro apps and that will help regardless of whether you're building the app as FSM or not.

And what if the state transitions are probability driven? How many states will you end up having to chart?

Not convinced YET...

Sent from my iPhone

Khalid Jebbari

unread,
May 18, 2015, 1:01:55 PM5/18/15
to clojur...@googlegroups.com
Indeed, the combination of all states/transitions could lead to something unreadable and unmanageable. I was talking about that with a colleague the other day. Don't have a solution (yet), I'm still learning and reading about FSM.

As I was saying in my previous post, having some sort of hierarchy in states machines could help. It boils down to composability of state machines. A few weeks ago some folks showed me a web UI where they could drag and drop and connect state machines to produce a bigger one. The resulting state machine basically generated a new application they could use for a client. I was REALLY impressed. They were using Elixir for that but it doesn't matter.

So, compatibility is key here. The aforementioned clojure libs are using data to represent FSMs, so the composition can be made easily I think (from a syntax point of view at least).

Needs more thinking...

Daniel Kersten

unread,
May 18, 2015, 1:13:23 PM5/18/15
to clojur...@googlegroups.com

From my understanding of it:

Use higher level states and decouple them somewhat from the data.

For example, games do have lots of dynamically changing data. In a modern shooter you might have dozens of characters with positions, orientation, velocity, health information, weapons, ammunition, etc all of which can be  constantly changing. And that's just taking the characters into account.

I wouldn't go and build a state machine that enumerates all of the possible transitions from a "twelve characters with done distribution of attributes in this location moving in that direction" state. I'd break it down so that each character has a high level state like "seeking powerup" or "running".

Probably not a great example although it does illustrate that you might have a hierarchy of state machines. In the game example, the highest level might be something like "in play" or "paused" and the lowest might be an each characters "firing weapon".

In client side web app, you could say that each configuration of data is a state (the re-frame readme mentions that you could think of the app-db like this), but I think that's too fine grained to be useful.

Instead I'd define higher level states (possibly in a hierarchy). I'd ask myself, regardless of the data available, what are the logical states that a user could be in and for each one, what are the actions that they can perform (and what state does each action transition them to).
This could be as simple as pages and links, but with a rich single page application it's more likely finer grained than that. Maybe what dialogs or widgets are accessible.

Again, you could then layer these into a hierarchy of state machines.

One advantage of this is you always know what a user can do at any given time because you can look at what state they're in.

I think of FSM states as orthogonal to the data, not as the data itself. The states dictate what data is accessible and what can be done to it; the data doesn't dictate what state the application is in.

I suppose terminology gets confusing, but this is the approach I'm toying with. I'll see how that goes :)

But yeah, needs more thinking.

Khalid Jebbari

unread,
May 18, 2015, 1:26:51 PM5/18/15
to clojur...@googlegroups.com
I like how you break up the state machines, it has sense in web app. Page 1 has 2 widgets, page 2 has a form. Each widget/form can have a FSM associated with it, the higher level FSM knowing just the higher level state of all widget displayed. Mmmh... Interesting. 
You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/7STtgK5QiIc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Marc Fawzi

unread,
May 18, 2015, 1:26:53 PM5/18/15
to clojur...@googlegroups.com
Imagine a search box that takes some query and depending on the server interpretation of the query the client needs to transition to some state.

That's not your average scenario but its a whole category of apps where the UI state depends on the data.

You can say that classical FSM is not fit for this category of apps. Or you can think some more ... :)

Sent from my iPhone

Marc Fawzi

unread,
May 18, 2015, 2:27:40 PM5/18/15
to clojur...@googlegroups.com
<<
Imagine a search box that takes some query and depending on the server interpretation of the query the client needs to transition to some state.
>>

btw, this is just an example for the data-driven UI category but the scenarios where server generated data has to drive UI state are plenty and can be found in almost every sufficiently complex app.

Marc Fawzi

unread,
May 18, 2015, 4:23:25 PM5/18/15
to clojur...@googlegroups.com
Back to composability

I read about stacked vs hierarchical FSMs and it looks like what you want is a stacked one not a hierarchical one... Subgraphs dont  have to be entangled with the global graph

Sent from my iPhone

Khalid Jebbari

unread,
May 18, 2015, 4:28:54 PM5/18/15
to clojur...@googlegroups.com
Thanks for the clarification, I didn't about them. 

Now I need even more reading and thinking :)

Marc Fawzi

unread,
May 18, 2015, 4:35:01 PM5/18/15
to clojur...@googlegroups.com
"stack-based" is not exactly "stacked" but I was thinking chip design for some reason (http://en.wikipedia.org/wiki/Three-dimensional_integrated_circuit

The one diagram that made it obvious:

Khalid Jebbari

unread,
May 18, 2015, 4:49:04 PM5/18/15
to clojur...@googlegroups.com
This is it, Marc. The SFSM diagram represents exactly what I had in mind. Do you think they're viable in the context of a web app ? As long as they can be stacked as needed, they could handle any complexity of UIs, no ?

Khalid aka DjebbZ
@Dj3bbZ

Khalid Jebbari

unread,
May 18, 2015, 4:52:01 PM5/18/15
to clojur...@googlegroups.com
Regarding SFSMs, it looks like the top-level states would be URLs (in a well behaved application), and the nested ones would be for any widgets inside the pages. Just thinking out loud.

Khalid aka DjebbZ
@Dj3bbZ

Marc Fawzi

unread,
May 18, 2015, 5:55:27 PM5/18/15
to clojur...@googlegroups.com
with a programing language like ClojureScript, we have multiple paradigms to deploy to tackle different kinds of design challenges and come up with an optimal solution to each challenge. 

It's hard to see how using a single-paradigm tool like FSM or SFSM or even EFSM (the most robust FSM which tackle the complexity of web software creation [1]) can deal with the novel challenges I run into when building a cutting edge web app (e.g. real time, complex interactive data visualizations) 

When was the last time when you've worked on a novel feature and did not meet a novel challenge? Novel challenges abound in R&D oriented product development and they are best dealt with when you have multiple paradigms that you can deploy against it. 

If you're building cookie cutter, conventional, no thrills web apps, then there are many single-paradigm tools that can be used, including but not limited to FSMs. 

Khalid Jebbari

unread,
May 18, 2015, 7:01:05 PM5/18/15
to clojur...@googlegroups.com
Indeed I was thinking during the while about the last project I participated, a classical web site with e-commerce processes and forms, for which FSM would have been useful (both client side and server side). I agree with you, there's no silver bullet when we consider the variety of applications possible on the web platform.

Mike Thompson

unread,
May 19, 2015, 12:17:40 AM5/19/15
to clojur...@googlegroups.com
On Tuesday, May 19, 2015 at 3:13:23 AM UTC+10, Daniel Kersten wrote:
> From my understanding of it:
>
> Use higher level states and decouple them somewhat from the data.
>
> For example, games do have lots of dynamically changing data. In a modern shooter you might have dozens of characters with positions, orientation, velocity, health information, weapons, ammunition, etc all of which can be  constantly changing. And that's just taking the characters into account.
>
> I wouldn't go and build a state machine that enumerates all of the possible transitions from a "twelve characters with done distribution of attributes in this location moving in that direction" state. I'd break it down so that each character has a high level state like "seeking powerup" or "running".
>
> Probably not a great example although it does illustrate that you might have a hierarchy of state machines. In the game example, the highest level might be something like "in play" or "paused" and the lowest might be an each characters "firing weapon".
>
> In client side web app, you could say that each configuration of data is a state (the re-frame readme mentions that you could think of the app-db like this), but I think that's too fine grained to be useful.
>
> Instead I'd define higher level states (possibly in a hierarchy). I'd ask myself, regardless of the data available, what are the logical states that a user could be in and for each one, what are the actions that they can perform (and what state does each action transition them to).
>
> This could be as simple as pages and links, but with a rich single page application it's more likely finer grained than that. Maybe what dialogs or widgets are accessible.
>
> Again, you could then layer these into a hierarchy of state machines.
>
> One advantage of this is you always know what a user can do at any given time because you can look at what state they're in.
>
> I think of FSM states as orthogonal to the data, not as the data itself. The states dictate what data is accessible and what can be done to it; the data doesn't dictate what state the application is in.
>
> I suppose terminology gets confusing, but this is the approach I'm toying with. I'll see how that goes :)


As Kevin Lynagh said earlier in this thread, the best book around on this subject is Horrocks':
http://www.amazon.com/Constructing-User-Interface-Statecharts-Horrocks/dp/0201342782

Kevin also mentions a book I've not read yet: "Practical UML Statecharts in C / C++".

--
Mike


Khalid Jebbari

unread,
May 19, 2015, 1:42:39 AM5/19/15
to clojur...@googlegroups.com
Am reading Horrocks' book. Did you ?
> --
> Note that posts from new members are moderated - please be patient with your first post.
> ---
> You received this message because you are subscribed to a topic in the Google Groups "ClojureScript" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojurescript/7STtgK5QiIc/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to clojurescrip...@googlegroups.com.

Jamie Orchard-Hays

unread,
May 19, 2015, 9:34:49 AM5/19/15
to clojur...@googlegroups.com
I agree. The word that came to mind while reading your comments, Daniel, was "modularity". Does modularity imply local state? Pondering...

Jamie

Daniel Kersten

unread,
May 19, 2015, 11:58:22 AM5/19/15
to clojur...@googlegroups.com
I don't think it implies local state, necessarily, although it may benefit from it. I think alternatives can be modular too.

For example, re-frame's approach of a central place to store data is like a Blackboard system, which may even help modularity, not hinder it, because modules don't need to know anything about each other - only that the data they read and write is in the central place and may be (transparent to the modules) be accessed/updated by multiple modules transparently (and hopefully gets validated eg through a schema or constraint system to prevent one module from writing data that breaks another module).

On the other hand, local state implies encapsulation and encourages strict interfaces to access it, which can also help modularity. In my personal experience this has (more often than not) led to tightly coupled modules instead, however.

I personally prefer the re-frame single-place-for-data approach because in my opinion its benefits outweigh its disadvantages, but perhaps I've just been doing local state wrong :) (actually pretty likely!)


PS: It'll probably be some time before I get a chance to read Horrocks' book. If anybody knows of any similar content available on the web for me to read in the meantime, I'd love to hear of it!

Jamie Orchard-Hays

unread,
May 20, 2015, 9:51:39 AM5/20/15
to clojur...@googlegroups.com
For local state, I mean state that has to do only with the component itself, nothing to do with the data itself. For example, if I have a component that can switch between editing/reading states, I can't imagine why I would want this information stored outside of the component itself.

Jamie

Khalid Jebbari

unread,
May 20, 2015, 9:58:18 AM5/20/15
to clojur...@googlegroups.com
You would want it if you want to inspect/debug/transmit/replay the whole the state of your application. Having nothing encapsulated and everything in a global state permits this.

Khalid aka DjebbZ
@Dj3bbZ

Jamie Orchard-Hays

unread,
May 20, 2015, 10:03:17 AM5/20/15
to clojur...@googlegroups.com
Thanks, Khalid.

Jamie

Marc Fawzi

unread,
May 20, 2015, 10:27:25 AM5/20/15
to clojur...@googlegroups.com
Yup, well captured.

Marc Fawzi

unread,
May 22, 2015, 11:58:36 AM5/22/15
to clojur...@googlegroups.com

Khalid Jebbari

unread,
May 22, 2015, 12:24:15 PM5/22/15
to clojur...@googlegroups.com
I mentioned it in the very first post of this discussion

Khalid aka DjebbZ
@Dj3bbZ

Marc Fawzi

unread,
May 22, 2015, 12:34:12 PM5/22/15
to clojur...@googlegroups.com
I just discovered it! 70 messages in this thread ;)

A tcp server (one of the examples) is nothing like the scenarios in the UI but this type of reducing is what i was converging to, not for UI though...

Sent from my iPhone

David Aiken

unread,
May 30, 2015, 9:19:09 AM5/30/15
to clojur...@googlegroups.com
Perhaps of interest: http://www.drdobbs.com/who-moved-my-state/184401643 . Miro Samek's work with statecharts became quite influential in the embedded world.

Herwig Hochleitner

unread,
Jun 1, 2015, 9:27:17 AM6/1/15
to clojurescript
Wow, big thread.
I just want to offer, how I've done a FSM implementation in CLJS:

- FSM edges are core.async channels
- FSM state changes are represented by passing a token object (which can contain additional state vars) across an edge channel
- Each state is represented by a go-loop, with a single in-edge and multiple out-edges
- After being passed the token by its in-edge, a state runs, then passes the token to one of its out-edges
All said and done, this mainly exploits the excellent work on core.async and core.match. Overall, it smells a lot like actors, only that the possible messages are pre-determined.
If you are interested, ping me for code.

Khalid Jebbari

unread,
Jun 1, 2015, 11:35:06 AM6/1/15
to clojur...@googlegroups.com
So the various states are encapsulated in a go loop ? I always knew that core.async could be used to model FSM's. 

Well, if you can share the code somehow, do so. 
--

Herwig Hochleitner

unread,
Jun 1, 2015, 12:01:12 PM6/1/15
to clojurescript
2015-06-01 17:35 GMT+02:00 Khalid Jebbari <khalid....@gmail.com>:
So the various states are encapsulated in a go loop?

In multiple go-loops, actually.
 
I always knew that core.async could be used to model FSM's.

In a way, core.async go blocks desugar into an fsm implementation (reified state, explicit state transitions). Maybe that's what makes them well-suited to implementing FSMs on top.

Well, if you can share the code somehow, do so.

Khalid Jebbari

unread,
Jun 1, 2015, 12:26:16 PM6/1/15
to clojur...@googlegroups.com
Thx ! Will be an interesting read 
--

Marc Fawzi

unread,
Nov 12, 2015, 11:31:06 AM11/12/15
to clojur...@googlegroups.com

You received this message because you are subscribed to the Google Groups "ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojurescrip...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages