tazjin / elm-yesod

elm-yesod contains the Language.Elm.Yesod module for embedding Elm code in Yesod applications.
BSD 3-Clause "New" or "Revised" License
15 stars 8 forks source link

(1) 'elm-server' (w/o yesod) ok (but 'ZipCodes.elm' not ok in IE9)... (2) 'elm-yesod' ok in Chrome and Firefox (but route '/' not ok; and all routes render blank in IE9) #7

Open DonaldScott opened 11 years ago

DonaldScott commented 11 years ago

(0) Thank you Neil @ngunn for your helpful explanation of unzipping / modifying / zipping the package in the .cabal directory and then running cabal install elm-yesod again, here:

https://github.com/tazjin/elm-yesod/pull/4#issuecomment-14325616

This allowed elm-yesod to compile. I suspect part of the trick is to not do cabal update before re-doing cabal install elm-yesod, correct?

Sorry for yet another lengthy post, but I wanted to test both elm-server (involving 4 demos) and elm-yesod (involving 3 demos) in 3 browsers, so there was a bit of a minor "combinatorial explosion".

Summary

Full testing details below: section (1) describes testing elm-server (without yesod), and section (2) describes testing elm-yesod.

Next steps

(1) Testing elm-server by itself without yesod

I attempted to run elm-server by itself without yesod (on port 8000) following the instructions here:

https://github.com/evancz/Elm#using-the-elm-compiler-and-server

That worked fine.

Note: I am using nohup as follows to make sure the server doesn't "die" when I close my Putty SSH window:

nohup elm-server &

The / page displays a convenient directory listing (automatically generated by Elm? nice touch - reminds me of Tomcat :-), showing 4 clickable file links, which take you to the 'clock' demo, the 'mousePosition' demo, the 'ZipCodes' demo, and a slightly modified demo 'shapes3', shown below:

myBlue  = rgb    0  85 170
myGreen = rgba  28 267  85 0.5
myRed   = rgba  90   0   0 0.7

scene (x,y) (w,h) =
  collage w h
    [ rotate (toFloat (2*x+y) / 360) (filled myBlue (ngon 4 (100+x) (300,300)))
    , filled myGreen (ngon 5 30 (x,y))
    , rotate (toFloat (x+2*y) / 360) (filled myRed (ngon 4 (100-y) (200,200)))
    ]

main = lift2 scene Mouse.position Window.dimensions

IE9 error: ZipCodes demo doesn't work in IE9. The page displays in IE9, but when I enter a correct 5-digit zipcode, nothing happens, even after hitting Return. If I hit return a few times, the prompt text under the field disappears, and a small empty graphic box (the "waiting" icon?) appears in its place.

(2) Testing elm-yesod - overview of results

I continued with the steps here:

https://github.com/evancz/Elm/wiki/Elm-with-Yesod:-Getting-Started

I went through 4 testing phases, and on the 4th testing phase it finally worked (but route `/`` doesn't work in Chrome and Firefox, and none of the routes work in IE)

What got it working (mostly) was when I made a change in Main.hs so that it now references:

https://raw.github.com/evancz/Elm/master/elm/elm-runtime-0.7.2.js

Testing elm-yesod in Chrome and Firefox, about 20% of the time when attempting to GET a route involving graphics (eg, /mouse or /shapes or /clock), a blank page renders initially, and the browser must be refreshed in order to correctly render the page. (IE9 never works with elm-yesod: it always renders a blank page, with a Title.)

Testing phases (a), (b), (c) below for elm-yesod are no longer the current behavior. The current behavior for elm-yesod is described in testing phase (d) below.

(2)(a) First testing phase for elm-yesod

During this first testing phase the browser would GET the fairly short HTML source shown below, complaining about an ambiguous occurrence of 'link', and rendering a blank browser window (meaning the HTML was invalid?).

<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<title>Welcome!</title>
<style type="text/css">html,head,body { padding:0; margin:0; }body { font-family: helvetica, arial, sans-serif; }</style><script src="https://raw.github.com/evancz/Elm/master/elm/elm-runtime-0.3.5.js"></script><script> 
Elm.main=function(){
 return Elm.Graphics.text(Elm.Text.monospace("Error: Ambiguous occurrence of 'link' could refer to Text.link, Graphics.link<br>"));}; </script></head>
<body><div id="widthChecker" style="width:100%; height:1px; position:absolute; top:-1px;"></div><div id="content"></div><noscript>Error: Ambiguous occurrence of 'link' could refer to Text.link, Graphics.link</noscript><script> Dispatcher.initialize(); </script></body>
</html>

(2)(b) Second testing phase for elm-yesod

I then did the changes suggested here https://github.com/ngunn/Elm/commit/346ff6bb5d675ff27dc3a7b11c3a48c9c650b96d which made the browser GET the much more complete HTML source (but still somehow invalid?) shown below, clearly showing a lot of Javascript which had been generated by Elm. However, the rendered page was still blank (although the Title did appear correctly in the page's tab in the browser).

<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<title>Welcome!</title>
<style type="text/css">html,head,body { padding:0; margin:0; }body { font-family: helvetica, arial, sans-serif; }</style><script src="https://raw.github.com/evancz/Elm/master/elm/elm-runtime-0.3.5.js"></script><script> 
try{

if (Elm.Main) throw new Error("Module name collision, 'Main' is already defined."); 
Elm.Main=function(){
 var $op={};
 for(Elm['i'] in Elm){eval('var '+Elm['i']+'=Elm[Elm.i];');}
 try{
  if (!(Elm.Prelude instanceof Object)) throw new Error('module not found');
 } catch(e) {
  throw new Error("Module 'Prelude' is missing. Compile with --make flag or load missing module in a separate JavaScript file.");
 }
 var hiddenVars={};
 for (Elm['i'] in Elm.Prelude) {
  if (hiddenVars[Elm['i']]) continue;
  eval('var ' + Elm['i'] + ' = Elm.Prelude[Elm.i];');}
 function title_0(w_9){
  return size(w_9)(60)(text(header(toText('Elm-Yesod'))));}
 function heading_3(outer_10){
  return function(inner_11){
   return function(x){
    return color(mediumGrey_2)(size(outer_10)(61)(color(lightGrey_1)(size(outer_10)(60)(size(inner_11)(60)(x)))));}(title_0(inner_11));};}
 function skeleton_4(body_12){
  return function(outer_13){
   return function(){
    var inner_14=((compare(outer_13)(820)[0] === 'LT')?(outer_13-20):800);
    return flow(down)(['Cons',heading_3(outer_13)(inner_14),['Cons',body_12(outer_13)(inner_14),['Nil']]]);}();};}
 function info_6(w_15){
  return List.map(function(f_16){
   return f_16(['Tuple0']);})(List.intersperse(function(x_17){
   return plainText('&nbsp;');})(List.map(function(x){
   return function(e_18){
    return function(x_19){
     return e_18;};}(width(w_15)(x));})(['Cons',section_5(Value.str('Written in Elm, served with Yesod')),['Cons',text(Value.append(toText('This page is written in '),Value.append(Text.link(Value.str('http://elm-lang.org/'))(toText('Elm')),Value.append(toText(' and served using the '),Value.append(Text.link(Value.str('http://yesodweb.com/'))(toText('Yesod Web Framework')),toText('. Since you are looking at this page it is safe to assume that you already have the example code. ')))))),['Cons',text(toText('Type-safe URLs are rendered using simple QuasiQuoter variable interpolation.')),['Cons',section_5(Value.str('More examples:')),['Cons',text(Value.append(toText('- '),Text.link(Value.str('/mouse'))(toText('A simple mouse input example')))),['Cons',text(Value.append(toText('- '),Text.link(Value.str('/clock'))(toText('An animated analog clock')))),['Cons',text(Value.append(toText('- '),Text.link(Value.str('/shapes'))(toText('Some simple rendered shapes')))),['Nil']]]]]]]])));}
 function body_7(outer_20){
  return function(inner_21){
   return function(x){
    return width(outer_20)(flow(down)(function(x_22){
     return function(y_23){
      return ['Cons',x_22,y_23];};}(plainText('&nbsp;'))(x)));}(info_6(inner_21));};}
 var lightGrey_1=rgb(240)(241)(244);
 var mediumGrey_2=rgb(216)(221)(225);
 var section_5=function(x){
  return text(bold(Text.height((5/4))(toText(x))));};
 var main_8=lift(skeleton_4(body_7))(Window.width);
 return {$op : {},
 title:title_0,
 lightGrey:lightGrey_1,
 mediumGrey:mediumGrey_2,
 heading:heading_3,
 skeleton:skeleton_4,
 section:section_5,
 info:info_6,
 body:body_7,
 main:main_8};}();
Elm.main=function(){
 return Elm.Main.main;};
} catch (e) {
Elm.main=function() {
var msg = ('<br/><h2>Your browser may not be supported. Are you using a modern browser?</h2>' + '<br/><span style="color:grey">Runtime Error in Main module:<br/>' + e + '</span>');
document.body.innerHTML = Elm.Text.monospace(msg);throw e;};} </script></head>
<body><div id="widthChecker" style="width:100%; height:1px; position:absolute; top:-1px;"></div><div id="content"></div><noscript><p><h1>Elm-Yesod</h1></p><p>&nbsp;</p><p>Written in Elm, served with Yesod</p><p>This page is written in <a href="http://elm-lang.org/">Elm</a> and served using the <a href="http://yesodweb.com/">Yesod Web Framework</a>. Since you are looking at this page it is safe to assume that you already have the example code. </p><p>Type-safe URLs are rendered using simple QuasiQuoter variable interpolation.</p><p>More examples:</p><p>- <a href="/mouse">A simple mouse input example</a></p><p>- <a href="/clock">An animated analog clock</a></p><p>- <a href="/shapes">Some simple rendered shapes</a></p><p>&nbsp;</p></noscript><script> Dispatcher.initialize(); </script></body>
</html>

It was at this point that I actually did the steps in (1) above - testing elm-server by itself (without yesod), which worked. Then I proceeded to testing phase (c) below.

(Testing phase (c) below was the first time that the HTML source actually rendered something in the window. I wonder if this was somehow related to the fact that I did (1) launching elm-server (without yesod) just before doing testing phase (c) for elm-yesod? This would not seem to make sense as I do not believe that there is any interconnection between testingelm-server and testing elm-yesod in different directories, on different ports.)

(2)(c) Third testing phase for elm-yesod

In this third testing phase, the browser would GET the valid HTML source shown below, mentioning "Not Found", and this rendered in the browser window, along with the (supposedly) not-found filename. This happened whether or not the file actually existed on the server.

<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<title>Not Found</title>
</head>
<body><h1>Not Found</h1><p>/clock</p></body>
</html>

(2)(d) Fourth testing phase for elm-yesod (the current behavior)

I remembered reading the following:

https://github.com/evancz/Elm

An important note: When you install the elm compiler, it automatically downloads Elm's JavaScript runtime system to ~/.cabal/share/Elm-x.y.z/. The runtime system will follow the name scheme elm-runtime-x.y.z.js where x.y.z matches the version number of the compiler. If you want to serve this file from a different location, copy it from its home and always be sure that code compiled with version x.y.z of the compiler is served with version x.y.z of the runtime system.

So I grabbed the link to the latest elm-runtime-x.y.z.js here:

https://raw.github.com/evancz/Elm/master/elm/elm-runtime-0.7.2.js

and modified Main.hs in Elm/Examples/elm-yesod to reference this link.

Now in Chrome 26 and Firefox 19 all routes work except for / (which incorrectly complains that I do not have a modern browser), while IE9 merely renders a blank page with a Title.

The error happening in Chrome and Firefox using elm-yesod, renders the following for the route /:

Your browser may not be supported. Are you using a modern browser?
Runtime Error in Main module:
TypeError: Cannot call method 'appendChild' of null

And the HTML source says:

<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<title>Welcome!</title>
<style type="text/css">html,head,body { padding:0; margin:0; }body { font-family: helvetica, arial, sans-serif; }</style><script src="https://raw.github.com/evancz/Elm/master/elm/elm-runtime-0.7.2.js"></script><script> 
try{

if (Elm.Main) throw new Error("Module name collision, 'Main' is already defined."); 
Elm.Main=function(){
 var $op={};
 for(Elm['i'] in Elm){eval('var '+Elm['i']+'=Elm[Elm.i];');}
 try{
  if (!(Elm.Prelude instanceof Object)) throw new Error('module not found');
 } catch(e) {
  throw new Error("Module 'Prelude' is missing. Compile with --make flag or load missing module in a separate JavaScript file.");
 }
 var hiddenVars={};
 for (Elm['i'] in Elm.Prelude) {
  if (hiddenVars[Elm['i']]) continue;
  eval('var ' + Elm['i'] + ' = Elm.Prelude[Elm.i];');}
 function title_0(w_9){
  return size(w_9)(60)(text(header(toText('Elm-Yesod'))));}
 function heading_3(outer_10){
  return function(inner_11){
   return function(x){
    return color(mediumGrey_2)(size(outer_10)(61)(color(lightGrey_1)(size(outer_10)(60)(size(inner_11)(60)(x)))));}(title_0(inner_11));};}
 function skeleton_4(body_12){
  return function(outer_13){
   return function(){
    var inner_14=((compare(outer_13)(820)[0] === 'LT')?(outer_13-20):800);
    return flow(down)(['Cons',heading_3(outer_13)(inner_14),['Cons',body_12(outer_13)(inner_14),['Nil']]]);}();};}
 function info_6(w_15){
  return List.map(function(f_16){
   return f_16(['Tuple0']);})(List.intersperse(function(x_17){
   return plainText('&nbsp;');})(List.map(function(x){
   return function(e_18){
    return function(x_19){
     return e_18;};}(width(w_15)(x));})(['Cons',section_5(Value.str('Written in Elm, served with Yesod')),['Cons',text(Value.append(toText('This page is written in '),Value.append(Text.link(Value.str('http://elm-lang.org/'))(toText('Elm')),Value.append(toText(' and served using the '),Value.append(Text.link(Value.str('http://yesodweb.com/'))(toText('Yesod Web Framework')),toText('. Since you are looking at this page it is safe to assume that you already have the example code. ')))))),['Cons',text(toText('Type-safe URLs are rendered using simple QuasiQuoter variable interpolation.')),['Cons',section_5(Value.str('More examples:')),['Cons',text(Value.append(toText('- '),Text.link(Value.str('/mouse'))(toText('A simple mouse input example')))),['Cons',text(Value.append(toText('- '),Text.link(Value.str('/clock'))(toText('An animated analog clock')))),['Cons',text(Value.append(toText('- '),Text.link(Value.str('/shapes'))(toText('Some simple rendered shapes')))),['Nil']]]]]]]])));}
 function body_7(outer_20){
  return function(inner_21){
   return function(x){
    return width(outer_20)(flow(down)(function(x_22){
     return function(y_23){
      return ['Cons',x_22,y_23];};}(plainText('&nbsp;'))(x)));}(info_6(inner_21));};}
 var lightGrey_1=rgb(240)(241)(244);
 var mediumGrey_2=rgb(216)(221)(225);
 var section_5=function(x){
  return text(bold(Text.height((5/4))(toText(x))));};
 var main_8=lift(skeleton_4(body_7))(Window.width);
 return {$op : {},
 title:title_0,
 lightGrey:lightGrey_1,
 mediumGrey:mediumGrey_2,
 heading:heading_3,
 skeleton:skeleton_4,
 section:section_5,
 info:info_6,
 body:body_7,
 main:main_8};}();
Elm.main=function(){
 return Elm.Main.main;};
} catch (e) {
Elm.main=function() {
var msg = ('<br/><h2>Your browser may not be supported. Are you using a modern browser?</h2>' + '<br/><span style="color:grey">Runtime Error in Main module:<br/>' + e + '</span>');
document.body.innerHTML = Elm.Text.monospace(msg);throw e;};} </script></head>
<body><div id="widthChecker" style="width:100%; height:1px; position:absolute; top:-1px;"></div><div id="content"></div><noscript><p><h1>Elm-Yesod</h1></p><p>&nbsp;</p><p>Written in Elm, served with Yesod</p><p>This page is written in <a href="http://elm-lang.org/">Elm</a> and served using the <a href="http://yesodweb.com/">Yesod Web Framework</a>. Since you are looking at this page it is safe to assume that you already have the example code. </p><p>Type-safe URLs are rendered using simple QuasiQuoter variable interpolation.</p><p>More examples:</p><p>- <a href="/mouse">A simple mouse input example</a></p><p>- <a href="/clock">An animated analog clock</a></p><p>- <a href="/shapes">Some simple rendered shapes</a></p><p>&nbsp;</p></noscript><script> Dispatcher.initialize(); </script></body>
</html>

Recall (as mentioned in testing phase (2)(b) above) that the elm source for the / page (generating the HTML source above) uses the latest code modified by Neil @ngunn which can be inspected here: https://github.com/ngunn/Elm/commit/346ff6bb5d675ff27dc3a7b11c3a48c9c650b96d

So when using elm-yesod (runhaskell Main.hs), for the route /, we have the following incorrect behavior:

Error: Ambiguous occurrence of 'link' could refer to Text.link, Graphics.link

I have no idea why the behavior of the / route is incorrect and inconsistent in elm-yesod.

Conclusions

I am pleased to have Elm and elm-yesod running, and I really appreciate the help from @ngunn Neil as well as the Elm language design itself by Evan @evancz.

Elm represents a major breakthrough in how web programming is done, using concurrent FRP to permit a very high-level declarative programming style, and it is great to have it working both standalone (elm-server) and using elm-yesod (leveraging Yesod's high-performance Warp webserver).

Pending minor issues

The command elm-server always works (including in IE9), and there only two minor issues remaining when when using elm-yesod to run runhaskell Main.hs in Elm/Examples/elm-yesod:

Issue (i) - When using elm-yesod, why does the route /:

Issue (ii) - When using elm-yesod, why do the routes involving graphics occasionally fail to render on the first page load (about 20% of the time), requiring an addiitional page refresh in order to render?

Update

I now see that Neil @ngunn has posted a fix for Issue (i) above, here:

https://github.com/ngunn/elm-yesod/commit/f95507f3862b5d56885024f022bb21ce2e1039ee

As this involves repeating step (0) above, including re-running cabal install elm-yesod, I will try this fix later, possibly on a different server at AWS EC2, to avoid any chance of messing up the server that is already showing mostly correct behavior.

Thanks for any help or feedback!

DonaldScott commented 11 years ago

My understanding is, the Haskell compiler (and the Elm compiler?) may need a lot of RAM to compile the programs. But once compilation is completed, much less RAM is needed to run the programs.

Here is a snapshot of the first few lines of output from the top command:

top - 04:12:13 up 15 min,  1 user,  load average: 0.00, 0.01, 0.02
Tasks:  62 total,   3 running,  59 sleeping,   0 stopped,   0 zombie
Cpu(s):  1.7%us,  0.0%sy,  0.0%ni, 98.3%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   3869704k total,   447060k used,  3422644k free,    12488k buffers
Swap:        0k total,        0k used,        0k free,   236752k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  976 root      20   0  262m 220m  58m S  1.3  5.8   0:08.93 ghc
  975 root      20   0  6268 1076  848 R  0.7  0.0   0:00.94 runghc
 1166 ubuntu    20   0 30280  12m 9168 R  0.3  0.3   0:01.02 elm-server

I'm not sure exactly how to interpret the output of the top command, but it does appear that RAM usage and CPU % are quite low, which seems encouraging.

evancz commented 11 years ago

For the IE9 issue, it is because that browser disallows cross-site AJAX requests in all cases. To look up the zip codes, I call some API somewhere and IE9 does not like that. It should throw errors to that effect in the debugging console.