Bitten By Significant Whitespace

I’ve come to love significant whitespace since using it in CoffeeScript. (I’d dismissed it due to generally not getting on with Python, but really that’s for other reasons.) By eliminating the need for { }, code is more to the point.

However, significant whitespace is playing with fire and I just got burned.

The code helps to tailor sidemenu behaviour for a touch device. Anyway, the final false was wrongly indented. It should have been indented by 2 more characters to appear directly under the other 3 lines.

It must have been a quick edit or something, but the net effect was forms couldn’t be submitted when on a touch interface. I couldn’t quickly track it down, so made some workarounds to get things working, but then I realised it was happening on all forms, so looked into it more.

Lessons: * Be very careful changing any Coffee indents * Modernizr.touch would be a good starting point to search for the cause of any bugs like this.

Jeremy Ashkenas: CoffeeScript [Full Frontal 2011 Live Blog]

Live blogging at Full Frontal 2011, where Jeremy Ashkenas is first up, presenting on CoffeScript.

Motivation

Good work is being done inside and outside of the TC39 committee. The next version of JavaScript is an open process, a working mailing list you can get involved with, and there’s a chance to experiment and shape it.

“It’s just JavaScript”: CoffeeScript is not about creating a new language or letting us apply an existing language in a browser.

Bootstrapping

Original version of CoffeeScript was written in Ruby, but as v0.5, it became significant enough to bootstrap it: Ruby builds an initial CoffeeScript compiler, which then compiles the real CoffeeScript compiler. Slightly more complicated, but you get the idea: CoffeeScript is basically written in CoffeeScript.

Language Features

  • No “var”
  • No “function”
  • Significant whitespace
  • Comprehensions:

    1. for stooge in list
    2.     console.log 'hi ' + stooge

  • Block strings and interpolation (works for regex’s too):

    1. """
    2. An old pond
    3. a #{animal} jumps in
    4. the sound of water
    5. """

  • Object-oriented, and unlike normal prototype stuff, you get to call super.
  • Binding “this” to the actual object with =>

How it happens

Instead of waiting for a standards body or a browser to give you the language, you can roll your own today. CoffeeScript uses Jison for parsing, there’s also Pegs if you prefer. A nice tip is “it’s okay to cheat”! CoffeeScript uses a strict tokeniser to disambiguate, making the parsing much easier than if it allowed all sorts of ambiguities.

WebWorkers, Cross-Domain JSONP, and CoffeeScript

Today’s buzzword gotcha is brought to you by WebWorkers, Cross-Domain JSONP, and CoffeeScript. TL;DR: Always use “self” for WebWorker “globals”. And a library.

My Twitter app’s locking the main thread a bit, so thought I could quickly push polling functionality into a WebWorker. Not so fast …

Firstly, WebWorkers don’t do JSONP the normal way. You can’t inject a script because there’s no DOM. This means you can’t even use standalone JSONP libraries, let alone jQuery (which doesn’t even run in a WebWorker, as it assumes DOM presence, and would be a waste of bandwidth anyway).

Fortunately, we can use importScripts(). Conveniently enough, importScripts() can be used anytime and accepts any old string. So the API would actually be useful in the main thread too, but I digress …

So in Plain Old JavaScript, you would do:

javascript

  1. function go() { doSomething(); }
  2. importScripts("api.twitter.com?callback=go");

Works just fine. But this won’t work in the CoffeeScript equivalent, i.e.:

  1. go = -> doSomething()  ### NOT CALLED
  2. importScripts "api.twitter.com?callback=go"

In fact, Chrome gives a mysterious error “Uncaught undefined”, while Firefox cuts to the chase: “go is not defined”.

The reason is that the CoffeeScript compiler wraps the entire thing in a closure. This is tremendously useful default behaviour, as you don’t have to worry about adding new globals, but can cause problems in situations like this. If the script comes back from Twitter and calls “go()”, where does that actually end up? With the main thread, a CoffeeScript can explicitly bust properties out of the closure using the global namespace, i.e. “window.foo” (or “global.foo” in the case of a server-side Node script). In fact, there’s a solution to this with WebWorkers too. have their own global identity, “self”.

  1. self.go = -> doSomething()  ### FTFY
  2. importScripts "api.twitter.com?callback=go"

Works.

Be sure to use “self” for other WebWorker “globals” too, i.e.:

  1. self.onmessage = (ev) -> doSomething()

Here’s a tiny jsonp library to support this:

So you can say:

Comparing CoffeeScript and Kaffeine

I’ve recently been Node web programming and as a long-time proponent of DRY, I’m more than happy about the Alt-JS movement that has taken the webdev community by storm over the past six months, along with alt-HTML and alt-CSS.

We used CoffeeScript and Less for HN Reader. Faviconist is built on Jade for HTML, Stylus for CSS, and Kaffeine for JavaScript. I’m now doing a project in CoffeeScript for comparison, and because I found Kaffeine wasn’t as much of a finger-typing saver as I’d hoped for. Also, CoffeeScript has a huge ecosystem at this point. There are definitely great things about both, and indeed they are more similar to each other than either is to JavaScript, and they both make JavaScript a lot more DRY and pleasant to deal with. But let’s look at the differences:

Kaffeine pros:

  • Kaffeine has the major benefit of maintaining line numbers. With CoffeeScript, any error messages require a little thinking in order to track down the bug. Sometimes I even have to manually compile the CoffeeScript to track down the errant code (some of the various ways I’m using CoffeeScript only ever serve the JS, or keep it in memory, rather than ever saving it).
  • Retains ternaries, which, being symbolic, are more to my liking than the “if then else” CoffeeScript idioms. (I like symbols for code structures because what words remain are those that matter, i.e. those related to business and application concepts.)
  • Has a number of nice “coding in the small” idioms which aren’t present in CoffeeScript, especially the pipe and async. The latter lets you create a traditional “sleep 1000” on its own line, giving the illusion of synchrony.
  • Doesn’t impose Python-like significant whitespace. Actually, I’ve come to like this about CoffeeScript (see below), but there’s certainly a learning curve until you get how to handle the various situations, which doesn’t need to happen with Kaffeine.
  • Truly JS++. The Kaffeine compiler will compile a regular JavaScript file just fine. With CoffeeScript, although the language aspires to be “it’s just JavaScript”, standard features like “function” keyword are simply not part of the syntax.

CoffeeScript pros:

  • There’s one extremely compelling reason to choose CoffeeScript right now, above all other dialects: The ecosystem. There’s exponentially more docs (the core docs themselves are great), more existing code, and more tools in CoffeeScript, than any other dialect. (Just as the ecosystem of JavaScript is in turn exponentially bigger than that of CoffeeScript.) Don’t get me wrong. It’s great that other dialects like Kaffeine are out there pushing the barrier, and even the less JavaScripty tools like ClojureScript and Dart which treat JavaScript more like a virtual machine. And because they all compile down to JavaScript, you can use them today. But if you want to ship code fast, CoffeeScript is the sweet spot right now: The benefits you’d want in a “JavaScript++”, with best support and minimal effort to integrate it into projects. There are already several CoffeeScript books in the making, for example. StackOverflow registers 600+ questions about CoffeeScript, while other dialects are in the single or low double digits. (And 130K questions about JavaScript, to prove my point about CoffeeScript still being a minnow in the big JS sea!) There are also an increasing array of tools where CoffeeScript support comes out of the box, e.g. connect-assets.
  • For a related reason, CoffeeScript may be the most future-proof dialect out there. Though we can expect JS.Next to learn from all the dialects, it’s clear Brendan Eich is paying close attention to CS:

  • The syntax is best described as relaxing! (With apololgies to CouchDB.) I initially balked at the significant whitespace, but now I appreciate its ability to wipe out curly braces and parentheses. It’s just nice to define hash literals without commas and braces, for example.
  • Operators like “isnt” and “unless” make the code much more literate. Why add extra baggage? Because to a computer “5==count” is the same as “count==5”, but to a human, there’s always a context which makes one more natural. See Yoda Conditions. These kinds of expressions help avoid double negatives too.
  • Class construct, which like everything, “de-sugars” to regular JavaScript.
  • There’s a reverse compiler. Nice if you want to fork an existing JS library or code example.

I’m still not done yet. While these are “just” dialects of JavaScript, each has a sufficiently large feature set that it takes a while to get the hang of everything, when you’re using them in real-world projects. So I’m still jumping back and forth between my code and the doco to make sure I’m getting full benefit of both.

A few caveats about JavaScript dialects:

  • As I’ve already pointed out, JavaScript is still vastly more standard than any dialect. While Dart may come to Chrome in the future, and various features will morph into JS.Next, you’re always going to be doing more tool integration and working with less docs. If releasing open source, you’ll want to distribute the JS or at least make it easy for someone to generate it. What’s good though is you don’t have to learn something completely different, so it doesn’t become a maintenance issue. You could easily hire a JS developer to maintain a CS app, they’ll be up and running in a few.
  • All create extra overhead in terms of tooling. Whether you’re dealing with a browser or Node, both of those deal in the currency of JavaScript, and some conversion must take place.
  • There are many situations where you still have to use plain old JavaScript. For example, working with legacy code or other people’s JavaScript. Writing inline script tags or dynamic code which will be eval’d in the browser (unless you also want users to download the CS compiler). You’ll need to get used to switching between the two, and that’s painful because once you start working with them, trust me, you WILL NOT want to go back. (This has been true for Jade vs HTML and Stylus vs CSS as well.) So I’m often having to consciously ask myself what I’m coding in right now.

Keeping “require” modules DRY in Node

Update: See the follow-up on StackOverflow. I agree with those who rallied against use of global, but no need to throw the baby out with the bathwater; as shown in that thread, we can instead use “eval” to keep it DRY.

You see a lot of NodeJS code like this:

javascript

  1. connect = require 'connect'
  2. express = require 'express'
  3. redis = require 'redis'
  4. sys = require 'sys'
  5. coffee = require 'coffee-script'
  6. fs = require 'fs'

BTW this is CoffeeScript, not pure JavaScript, but the same logic applies.

The point is, Node’s module system gives you a layer of abstraction you probably don’t need. 9 times out of 10, people just assign a module to a variable by the same name. You would, wouldn’t you?!! You don’t have to think about alternatives, and you can easily cut and paste other people’s code. And it’s now just an industry convention.

So why not practice convention over configuration, and keep things DRY with the following idiom:

javascript

  1. "underscore,connect,express,redis,sys,coffee-script,fs"
  2.   .split(',').forEach (lib) -> global[lib] = require lib

Thanks to the “global” object, which is like the “window” object in the browser, we can easily require new libraries without unnecessary clutter.

And – as with any decent convention-over-configuration setup – we can still override the convention if we desire:

javascript

  1. _ = underscore

Is this what the App of 2015 looks like? HTML5 + CoffeeScript + Less + WebStore + PhoneGap + Apparatio

My weekend project was a Hacker News Reader (source code). It works on Chrome, Safari, and Opera. Sorry, Firefox doesn’t work at present, reasons why speculated below. Mostly started this project because I want one with Twitter integration, but also to play with a few technologies, which I did. Among things explored and demonstrated:

  • Server-Less App The app is entirely browser-side. The server serves only as a repository of static files. Content comes from the iHackerNews unofficial API, by way of JSONP, the protocol we’ll still use for cross-domain calls until CORS becomes eminent in the hopefully not-too-distant future.
  • CoffeeScript. The app logic is written in CoffeeScript, and compiled to JavaScript in the browser with coffee-script.js.
  • Less. The UI styling is written in Less and compiled to CSS with less.js.
  • Chrome Web App. The app is also a hosted web app on the Chrome Web Store, which is to say (a) it can be discovered by users of the store; (b) it can be installed into the New Tab page of Chrome, for convenience sake; (c) if I wanted to, and added some extra server smarts, I could charge for it. I wrapped it up as an app with Paul’s super-handy Appmator utility.
  • Mobile HTML5. Although I didn’t (yet) optimise the site for HTML5, I did make it available on mobile, with the right icons for being added to the Home Page, and used Build.PhoneGap.com and Apparatio to roll it up as a native mobile app.

CoffeeScript

I wanted to try out CoffeeScript. While I’ve been skeptical about languages which are said to “compile down” to JavaScript, as if JavaScript was a complicated tangle of 1s and 0s rather than the high-level language it is…CoffeeScript is different. It’s more like JavaScript++ as the name implies. While I do find JavaScript to be pretty good, its syntax can get too verbose. Especially for closures, where you have too many curly braces and “function” keywords getting in the way of the actual logic we care about. You can also be more expressive with keywords like “unless”.

As ~98% of code people write never sees high-scale production use, I like to opt for quick and dynamic over tedious and “it just feels like the right way to do it”. So I employed coffee-script.js to compile dynamically, in the browser when the web app loads, instead of pre-compiling it on the command-line. Doing it dynamically means I can just type some code and reload the browser page, about the smallest possible debug-test cycle that is possible.

I found it slow-going at first, because I ran into some coffee-script.js gotchas and learned a few things about the language on the way:

  • Keep CoffeeScript in a separate server. Although CoffeeScript supports inline CoffeeScripts in your HTML (<script type=”text/coffeescript”>…here is my script….</script>) – perfect for quick hacking), I recommend you stick them in a separate file (<script type=”text/coffeescript” src=”myscript.coffee”></script>. I can’t explain exactly why, but when it was inline, things were note very deterministic and I found my CoffeeScript script was getting executed three times!
  • Use a server, not file:/// URIs. As with less.js, CoffeeScript mocks the browser’s script-downloading behaviour, using XMLHttpRequest to pull down any scripts specified in “text/coffeescript” script tags. Unforuntately, Chrome doesn’t let you XHR one file from another file, so you need to set up a server, if only to serve these static files over HTTP.
  • Don’t bother with window.onload (or jQuery intializer). I *think* coffee-script.js guarantees any CoffeeScripts you provide will be executed after the window has already loaded. I haven’t confirmed this, but seems to be that way on Chrome, Safari, and Opera. OTOH, the app doesn’t work on Firefox – CoffeeScript isn’t executed at all – and this may be related.
  • Declare before you use. A consequence of the previous rule is that your initialisation sequence will run in the order you specify it. This is different to the usual JS situation where you make a window.onload handler, which might call things that you’ve declared further down in the file. So in the code further down, note that I declare “show” before I call it. Pretty obvious, but JS habits and lack of descriptive error messages meant it caught me out.
  • Whitespace matters. There are no semi-colons and no braces here; similar to Python, whitespace dictates structure. However, you can also use parantheses to keep things on the same line, so I didn’t find this threatening to concise code.
  • Use “window” for global. Declarations like “x=5” out in the open will *not* make global variables; and nor is there a “var” keyword. The only way to hook into global space is to explicitly reference the “window” object; e.g. “window.x = 5”.

How’s the code look? See for yourself, CoffeeScript keeps it pretty tight. I couldn’t find many examples of jQuery-CoffeeScript combos, so this will hopefully be useful in that regard:

javascript

  1. storiesTemplate = _.template($("#storiesTemplate").html())
  2. show = () ->
  3.   $.getJSON "http://api.ihackernews.com/page?format=jsonp&callback=?",
  4.     (storyInfo) -> $("#stories").fadeOut () ->
  5.       $(this).html(storiesTemplate(storyInfo))
  6.       $(this).fadeIn()
  7.  
  8. show()
  9. setInterval show, 10*60*1000
  10.  
  11. $(".story").live "click", (ev) ->
  12.   return if $(ev.target).closest("a").length
  13.   window.open($(".url", this).attr("href"), "hackernews"+Math.random());

  • Less fails silently. Unfotunately, you don’t get error messages, so develop things gradually and be prepared to undo.

Less

If I was going to take a sip of CoffeeScript, it would only be fair to munch on a slice of Less too. If CoffeeScript is JavaScript++, then Less is CSS+. It’s really augmented CSS, because you can present a regular CSS stylesheet and it will be valid Less too. That’s not true at all about CoffeeScript; a typical JavaScript script has Buckley’s of passing the CoffeeScript compiler.

The other thing they have in common is JavaScript compilers. Which is not the case with SASS for instance.

Less talk:

  • Same XHR issues. As with CoffeeScript, the Chrome cross-domain restriction means you have to use a server even though the files are static. Also, it doesn’t seem to be possible to include Less stylesheets inline, or to get hold of the less compiler, which I think is a shame if true.
  • Silent fail. If any Less doesn’t parse, the entire stylesheet won’t be applied, and there’s no error message.

So some downsides, but the upside was a cleaner stylesheet with less redundancy. Gradient macro makes the ridiculously verbose gradient declarations completely DRY:

  1. /*****************************************************************************
  2.    MACROS
  3. *****************************************************************************/
  4. .linearGradient(@color1:#ccc, @color2:#ddd) {
  5.   background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0, @color1), color-stop(100%, @color2));
  6.   background-image:-moz-linear-gradient(center bottom, @color1 0, @color2 100%);
  7.   background-image:-o-linear-gradient(center bottom, @color1 0, @color2 100%);
  8.   background-image:linear-gradient(center bottom, @color1 0, @color2 100%);
  9.   background-color: @color1;
  10. }
  11. .postedBy:hover, .commentsLink:hover span { .linearGradient(#fa0, #f80); }

Elements can be nested in their parent selectors:

  1. .commentsLink,.commentsLink:visited {
  2.   position: absolute; top: 50%; right: 1em; height: 1em;
  3.   ...
  4.  
  5.   span {
  6.     ...
  7.     border-radius: 0.3em;
  8.   }
  9.  
  10. }

… and so can pseudo-selectors:

  1. .story {
  2.   padding: 0.5em 9em 0.5em @leftIndent;
  3.   border: 1px solid black;  border-bottom-width: 0;
  4.   background: #fff6f6;  cursor: pointer;
  5.   position: relative;
  6.  
  7.   &:hover { .linearGradient(#fc0, #fa0); }
  8.  
  9.   &:last-child { border-bottom-width: 1px; }
  10.    
  11. }

And you can do cross-browser CSS calculations today:

  1. .commentsLink,.commentsLink:visited {
  2.    ...
  3.   margin-top: -0.5em-@commentsLinkPadding;
  4.  
  5.   span {
  6.     ...
  7.     padding: 0em + @commentsLinkPadding;
  8.   }
  9.  
  10. }

Chrome Web Store

This is a full-screen, focused, app which therefore meets the criteria for Web Apps I outlined in Thinking In Web Apps. (It would be more so if it showed the actual stories and comments in the app too, but that’s something for later.)

So it made sense to submit to the Chrome Web Store. Fortunately, my colleague Paul Kinlan had the gall to attempt building a web app that packages up Chrome apps … and then he actually pulled it off! Appmator asks you for the address of your web app, asks a few other basic questions, and then makes a Zip file you can drag to your desktop and upload to the store. All in all, a 30 minute submission to the Chrome Web Store and a nice way for people to discover your web app. I would only recommend this for true “Apps” as opposed to general websites, and ideally those apps should be tailored to a full-screen experience.

Anyway, it’s on the store now – Hacker News landing page.

PhoneGap, PhoneGap Build, Apparatio

The app is NOT optimised for mobile at this time, but the cool thing about HTML5 is, for the most part, it “just worked” when I pointed iPad and Android phones to it. Some things need improvement, but this is good to see.

For exploration purposes, I decided to build it as a native mobile app anyway. I was lucky enough to receive beta invites for PhoneGap Build and Apparatio, two web services which let you do PhoneGap without actually installing anything on your PC. I built this app with both products.

Apparatio, which I used previously, worked nicely and I was able to get a native Android app.

PhoneGap Build supports GitHub like Apparatio, but also allows uploading a zip file. I actually had a problem pointing it at my GitHub repo (as with Apparatio, it needs to provide clearer instructions on exactly what form of URL is required). After I uploaded the zip file, it showed me a dashboard of 4 packages being built, and just went ahead and built each of them. Nice and simple!

Android, WebOS, Symbian, Nokia. Conspicuous by its absence is iOS, which Apparatio does support.

DropBox on my Macbook and Galaxy made it easy to copy the APK downloads to my Galaxy and install it. The Apparatio one took some time to download initially, but worked fine. The

Other Notes

Block-level “a” element, NOT. HTML5 makes it possible for “a” elements to be block-level, i.e. to include other tags including other “a” elements. This would be ideal for the Hacker News Reader UI, where the entire story block is a link to the story itself, but the block also contains links to comments and authors. It turned out to be legal in modern browsers, but with blatantly wrong layout, as someone else found earlier. Shame as I had to introduce considerably more JavaScriptCoffeeScript and CSSLess.

InnerHTML, Templating, and Live(). Seems like every project I do these days, I’m building dynamic content with innerHTML, as opposed to DOM manipulation. The first reason is I’ve become accustomed and satisfied with templating, and see it as a growing trend thanks to language-agnostic formats like Mustache as well as server-side JavaScript. (Though in this case, it’s a pure browser-side app.) So make a template and stick it on the page with innerHTML (or jQuery’s .html() ). The second reason is jQuery’s live() command makes it easy to handle events against the newly formed elements, which was previously a pain with innerHTML().

Setting text size. As you can see, text size varies with the number of points a story has. The relevant line appears in the template:

font-size:<%= Math.log(Math.log(Math.max(s.points,10))) %>em;

Using a log scale ensures a story with 2000 points isn’t 2000 times bigger than a story with 2 points. To dampen the effect further, which I think is important in mobile displays, I used a double-log and applied a minimum threshold of 10 points.

Future Things

There was actually an itch to be scratched here, beyond trying out various tech. Specifically, I’ve been wanting a mobile HN reader that would: (a) tweet; (b) quickly mark for instapaper reading, and ideally when I’m offline. (It would show the latest stories it saw when online, and cue them up for later…in general, I wish more offline apps would support cueing things like Tweets and Read Laters for when you’re back online.)

Fin.

So is this the Web App of 2015 or not?

There are definitely elements here that are not in wide use now, but will be in wide use by 2015. The whole “write once, run many” aspect of HTML5 is just getting wheels. It’s now clear that Apple won’t dominate the landscape by 2015, so regardless of their ever-fluctuating stance, HTML5 will be able to power the apps of the majority of smartphones and tablets…not to mention the possibility of apps on TV, cars, watches, washing machines, etc etc etc. (Can you tell it’s been CES week?)

And with HTML5 being able to power these apps, we’ll need ways to fit them into the App ecosystem, and that’s where PhoneGap and friends fit in. As I’ve said in various places, I always found it painful to have to set up Eclipse, PhoneGap SDK, and so on just to make a native app from HTML5. These are the things HTML5 developers don’t want to do, and the PhoneGap team is well aware of it, hence their PhoneGap Build effort. Apparatio provides relief in the same way and we can expect the same from others, especially so in light of Heroku’s high-valuation acquisition bringing attention to the whole idea of cloud programming. GitHub is also a vital component of this vision becoming reality. The next step will be to automate deployment into the stores, hopefully something that app-hungry stores will be happy to facilitate.

The Chrome Web Store is a related concept aiming to “app-ize” we apps. Mozilla is investigating a similar idea, and we’ll see a lot more of it in the next few years too. Some people say “but it’s just a bookmark”, an understandable reaction, but I think people are starting to get it now. It should only be an app if it really is an app, ie. if the underlying URL is really a media-rich, focused, app rather than a big collection of information and hyperlinks. And the Web Store is not just a place where you can buy and install apps; it’s also a directory where you can find all the cool apps on the web, with screenshots and reviews and comments to help you decide if they’re worth trying out.

Offline is important in the app story too (although this app doesn’t (yet) do any offline). You don’t type in URLs when you’re on a plane, but you do click on app icons to launch them. So offline web apps need an app-like presence to meet user expectations.

How about those languages, CoffeeScript and Less? CoffeeScript seems promising and I’ll definitely be using it more. At the same time, I’m weary because I feel like JavaScript may be “good enough” and CoffeeScript becomes a maintenance problem if others need to see your code. But the leap is not a big one.

As for Less, I’m more convinced it’s the way to go. We nowadays have an absurd proliferation of “-webkit-” “-moz-” “-o-” “-msie-” prefixes and the various syntaxes to go with them. It’s for good reason, but it’s also as much an affront to the DRY principle as any agilist could ever fear, and it’s unfortunate the standards don’t provide better support for dealing with this sort of thing. Likewise, the need for variables/constants and calculations. Less allows for all of that, and in a way that will work on legacy browsers even when some of these features do come about. So, yes, I’ll be committing to Less in the future.

What’s very cool about both Less and CoffeeScript is the JavaScript frameworks. It’s ideal for starting off a project, and when performance gets to be an issue, you can easily switch to the compiled version after that.

Finally, I’ll mention that templating is a design pattern that’s also going to be big in 2015. There’s much more framework support these days and it’s easy to use the same templates on the browser and the server.