Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Thi.ng/hdom – S-expression based, pure ES6 UI/VDOM components
180 points by toxmeister on Sept 23, 2018 | hide | past | favorite | 64 comments
https://github.com/thi-ng/umbrella/blob/master/packages/hdom...

Lightweight UI component tree definition syntax, DOM creation and differential updates using only vanilla JS data structures (arrays, iterators, closures, attribute objects or objects with life cycle functions, closures). By default targets the browser's native DOM, but supports other arbitrary target implementations in a branch-local manner, e.g. to define scene graphs for a canvas element as part of the normal UI tree.

Benefits:

- Use the full expressiveness of ES6 / TypeScript to define user interfaces

- No enforced opinion about state handling, very flexible

- Clean, functional component composition & reuse

- No source pre-processing, transpiling or string interpolation

- Less verbose than HTML / JSX, resulting in smaller file sizes

- Supports arbitrary elements (incl. SVG), attributes and events in uniform, S-expression based syntax

- Supports branch-local custom update behaviors & arbitrary (e.g. non-DOM) target data structures to which tree diffs are applied to

- Suitable for server-side rendering and then "hydrating" listeners and components with life cycle methods on the client side

- Can use JSON for static components (or component templates)

- Optional user context injection (an arbitrary object/value passed to all component functions embedded in the tree)

- Default implementation supports CSS conversion from JS objects for style attribs

- Auto-expansion of embedded values / types which implement the IToHiccup or IDeref interfaces (e.g. atoms, cursors, derived views, streams etc.)

- Only ~5.5KB gzipped




There is an interesting synergy at play amongst UI as s-exprs, functional programming, and components/vdom where the whole is more valuable than the sum of its parts. I was surprised to find no mention of Reagent[1] nor Om[2] both ClojureScript interfaces to React. They share a remarkable similarity with Umbrella especially the s-expr syntax. However, Reagent[1] and Om[2]'s language choice pushes the functional composition further.

I'm interested to see this how this combination of ideas spreads outside the browser and into other applications of UI. As for myself, I've been experimenting with a writing a terminal emulator/component lib[3] using Clojure that is inspired by Reagent/Om and React Native. It's been satisfying seeing the way these ideas compose.

[1] - https://reagent-project.github.io/

[2] - https://github.com/omcljs/om

[3] - https://github.com/aaron-santos/zaffre/blob/master/src/examp...


I actually did mention Reagent in the readme in this section (below the diagram): https://github.com/thi-ng/umbrella/tree/master/packages/hdom...

Quote: "The syntax is inspired by Clojure's Hiccup and Reagent projects, which themselves were influenced by prior art by Phil Wadler at Edinburgh University, who pioneered this approach in Lisp back in 1999. hdom offers several additional features to these established approaches."

However, Reagent also still relies on React under the hood whereas hdom is completely standalone. Also, hdom has a lot more features in terms of what kind of values are supported within an hiccup tree and additionally, in this latest version (5.0), hdom already has been extended to support non-DOM targets to update. Currently, the only user of this feature is the hdom-canvas package (https://github.com/thi-ng/umbrella/blob/master/packages/hdom...), which allows you to define canvas scene graphs in the same way as the rest of the UI, but those virtual shape elements are translated into canvas draw calls. A WebGL version of this is in early progress. In general, by implementing a certain interface you can support any other target context/data structure.

Ps. I've never used Om so didn't list it as an active influence.


Thank you for pointing that out. My mistake in missing it!


This is by the same guy who wrote the massive thi-ng/geom library for Clojure, so the influence is implied ;)


> - Less verbose than HTML / JSX, resulting in smaller file sizes

I always wondered: if you use reasonable compression (what is reasonably expected to be provided by CDNs these days? brotli? I'm so out of the loop..), if you use this, how much does file size actually matter? Isn't it about entropy, rather than size? Say, if every HTML tag required 136 <'s to open, and 136 > characters to close. How would it actually affect different compression algorithms?

My intuition says that we're all on this wild goose chase for smaller file size while it may (should) not matter at all.

For example, I took Wikipedia's page on entropy and replaced each < by 136*<, same for >. I bring you the file sizes for different algorithms:

            normal	crazy	% larger
  raw       423953	3470903	718 %
  Gzip -9   78897	134319	70 %
  Bzip2 -9  64286	64441	0.24 % (!)
I don't know what compression algo is de rigeur these days, and 136 <s is obviously not the same as substituting double tags for sexprs. Still, I hope we can put this file size boondoggle in the perspective of entropy one day, instead of just mindlessly chasing the character count dragon.

EDIT: turns out you can convert HTML to pseudo sexprs with some regex. here are more realistic numbers:

  	    sgml	sexpr	% diff
  raw       423953	403777	-4.8 %
  Gzip -9   78897	76936	-2.5 %
  Bzip2 -9  64286	63590	-1.1 %
Less radical, but still following the trend.


Agreed. I also don't see it as key feature, but listed it more in nod to point out that hiccup format is less verbose. smaller file size also means less to read for humans (although, in general that itself doesn't mean better legibility :)


Out of curiosity I added brotli to the test results:

            normal	crazy	% larger
  brotli    58667	76202	30 %


  $ curl -s https://en.wikipedia.org/wiki/Entropy | sed s/\</$(printf '<%.0s' {1..136})/g | sed s/\>/$(printf '>%.0s' {1..136})/g | brotli | wc -c


It may be less verbose but it fails completely to render anything if javascript is not enabled (in the browser) on the domain the scripts are to be loaded from. Bad accessibility, bad degredation, bad practice for the web.

I'm sure it's fine for commercial JS web apps, but those are bad for the web too.

HTML is the web. All this pure es6/dom stuff is a cancer.


@danpeddle already pointed out the important part, but just as an addendum...

It all depends what one wants to do with a browser, doesn't it? For some of my projects the browser is merely a sandbox for delivering design tools and I really don't care about the HTML aspects of it at all.

For other projects, I only care about HTML generation and use hdom's sister library to generate static HTML from the same components:

https://github.com/thi-ng/umbrella/tree/master/packages/hicc...

If a user has JS disabled in the browser, fine. If not, then the browser app can hydrate the static HTML and add interactive features and cause cancer :)


> It may be less verbose but it fails completely to render anything if javascript is not enabled (in the browser) on the domain the scripts are to be loaded from. Bad accessibility, bad degredation, bad practice for the web.

I call BS on that. The accessibility tools that people actually use only work with "proper" browsers (counting in IE here) that all support Javascript. Modern browsers don't even officially support disabling Javascript. For most web apps, the only feasible way to degrade from "no Javascript" is to display "this page requires Javascript". Spending any more effort here because it's "good practice" is a complete waste of time. Put some effort into testing screen readers instead.

> HTML is the web.

Nonsense.

> All this pure es6/dom stuff is a cancer.

Hey, who are you to tell people what to use the web for? If somebody wants to serve as static HTML page, nobody is stopping them. Nobody is forcing people to use some Javascript framework for that.


> Modern browsers don't even officially support disabling Javascript.

That's right. The big browsers have all been co-opted (even firefox) and now target Grandma browsing Facebook and other SPA as their demographic. This is bad for the web but very good for corporations making money. These two things aren't very compatible no matter how much most web devs are invested in not realizing there's a difference.

I'm telling you how I see it and how I design my sites. None of them require javascript to function (even for my comment system). I can do this because I'm not being paid I'm just doing it for fun. I understand people being paid have to make bad websites. But that doesn't make it okay.


just wanted to point out that the joy of these approaches is that you can render on the server with the same components. the method for feeding it data obviously differs, but that's (when done right) pretty minimal.

my favourite way of demonstrating this was actually to turn js off and still see everything working. the progressive enhancement flag is still flying, and tools like this make it easier by far.

in this case, the server could be (soon!) java/clojure, which is insanely hard to do in an idiomatic, pleasant way with react components. really looking forward to getting this all wired up, and if anyone is interested we're discussing how to get hdom talking to CLJS here: https://github.com/thi-ng/umbrella/issues/36


I recall doing a benchmark of the Clang compiler that showed that parsing the source files takes much more time than any other stage of compilation.

Decompressing a larger amount of content on the client side, and then parsing it, is going to take more resources. Now, it may be a minuscule amount for one page load, but if you multiply it by hundreds of requests and thousands of users, I can see why smaller file sizes can be still be important.


For me personally, smaller file sizes and less verbosity both improve readability.


For me generally too, but I was afraid of the typical counter example: Perl oneliners, size coding in general...

I think there's fine line between aiming to be lightweight and aiming for smallest whatever by sacrificing all other aspects . I'm definitely in the former camp...


Nice visualizations. I particularly liked https://github.com/thi-ng/umbrella/tree/feature/estuary/pack.... Where can I find more stuff like this? :)


Karsten is an accomplished computational artist: http://postspectacular.com http://toxi.co.uk


hehe... just not an accomplished documenter of his works. those "websites" are all ancient. don't bother visiting!


I was a middle school student probably when I saw your hair demo in Processing. Still looks great!


Thanks! The estuary package started in Java[1] back in 2011, but after years of learning from mistakes, several attempts in Clojure & endless refactoring has now morphed in this TS package[2], which I'm slowly getting more happy with...

That demo will need to be refactored too and the plan is to extract those SVG components into their own package sometime during the winter break...

[1] https://www.flickr.com/photos/toxi/albums/72157627351329656

[2] https://github.com/thi-ng/umbrella/blob/master/packages/rstr...


I'd still rather write markup in a format that looks like markup. I think this is why JSX is so popular.

This is why I prefer something like lit-html with actual markup.

Example from hdom readme:

    // component w/ local state
    const counter = (i = 0) => {
        return () => ["button", { onclick: () => (i++) }, `clicks: ${i}`];
    };
In lit-html:

    const counter = (i = 0) => {
      return () => html`<button @click=${() => i++}>clicks: ${i}</button>`;
    }
Yeah, there some extra weight to the close tag, but it looks like HTML.

Disclaimer: lit-html author. https://github.com/Polymer/lit-html


I completely appreciate that there're many people who much prefer the separation between code and markup, however, if you ever worked w/ Lisp you'll also learn to appreciate that code is just data and that the earlier separation really is just a convention. Maybe that convention is much better suited for supporting different job descriptions in companies (UX vs. dev), but it's also somewhat arbitrary...

The benefits of the component as plain, non-stringified data are not the avoidance of closing tags, but what you can do with those components computationally. E.g. instrumentation, behavior/attribute injections, configuration, filtering etc. An approach based on string interpolation can only go this far, whereas having everyting as arrays, objects, iterators your possibilities of composition & transformation are almost endless...

Also, take for example the hdom-canvas support library which doesn't target the DOM itself, but defines virtual elements translated into drawing calls. Using the string interpolation approach would be complete overkill since these elements would have to be re-parsed again, in which case SVG would be faster. But I can easily manage 10k+ moving particles @ 60fps with the hdom approach:

https://demo.thi.ng/umbrella/hdom-canvas-shapes/#points-10k


Writing HTML in HTML isn't so much about separation of code and markup (for everyone at least, for some it admittedly is), but about representing DOM in the same way as HTML. The rules and semantics are the same (because we use the browser's HTML parser), it looks the same, children and attributes are represented the same. It'll be familiar and understandable to programmers new the library, but familiar with the web.

FYI, lit-html isn't string interpolation and templates are data, so you can transform and compose templates, operate on arrays/iterables of them, or Promises or async iterables of them, etc.

And it doesn't re-parse HTML (because it's not string interpolation), so that particle demo could also be fast - elements can also represent draw calls like a scene graph. I've been meaning to do a similar demo actually...


Thanks for the clarifications & apologies if I misunderstood some parts earlier. Will definitely check out your project more closely...

From a quick glance at the docs, though it seems to me there're a lot of other differences in approach (apart from the syntactic differences), especially WRT state. I will have to study these further before being able to comment properly...

Just one more note about XML vs. JS syntax to describe elements. As I said earlier, am sure there's ton of people who prefer the familiar way, but objectively, do you really find hdom's approach less readable? Honest question!

    // lit-html
    list = html`<ul>${items.map((i) => html`<li>${i}</li>`)}</ul>`

    // hdom
    list = ["ul", items.map((i) => ["li", i])]


Absolutely, because it just looks more like HTML. It becomes much more apparent as you have attributes, larger static sections of templates, and styles.

Check out this app component in PWA Starter Kit: https://github.com/Polymer/pwa-starter-kit/blob/master/src/c...

Or the template from the js-framework-benchmarks: https://github.com/krausest/js-framework-benchmark/blob/mast...

Or the template from one of the wired-elements: https://github.com/wiredjs/wired-elements/blob/master/packag...

Given HTML-specific syntax-highlighting and large templates, I really think the HTML format is easier to read.


That's the thing, though, I deeply beg to differ on that and I really think your view and statement that using HTML syntax is so much better is just very subjective. Both of our projects create and manipulate the DOM. Which syntax to use for that purpose is IMHO irrelevant as long as it produces correct results. To me HTML syntax is nothing more than a necessary evil (Btw. I've worked w/ HTML since '95), and something I'm more than glad to have almost eradicated from my professional life, nor is it something I want to still actively embrace. Judging by the popularity of XML I'm not alone... thousands of Clojure/ClojureScript users using hiccup syntax on a daily basis also do not want to go back to literal HTML after using hiccup for more than a day.

Isn't also a large appeal of working with components to compose UIs in a more abstracted manner, with different semantics? The hiccup format doesn't use anything alien to web developers and is (objectively!) less noisy in terms of punctuation to express the same concepts. And array manipulation in JS is (again objectively!) easier than manipulating the same kind of data in some <template> DOM. Hdom's syntax is arguably also less tainted by HTML, and that makes sense and was desired, precisely because it is NOT only aimed at the browser DOM, which is just one target (the default). Since the syntax is just `[tag, attribs, ...body]` it's sufficiently general to work equally well for many other use cases not aimed a browser DOM, e.g.

- Server-side rendering

- Static site generation

- WebGL UIs, shader definitions, entire scenes

- Tagged data interchange format between different apps (on top of JSON)

- 3D printing (use as interim format before generating final G-code)

- ...

Lastly, from your new examples given, in my mind, it seems your project is simply aimed at not just somewhat different use cases (albeit there are of course large overlaps), but also a somewhat different audience. Note, I'm not saying that hdom is worse or better than lit-html, it's simply based on a (quite) different philosophy. So please don't compare apples with pears!


Let's start some controversy.

I think JSX is popular because of historical reasons. Not the history of JSX, but the Web in general, specifically about HTML being the primary API to build web interfaces since the early days. Everyone feels at home with HTML because of this. We all started with it and it is unquestionable for many.

DOM is the second way to build a web interface. But nobody in their right mind would prefer DOM over HTML as long as HTML is sufficient for the task at hand.

The advent of stuff like Hyperscript and virtual DOMs of today introduced a new alternative to HTML and DOM: Now we can build our interfaces using simple data structures (POJOs). This was already possible from the beginning, but the interactivity requirements of the modern application forced us to invent the "UI as a function of the state" paradigm and this approach became mainstream after that (AFAIK).

But wait... Isn't HTML a data structure as well? Not sure. It clearly started as some kind of DSL for building web interfaces. Then XML is invented and HTML was declared as being an extension of XML; thus some kind of data structure (roughly). Then it got complicated. Today I have no idea about what HTML is.

This is the first part of the story.

The second part is about RPC. This one is short. XML used to be everywhere until the advent of JSON. Suddenly everybody started to embrace JSON and hate XML; for several reasons most of which are reasonable.

The question is....

Why isn't the same thing happening with user interfaces? HTML is a subset of XML (or something like that), and the syntax proposed in this article is clearly JSON. Why is everybody embracing JSX like crazy. Do they not find it extremely ugly, as XML is, compared to a POJO?

Is HTML really a better way to represent user interfaces than a solution such as the one presented in this article?

It may be hard to answer as we are all biased in favor of HTML.

I'm not experienced enough to make big claims, I'm just thinking out loud, I find this topic interesting. Corrections and feedbacks are welcome. Not trying to start a flame war.


My two cents: It's a generation of programmers raised on the Web who think web technology is good because that's what they know. They believe in "open standards" and "design by committee" and nonsense like "semantic HTML" being "best practice".

To everyone outside of that bubble, the following is obvious: Web technology sucks. HTML sucks. CSS sucks. Javascript sucks. They're inadequate solutions, which is why people keep reinventing the way to do web frontend every two years.

> It may be hard to answer as we are all biased in favor of HTML.

Not me.


Yeah I kinda agree with this. I'm not old enough to remember the pre-Web era but I did my share of research and know that hackers of the previous generation does not hold much love for the Web, and I'm convinced that it's for good reasons. Nowadays I feel the same way.

But this problem is not specific to our generation as far as I understand. Maybe the thing that sucks the most that became big in our generation is the Web, but weren't there similar scenarios before us as well?

Doesn't Linux suck? Systemd? Java?

Doesn't every freaking mainstream technology suck? I regularly come across rants from significant developers regarding this issue. There probably is no way to prevent this from happening. Because worse is unfortunately better.

I regularly and seriously consider to leave software development because of this (as I'm really suffering) but then I remember how much power it gives me despite the horrible faults it has.

I think it's very important to understand that the tools we work with mostly suck. In my opinion this is something that is missing from software education curriculums. People need to know and understand how and why everything is messed up and how can we work around this issue while preserving our mental health and the intellectual satisfaction we are supposed to get from the craft.


> Doesn't every freaking mainstream technology suck? I regularly come across rants from significant developers regarding this issue. There probably is no way to prevent this from happening. Because worse is unfortunately better.

Web technology is fundamentally different in that you have a big standards body regulating everything from layout down to color blending, then leave it up to multiple vendors to implement all that stuff at great expense and with long delay. Worse, it's all shoehorned on top of some "document model" than was never designed with applications in mind. It's a waste.

To see what a better platform could look like, consider this:

https://mickens.seas.harvard.edu/publications/atlantis-robus...

https://www.youtube.com/watch?v=1uflg7LDmzI

The key here is reducing surface area. It's entirely possible that WASM will bring us closer to that, but the web developers of today probably won't like that.

> I regularly and seriously consider to leave software development because of this (as I'm really suffering) but then I remember how much power it gives me despite the horrible faults it has.

I don't know what you do, but I find web development to be significantly more frustrating than other areas. That doesn't mean there isn't frustration elsewhere though and it's definitely part of the job.


Will respond more about that later, but just wanted to point you to Phil Wadler's 1999 paper about both XML and the S-expression approach (HTML/XML are essentially S-expressions too):

http://homepages.inf.ed.ac.uk/wadler/papers/next700/next700....


I hope you are aware about this attack vector[1] that was fixed in React long time ago.

1. http://danlec.com/blog/xss-via-a-spoofed-react-element


Thanks & I wasn't (aware of it). hdom doesn't use `.innerHTML` anywhere, though. So I'd dare to say less dangerous... though not saying it's out of danger!


Thanks for sharing and Kudos for TypeScript! This question is not aimed at you and certainly not at your work.

I used to be a game developer from an OOP background. I find this really hard to read.

https://github.com/thi-ng/umbrella/blob/master/examples/hdom...

Would anyone be able to give 2 cents about where the industry is headed in this regard? Will I be looked down on that my codebase is OOP in the future? Also for those who were cut from my cloth, did you find the transition difficult?


Thanks & I used to do 12+ years of Java and other OOP languages myself before I started using Clojure in 2011 and ever since got hooked on the more functional approach without trying to become a fundamentalist FP. These days I'm mainly using TS, Clojure/ClojureScript, Go, C (not C++)...

Re: the above example - you didn't really explain which aspects you find alienating, but finding this hard to read, could have to do with the fact that this demo uses a few other concepts & libs not very popular in the JS world (specifically transducers)? I totally grant that without prior knowledge of those constructs this specific code is not the easiest to comprehend (though I've tried to comment every step).

In general though, IMHO this approach is a lot more direct and lightweight than any OOP solution. Having your entire UI defined using native ES6 primitve data structures, provides huge potential and endless options to transform raw app state into UI components. If you ever worked with a Lisp, you'll also find the nested inline definitions not that hard to read. To me it's also more legible than having an (often) over-engineered, multi-layered OOP architecure, riddled with inheritance and so on... Though again, this all is highly subjective. There's certainly ample opportunity to refactor this example into smaller functions and I might as well do that when I get a chance...


Not at all.

All successful mainstream languages have embraced a mix of OOP and FP.

OOP is not a synomim for Java and C++ way of programming with classes.

There are other ways of doing OOP, even all those map/filter/fold patterns were already present in Smalltalk.

I learned OOP with Turbo Pascal 5.5, followed by C++, Clipper, Smalltalk, Eiffel, Oberon, Component Pascal, and many others.

Parallel to that, also SWI Prolog, Lisp and Caml Light as introduction to other paradigms.

It helps to keep an open mind, and approach learning by thinking about paradigms in abstract and not getting too focused on language details.


I have no idea where the industry is headed, but I feel rather confident that there will always be some group of people that will find some reason to look down on your codebase. If you rewrote the whole thing in state-of-the-art FP tonight, then by morning there will be some contingent of zealous logic programmers with their noses turnt up.

Don't worry about being looked down on. Worry about improving yourself as our field figures out how we can improve our craft. There's a lot of noise, which makes it hard to tell which things offer the most improvement, but knowledge is never a setback. You will never learn something new and be worse off than you were before. And learning something new doesn't require you to rewrite your codebase to use it, either—of course, you can if you so chose, but that's not a decision you can make until you've grokked the newness ;)

As for the difficulty of the transition: I didn't find it difficult, but I also had the luxury of time. I think it could potentially be difficult for someone thrown into the deep end (e.g., "go into the `hdom-canvas-draw` example and implement new features X, Y, and Z by Thursday!"). People often talk about FP being a different way of thinking about programming. That's true to some extent, but it's not entirely different from what you're used to. I think the thing that seems strangest for newcomers to FP isn't the way of thinking, it's the terminology. Names like `filter`, `map`, `mapcat`, `partition`, &c. seem odd and incomprehensible at first. But they're just operators, like `add`, `multiply`, `insert`, `append`. They're the kind of operator that make functional programming functional: they operate (at least in part) on functions.

If FP is something you want to learn, my advice is to start slow and simple. Some people can probably dive into that example code, tear it apart, figure out how it works, and learn that way. Perhaps I could if I were motivated enough, I don't know. What I do know is that I can't be arsed to do something like that. It's frustrating. Annoying. I have better things to do with my time.

The common advice is Abelson & Sussman's «Structure and Interpretation of Computer Programs», which is pretty good, though it has some problems. Felleisen & al. wrote «How to Design Programs» to address some problems with SICP, and wound up introducing even more problems. The choice between those two boils down to whether you want a book that addresses you like a 13-year-old with a good grasp of the infinitesimal calculus (SICP) or one that addresses you like an 11-year-old that barely understands elementary algebra (HtDP). Personally, I rather like Paulson's «ML for the Working Programmer», which assumes you're an adult who probably doesn't hold a degree in mathematics. Unfortunately, it's not freely available like the other two.

If you decide to go with SICP, I recommend using either MIT Scheme or Racket with sicp mode (you can install the mode through the menus in the DrRacket IDE). For MLftWP, I'd recommend using PolyML (any Standard ML implementation compatible with the revised definition will work fine, but PolyML is actively maintained and easy to set up).

From there, you should be pretty versed in the foundations of FP, and it's just a matter of practice to get comfortable. Of course, there will always be more to learn—the student may never rest :)

ETA - My best advice would be this: if you have the money and the time, read both the Paulson book and SICP; if you have the money but not the time, read the Paulson book; if you don't have the money, read SICP, which you can find in many formats on the Web (it's CC BY-SA licensed). I would not recommend HtDP to a professional programmer, and only maybe to a neophyte.


very well said indeed! +1


OP, a README in that directory would be useful

I found some docs:

http://docs.thi.ng/umbrella/hdom/


I dunno, there's a 1000+ LOC readme there. Also updated the above link to point to it directly. It's a shame that GH is automatically displaying the readme on desktops, but not in mobile version...


Doh! I did miss it



I've never quite understood S-expressions. Can someone give me a good primer on them?


S-expressions are just the lowest common denominator of programming language syntax and more generally trees (aka lists of atoms and lists).

The idea is that all program listings are essentially just lists of things, including other lists: A Javascript source file can be thought of as a list of lines, or more semantically a list of statements and expressions. Function calls are just the name of a function and a list of arguments. Each different kind of statement or expression has its own kind of syntax: a `function(baz) { yadda(baz); }' expression uses different syntax than a `if (something) { yadda(foo); } else { blah(bar); }' statement. S-expression languages just use one kind of syntax for everything, so you can do `(lambda (baz) (yadda baz))' and `(if something (yadda foo) (blah bar))' instead.

This uniformity of syntax allows most of the interesting features of lisps, like that everything is an expression (returns a value), and macros, which are kind of a souped-up version of C preprocessor `#define's.


A great explanation. It's a pity that is buried in the thread.


There is (almost) nothing to get. They're just lists. The lists can contain anything you want, including other lists.


Wow - this is an impressive body of work.

Particularly like the Estuary and chart samples.

You mentioned you still use ClojureScript - but I'm guessing you're not using CLJS for front-end stuff anymore and just stick to your ES6 libs?


Merci beaucoup & please see my other comment about this...


Yes, saw you posted it after my comment.

You were one of the reasons why I got interested in Clojure / CLJS. Even visited a workshop of yours a couple of years ago ;) I'm still in dabbling mode unfortunately.

So my understanding after reading your tweets - is that for your use cases ES6/TS is better suited for front-end stuff. But Clojure is still worth digging into - if only for learning the philosophy, principles, ideas and simplicity of FP.

For what use cases would you use ClJ / CLJS nowadays?

Anyway, thanks again for your work - I like the hiccup style of building UIs - so will be checking out hdom.

Edit: typo


Oh hello & wow... now if only I knew who you are... :)

Those tweets really weren't meant to discourage people from using CLJ(S) at all!! I was merely trying to explain why, personally, I think TS is better suited for my own use cases (and those of my job)... I'm not building "standard" websites and for the tools I'm dealing with, CPU performance is more important than for most other web apps. I sometimes just felt constrained by some of the features & indirections people usually come to Clojure/Script for in the first place. For example, when generating or processing large amounts of geometry I really don't want or even need immutable data structures by default and so I wanted to have the freedom of being able to break out from this more easily. In some cases I too wanted to have more of a 1:1 correspondence between the code I write and what it ends up as in JS, something the combo of CLJS + GCC made hard(er). ES6 syntax offers many niceties (e.g. array and object destructuring, iterators, async fns) and without those I'd probably be still using CLJS more.


Ok thanks for clarifying - I didn't read your tweets as a discouragement and it makes sense the type of apps you're working on you probably need to squeeze out as much performance as you can and need something which gives you more control.

I was in your last Clojure for beginners course. I remember your dog was very friendly and liked to play with the half chewed tennis ball ;)


A bit off-topic, but have you moved away from Clojure entirely? I guess it sorta makes sense b/c creative coding in Clojure never quite took off (though there are some very nice individual libraries)

I really loved your geom library btw, thank you!



Thanks for the context :) Yeah, I understand it's a bit hard to work on a massive project of that scale with little feedback! It can be a bit draining.

I funny enough came at CLJ in part for the math functionality - but I might hit the awkwardness you mentioned. Mostly I'm quite interested in Neanderthal which runs native (MKL) and OpenCL. But living outside of the browser and JS/CLJS presents other limitations (like mixing geom with JavaFX for GUIs has some clunkyness - and the JavaFX wrapper libraries like fnfx are great but not polished)

It's great to see the no-org branch! I love org-mode and use it a lot (for templates and examples) but it always ends up a bit tricky for me to massage the code layout to fit the right narrative structure - so I get why you're moving away to encourage contribution :)

Thanks again for this awesome piece of tech. The API is just so fun to use once you learn how it works (it's so different and ergonomic from anything else I've used in C++/Cinder/etc.), and the end result looks very sexy. I'll keep using it and maybe add some PRs with examples once no-org merges in


Ps. Thanks for the kind words!!! :)


What makes this different from Mercury (https://github.com/Raynos/mercury) and a ton of other pre-React frameworks?


I don't want to sound snarky (and please don't see it as such), but did you read the readme? I'm not familiar with Mercury, but looking at the code examples, some obvious differences are:

- hdom does NOT care about state, where it comes from or how you manage it

- hdom uses vanilla arrays(*) instead of `h()` function wrappers, which not just create more legible (IMHO) style, but also open components up for more options in terms of construction/composition and also transformation.

I think that last aspect is seldom talked about in all those discussions and comparisons w/ React & co. Entire 3rd party frameworks are invented & maintained just to provide augmentation, configuration & injection features for React style components, issues which can be much easier solved if your components are in a less abstract & more native data format already. ES6 provides a ton of nice features to construct and transform arrays without any additional support structures. hdom also supports & fully embraces ES6 iterators and the more modern ways of transforming data they support.

There's a blog post about this forthcoming, so apols for not going into more detail here now...


Neither do most of the other libraries care about how you manage state. There are also other libraries that can take arrays/data as input, we ended up with inline functions mostly due to performance. There is no intrinsic advantage to plain arrays other than being serializable without an intermediate step, everything else is the same (jsx pragma and an array-based tree are fully interchangeable since you can easily generate one from the other).

Looking forward to that post as I’m always happy to be proven wrong!


FYI the "Mouse gesture analysis" example has performance issues after 300~ samples (e.g. keep drawing big circles continuously)

Win10/Chrome/i7 4790k/GTX 970


That's because that demo is using SVG for graphics and hence updating 300+ DOM elements every frame will of course start stuttering... Once I've updated that example to use the hdom-canvas support lib (https://github.com/thi-ng/umbrella/blob/master/packages/hdom...) this will be a non-issue. E.g. here's another demo which draws 10k rectangles @ 60fps. Only once I go >25K, I'm starting to loose framerate (MBP 2016):

https://demo.thi.ng/umbrella/hdom-canvas-shapes/#points-10k https://demo.thi.ng/umbrella/hdom-canvas-shapes/#points-50k


I found a similar issue with the git commit log example (when filtering from initial state and clearing filter) - I guess it can be reproduced via a "heavier" example e.g. a couple hundred nodes with more frequent state changing.


The commit table has a 100ms throttle on key press, but I think might have a bug, need to inquire more... :)

There's also a stress test here which updates several hundred nodes each frame:

https://github.com/thi-ng/umbrella/tree/master/packages/hdom...

E.g. each cell shown requires 3 DOM updates per frame, so in the 256 cells config it updates 768 DOM nodes. On my MBP2016 this config runs at ~35 fps, 192 cells (576 real DOM updates) at 59-60fps


Is the DOM an afterthought here, like in React, or is everything strongly tied to the DOM?


"Afterthought" is too a strong a term, but the largest aspect of that latest version of the library was to generalize the approach to aim at other non-DOM targets. I left another comment about this somewhere else in this thread. Basically, you can choose branch-local implementations of the main hdom interface. If none is provided, it uses the default one which targets the DOM...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: