joelburget / react-haskell

React bindings for Haskell
MIT License
351 stars 26 forks source link

Tighter React.JS integration, composable classes, modular animations. #20

Open johncant opened 9 years ago

johncant commented 9 years ago

I've done some refactoring and got rudimentary composable classes working, without losing any functionality. I've also factored out animations into their own modules. This should make native class interop a lot easier. I ended up changing quite a lot of things. Happy to discuss and change a few things if necessary.

joelburget commented 9 years ago

This is really cool. Will play around with this and get back with more detailed feedback later.

johncant commented 9 years ago

Thanks for taking a look! * awaits feedback *.

joelburget commented 9 years ago

Hey John, I did some work on merging this after the recent switch to GHCJS. I've made some good progress but it's not working yet. Think you could take a look?

johncant commented 9 years ago

Maybe later this week. Why have you switched to GHCJS? I tried GHCJS once and found that it produced megabytes of runtime and ran really slowly (of course I haven't benchmarked it properly). Will it still work on Haste?

jeremyjh commented 9 years ago

@johncant I had this question too. I think ghcjs runtime is more like 100kb now once its been minified - but yes it is a lot heavier than Haste. What worries me is not the size but the complexity of the runtime implementation. Haste has 400 LOC in the runtime. I'm not sure ghcjs runtime could be maintained by anyone other than luite.

@joelburget would you accept a PR that kept it runnable on Haste or are you done with Haste?

joelburget commented 9 years ago

Good question. Sorry for making that switch out of nowhere. I decided to switch because as far as I can tell GHCJS seems to have more of a future than Haste. I think this for two reasons.

First, Haste is very fragile. A lot of packages couldn't build. For example, the current install directions include running haste-inst install --flags="-integer-gmp" hashable, then actually installing with haste-inst install (see issue #14). I could never get a lot of packages to build (affine, lens, etc). I also kept breaking my environment and having to reboot (likely my fault).

Second, and more importantly, GHCJS seems to have a more vibrant community. As far as I can tell it has more users and there seems to be a decent amount of activity. I recently sent a couple announcements to the Haste mailing list, to almost no response (thanks Jeremy :) ) (and there haven't been messages about other topics), which was a big red flag to me. I worry that it would be up to me to improve things (like making the build more robust). My main project is an application, so I really don't want to lose focus to such low level stuff (I found myself never getting to work on the things I really care about). Really what I'm looking for is a very stable platform to build on.

I'd love to hear your thoughts @johncant and @jeremyjh.

@jeremyjh I think it would be possible to run on either GHCJS or Haste by using different backends.

johncant commented 9 years ago

I found both GHCJS and Haste a nightmare to set up (they both took me 2 whole days), so I used Vagrant, but for me that solves the installation difficulties in Haste. Haste's main failing as a language/compiler is that it doesn't support TemplateHaskell which breaks lots of packages. Are you sure you messaged the right mailing list? The last commit in haste-compiler was 5 days ago, and I'm pretty sure that compiling any Haskell into JavaScript is a pretty niche thing to want to do.

I'm all for using both and the biggest problem I can see is that using TemplateHaskell would break all the code for Haste.

Do you think GHCJS will end up being as small or as fast as Haste in the near future?

joelburget commented 9 years ago

+100 on being a pain to set up :( In fact I'm planning to upload the vagrant box I'm currently using - more or less the box I wish I could have just downloaded a few weeks ago - so anyone can get started easily with GHCJS.

I don't think template haskell is at all an issue for this package. It's not currently used and I don't have plans to use it (well, except for the todomvc example, but that could be changed).

Do you think GHCJS will end up being as small or as fast as Haste in the near future?

I really have no way of knowing :( Do you have speed benchmarks?

johncant commented 9 years ago

In the interests of getting this pull request merged and react-haskell working on Haste and GHCJS, Here's my Vagrant setup for Haste and this branch: https://gist.github.com/johncant/6a337759304b0e473659 . It should work without any modifications. Unfortunately, haste-compiler master breaks this PR at the moment.

I'm glad you don't need template haskell much. I don't have any benchmarks but it would be really easy to build them using react-haskell if it compiled on both GHCJS and Haste!

johncant commented 9 years ago

I'm going to need to spend more time on this understanding GHCJS's compiled output. Haste's compiled output took ages to get my head round, and GHCJS looks like it has even less documentation and is more complicated. Not that I expect documentation. How do you know what symbols like h$cN and h$ap1_e mean? Can you give me any pointers?

I have to say that I noticed the page lagging with GHCJS, but I can already tell that it's far superior from the development side.

johncant commented 9 years ago

Tried reductio ad absurdum: https://gist.github.com/johncant/ce2fdf37713396dba069

There's an issue in GHCJS about this: ghcjs/ghcjs#256

luite argues that this case (i.e exactly what we're trying to do in this PR) isn't necessarily a good idea, because a React JS render could trigger untold amounts of computation to take place. The solutions to this problem are to buffer the computations beforehand in another thread or evaluate certain things strictly beforehand. He's implemented his own virtual DOM with this in mind - https://github.com/ghcjs/ghcjs-vdom

When I call a Haskell callback from JS, I can't get a result back. h$runSync just crashes and normal JS execution just returns null. However, passing arguments to the callback works. Therefore (and luite mentions this) we can pass a callback that modifies its args or closure. This works however, but I don't like it, and it's clearly going to break when someone waits until the next JS tick to call the callback back:

https://gist.github.com/johncant/1a7b3533a32417adb201