hyperhype / hyperscript

Create HyperText with JavaScript.
MIT License
2.63k stars 110 forks source link

nice work w/ hyperscript & observable #16

Open DTrejo opened 10 years ago

DTrejo commented 10 years ago

It's funny how if you used hyperscript and observables at the correct levels of abstraction, you could very easily build something like angular. And that doing it using these libraries would give you something server-side render-able, with no extra effort. Also, if you do it correctly, you don't re-render unnecessarily, and get the performance of something like react's dom-diffing, except without needing to do dom-diffing. Also, you will much better understand how your page interacts with other parts of the page (b/c the bindings are in code, in one place, not in the html and written into the angular library). ALSO, your validation functions get to be separate from your transformation functions (e.g. "".trim()), so you can reuse your validation on the server-side. ALSO ALSO you get designer-readable html depending on the level which you use hyperscript: if you are heavy on the selectors, then most of the html will already be in the page, rather than hidden in a template which needs the full server running into order to get the CSS to work against it.

dominictarr commented 10 years ago

favorite

dominictarr commented 10 years ago

retweet

jo commented 10 years ago

YES!

Raynos commented 10 years ago

@DTrejo you don't get the react dom diffing performance. The way hyperscript & observable work together will cause a large amount of unneccessary dom mutations and reflows. you would need a different approach to get react level performance.

How do you get designer readable html ?

Mind you :+1: hyperscript + observable is a great example of a simple approach that works well.

dejanr commented 10 years ago

I don't see benefits, any app example and architectural decisions?

dominictarr commented 10 years ago

Hmm, I don't really have a full example - but I do have interactive demos on both hyperscript and observable, which is made with: https://github.com/dominictarr/demonstrate which is itself an app built with hyperscript & observable. Personally, I don't actually write a lot of UI applications, but there other other people that use this.

This is a personal thing, but I like to program close to the, uh, "conceptual metal". Take a pure abstract idea like "2 way data binding" and express that idea as simply as possible. I don't want to learn about a framework, I want to learn about the what the framework is in an abstract sense.

nakosung commented 10 years ago

I've built full-stack for mobile games(it's not even an web-app, it is running on cocos2d-x jsb(spidermonkey) and node.js!) with my own version of hyperscript, and it turned out that its architectural decision was so amazing! (I also have a plan to share some part of the stack as open-sourced).

@dominictarr 's programming philosophy seems to keep everything as much as abstract and simple. The beauty of architectural design was born from it, but it is preventing most of people to understand how it can be useful and extended.

its_my_turn = o.compute [num_players,my_id,turn], (N,k,t) -> t % N == k
my_score = o(0)
gain_score = (amount) -> my_score my_score() + amount
mission_accomplished = o.compute [target_score,my_score], (t,c) -> c >= t
some_nice_ui { visible:its_my_turn }
  some_nice_text_for_score { text:my_score }
  some_nice_ui_for_mission_accompllshed { visible:mission_accomplished } 

above is rough pseudo-code for game-client code.

IMO, the only part which isn't elegant is

o.compute [A,B], (a,b) ->

I have tried to refine it in several ways, but I have no idea until now. DI of angular.js can be a candidate as a remedy for keeping DRY principle.

o.compute.publish_data_source A, "a"
o.compute.publish_data_source B, "b"
o.compute (a,b) ->  # here o.compute automatically decides what a, b should be resolved as.

but it just make things more complicated and redundant.

Anyway, observable have helped me and my colleagues to develop games(which have very complicated logic and wires) with much fun!

dominictarr commented 10 years ago

thanks!

Personally, I disapprove of the angular DI - I feel it crosses a line that it doesn't need to cross. it might be ugly to do compute(args, fn) but it's more honest.

If there was a way to inspect the free variables in a function (i.e, the variables that are in scope, but are not arguments) then you could do this just by doing d = compute((){ return a + b }) but that's not javascript.

I think in general, it's best to embrace the limitations of a language and not fight it.

since the compute functions you are using are basically just normal operators, maybe better would just be to implement a library with operators?:

var x = require('observable-operators')
its_my_turn = x.equal(x.modulus(turn, num_players), my_id)
my_score = o(0)
gain_score = (amount) -> my_score my_score() + amount
mission_accomplished = x.greaterThanOrEqual(target_score, my_score)
some_nice_ui { visible:its_my_turn }
  some_nice_text_for_score { text:my_score }
  some_nice_ui_for_mission_accompllshed { visible:mission_accomplished } 

that is more explicit, i think.

nakosung commented 10 years ago

Adding some operators could make sense for average app dev, but for games there are many unique expressions to compute with. These situations make library of operators huge and useless. For common operators developer should memorize new vocabularies just for o.compute, I think it isn't desirable.

I also discovered angular DI style approach is not the way to deal with, so I have been playing with vanilla compute.

dominictarr commented 10 years ago

hmm, yes probably - I just suggested that because it worked for the example code you gave.

guiprav commented 3 years ago

Sorry for the thread necromancy, hopefully it's fine since the issue is still open.

I may have built something along the lines of what OP was suggesting and I just found this thread out because I was migrating some of my Hyperscript code to Dominant (my library) but I forgot to convert one call to Dominant and Hyperscript just did "the right thing", namely call my function before setting the DOM property.

I had this: el('td', {class:'lt',innerHTML:function() {return l()[0]}}) (where el is Hyperscript). But I meant this: d.el('td', {class:'lt',innerHTML:function() {return l()[0]}}) (where d.el is Dominant).

But on first render the DOM was updated as I had expected (meaning the innerHTML function was invoked and the return value was passed to the DOM) and I was "huh, I didn't know Hyperscript did this". Then I looked into the code and found something about "observables", which brought me to this thread.

Dominant doesn't use Dominic's observable library, but it does use functions much in the same way.

There are also no unnecessary redraws and no vDOM. JSX is supported, but optional. Check the README if you're curious: https://github.com/guiprav/dominant (the entire source code is around 600 LoC FWIW :))