Hacker News new | past | comments | ask | show | jobs | submit login
ClojureScript 101 (swannodette.github.io)
178 points by swannodette on Nov 9, 2013 | hide | past | favorite | 42 comments



If there's something in the tutorial that trips anyone up or doesn't make sense please let me know and I will amend.

I'm pretty excited about the latest release of ClojureScript that makes all this possible. With respect to speed of incremental compiles, code size, performance of generated code, and debugging we're finally in a good place (with of course many improvements planned in the near future).

None of this would have been possible without the tireless work of 62 contributors over the past two years, http://github.com/clojure/clojurescript/. A big thank you to all!

Happy hacking!


Couldn't really get started (with the latest clean install of leiningen from homebrew).

  $ lein cljsbuild auto async-tutl
  ...
  java.lang.Exception: Unknown build identifier: async-tutl
Did the name of the tutorial change?

edit: ell instead of 1. I guess that's why I use a heavily-serifed font for my coding. I even re-read the command several times looking for a simple typo to catch exactly this problem.


You have a typo there. The proper command is:

$ lein cljsbuild auto async-tut1

Note the one instead of the ell.


The project is async-tut1 not async-tutl. Look at your project.clj.


Being totally new to Sublime I was actually got confused on configuring paredit and lispindent with Sublime 2.

    get sublime2 http://www.sublimetext.com/2
    package control https://sublime.wbond.net/installation#st2


	launch "sublime 2"
	ctrl-`
	paste in the package control script (from above link) <ret>
	re-launch "sublime 2"

	⌘-shift-p # dialog comes up
	install ↓ <ret>
	# up will pop a dialog after reading the package database
	paredit <ret>
	⌘-shift-p
	install ↓ <ret>
	lispindent <ret>
The extra level of detail would cloudy the main flow of the tutorial so maybe have some supporting material links for the slower kids?


This is great. We currently hold a weekly clojure class at work, and this will be very helpful to some of the people who attend. The more content the better!


Suggestion: add a form around the input and button, and bind on its submit. That makes the application work when pressing [return] from the input field

(you'll have to include a (.preventDefault e) in the (listen) callback though)


The (.log js/console (dom/getElement "query")) command returns "null" instead of a DOM element. Curiously, manually running the equivalent command in the Chrome console works as expected:

  > goog.dom.getElement
    function (element) {
      return goog.isString(element) ?
        document.getElementById(element) : element;
    }
  > goog.dom.getElement("query")
    <input id=​"query" type=​"text">​


If someone could provide a good pointer to better understand lisp syntax / Clojure I would appreciate it. Clojure is so confusing and hard to follow for the beginner!


The biggest difference is that functions go after the left-paren, not before. Commas are also unnecessary, they're just whitespace

    foo(bar, baz) => (foo bar baz)

    herp(bar, derp(baz)) => (herp bar (derp baz))
This might help as well: http://adambard.com/blog/clojure-in-15-minutes/


Perhaps note that scripts should be loaded after form elements. You get an odd error if (init) runs before the form elements exist.


I love this example. Working through this made me finally get started on reading Hoare's CSP book. Thanks!


Love CLJS!.


Is there a maintained web page or cheat sheet somewhere that lists the functions and macros unique to ClojureScript?

The API Overview[1] page for Clojure itself is great, and many of those facilities are 1-to-1 in cljs. For individual libs, like core.async, it's easy enough to go look in the cljs namespaces (i.e. the source code) to see what's available.

But it seems there are at least several important facilities unique to ClojureScript and it would be great if they were spelled out somewhere readily accessible and linked to from the main README[2]. Digging through the source is a bit more challenging given that there's more to wade through than for individual libs.

[1] http://clojure.github.io/clojure/

[2] https://github.com/clojure/clojurescript/blob/master/README....


For example: I'm aware of 'js*', but where is it defined? Where is it documented? Does it have any "gotchas" that are explained somewhere?


js* should not be used - it's an implementation detail. I agree we could do a better job documenting what's unique to ClojureScript over Clojure.


Since this is supposed to be a 101 tutorial, could you please add (maybe in a later episode) some info on debugging and on parsing the stack trace.

When I add the text

    1+"
to core.clsj and have auto compile running, I get a gigantic stack trace but none of the entries points me to the line where the error occured. I wonder how people are supposed to handle a code base bigger than a few lines of code and I stop following the tutorial before having written a single line of clojurescript.


Clojure stack traces in general are pretty verbose. A few thoughts from a fellow user (not ClojureScript specific, but remember we're doing all of this in a Clojure project):

The top few lines of the stack trace have the most information. Entering some blank space or clearing your terminal will make it easier to find the top.

The errors are often caught way down in Java land so you can be left trying to map Java errors and types into your own code - I've learned to ignore 80%+ of the trace for this reason.

There's a bit of a learning curve to figure out what this can't be cast to that might mean, both for Java types and Clojure types. For example a missing opening paren may give a (roughly) "number can't be cast to sequence" error. A missing closing paren will generally throw an End Of File (EOF) error.

A lot of the time you can get a line number from the top entry in the stack trace, they'll be in parentheses and aren't labeled terribly well - but once you find it once you'll know where it is forever.

Sometimes, in certain situations - especially with macros that call across files (like for testing) - the stack trace might be completely unhelpful.

I'm definitely hopeful that this situation will improve in the future (and maybe we can get an Edwin style debugger? Please?) In the meantime we can use tools to help us at least keep our braces organized since that's half the difficult to trace problems right there. For Emacs some swear by paredit, I'm still working on my system but it includes electric-parens, rainbow-parens (so helpful!) and auto-closing parens.

Also, it's not as robust as Emacs but Lighttable offers some decent parentheses handling out of the box (I went back to Emacs after Lighttable was making connections to outside servers that I at least didn't mean to ask it to and didn't have time to deal with. Other than that Lighttable was great).


ClojureScript generally gives pretty good errors and location information, however there are probably cases still where that's not true. This is likely an issues with tools.reader? Thanks for the report, I'll ask around about getting this looked into.


Recreated in pure js with ES6 generators — http://andreypopp.com/posts/2013-11-09-recreating-core-async...


Fantastic! :)


Thank you for such a straightforward train I thought/workflow post. Would love to see something detailing how to deal with state in clojurescript. Use atoms? Pass the world in and return it modified? Dispatch within go funcs using symbols at the head of lists?


Jim Duey's protocol-monads[1] library is compatible with ClojureScript, and the State monad[2] can be a great for mechanism for "passing around the world". It's not always the best choice, but it's a good option to have in your toolbelt:

[1] https://github.com/jduey/protocol-monads

[2] https://github.com/jduey/protocol-monads/blob/master/src/clj...


In what circumstances is it the best choice? I get the impression that monad libraries in dynamic languages are basically toys, or are written to show off. If I'm wrong, I'd love to see examples of how you used them in a ClojureScript project, why (other than novelty value) you chose them over other approaches like atoms, and how they worked out in practice.


That's a fair question, to be sure.

However, your comments regarding monads see a bit off. I've seen the same sentiments expressed elsewhere (i.e. by others), so I think they represent something of a common misunderstanding.

First of all, monads are simply programming patterns which relate a set of values to a set of of functions. For any monad X, we can say that a function is monadic if it returns a value in the X Monad. The set of values are precisely those such that the following three Monad Laws[1] hold:

[ Using JavaScript syntax, they can be approximated as... ]

Left Identity

  mBind( mReturn(x), f ) == f(x);
  // => true
  
Right Identity

  mBind( mReturn(x), mReturn ) == mReturn(x);
  // => true
  
Associativity

  mBind( mBind( mReturn(x), f ), g ) == mBind( mReturn(x), function(x) { return mBind( f(x), g ); } );
  // => true
Above, x is any value in the Monad X; f and g are monadic functions for the same monad. The definitions of mBind and mReturn will vary depending on the monad, but the same laws (and sometimes additional properties) hold for each group of things taken as a whole – the monadic values, monadic functions, and the mBind and mReturn pair for those values and functions.

As you can see, there is nothing special that requires a statically typed language. That being said, if your language is statically typed, and even more so if its type system has advanced capabilities (e.g. Haskell's type system), then the relationships between the monadic values and functions can be leveraged to do a number of useful things, including catching a host of errors at compile time (though that's true regardless of monads).

If your language is dynamically typed, then you won't get those extra benefits, but you can still take advantage of the fact that monads can abstract away a great deal of plumbing between various pieces of your program.

Sometimes the "monad patterns" will appear in a language under another name, or will be used "under the hood" to implement a particular API. Clojure/Script's `let` for example is essentially the Identity Monad; and its `for` is very much akin to the List Monad (the same is true for list comprehensions in other languages). The core.async library involves an inspiring application of the State Monad pattern[2].

In my case, I'm working on an SCXML-like[3] framework which builds on Google Polymer, core.async and protocol-monads (and core.logic at some point?). By using the State Monad pattern (together with trampolines and core.async channels), the algorithms of SCXML can be implemented in a manner that's not dependent on side effects, instead flowing the "global state" between the computational steps in a manner that is consisent and relatively easy to reason about. To boot, by using channels to link together steps in the "mainEventLoop", time is deliberately yielded back to the CPU, making the framework even more concurrency friendly. The project will be open source soon, and I can follow up with some links to source code once it goes public on GitHub.

[1] http://en.wikipedia.org/wiki/Monad_(functional_programming)#...

[2] https://github.com/clojure/core.async/blob/master/src/main/c...

[&] https://github.com/clojure/core.async/blob/master/src/main/c...

[3] http://www.w3.org/TR/scxml/


You've piqued my interest around the state monad specifically, and I just added a long but promising-looking article on it[1] to my reading queue. However:

> Sometimes the "monad patterns" will appear in a language under another name, or will be used "under the hood" to implement a particular API. Clojure/Script's `let` for example is essentially the Identity Monad; and its `for` is very much akin to the List Monad (the same is true for list comprehensions in other languages).

I knew this from reading monad tutorials. And yes, certainly there exist useful things that happen to be monads. But I question the usefulness of explicitly pointing that out and saying, "Here's a monad library. I'm going to use this monad library to build X. You can use all the normal monadic functions on X, because it's a monad", as opposed to putting the monad stuff to the side and just building X. What would you gain from building `let` or list comprehensions atop a monad library? Would it be worth the users you'd confuse? The concept of monad is far too abstract to be intuitive to most people, so there is a cognitive cost to making people think about them.

1. http://brandon.si/code/the-state-monad-a-tutorial-for-the-co...


Jim Duey's blog post on the State Monad was helpful to me[1].

I agree that one shouldn't reach for monads just because one finds them interesting or neat, or challenging for that matter.

An analogy: consider that in many cases, `while` and `for` loops are great when looping is what's called for. But there are many times when recursion is more attractive still. But what to do if your language doesn't have TCO, and blowing the callstack is a real concern? Well, then you can use trampolines![2]

Now, trampolines are great, but you don't need to always reach for them. An analysis should be done of the problem at hand, and "the right tool chosen for the task", which may be a simple `while` loop. The same goes for monads. Are there cases where the State Monad makes the most sense among other programming patterns? Yes, I think so. Particularly, when you are trying to model a multi-step and nested-steps computation where the steps are intertwined in such a way that one can easily identify "global state" at work. In that case, the State Monad is a tool that will let you thread the global state through the steps and levels of computation without having to twist yourself into knots doing it some other way AND without relying on unfettered mutability.

[1] http://www.clojure.net/2012/02/10/State/

[2] http://raganwald.com/2013/03/28/trampolines-in-javascript.ht...

[&] http://clojure.github.io/clojure/clojure.core-api.html#cloju...


A nice suggestion, will think about it.


It looks great, thanks! Here's another one I just saw: https://github.com/magomimmo/modern-cljs/blob/master/doc/tut...

I got started with CLJS about a year ago but ran into performance issues with the specific application I was building, hopefully things have improved now.


ClojureScript has had performance escape hatches for some time. It's pretty much always possible to get the performance of hand written JS, important otherwise our data structures would be lame. We'll likely document these techniques in the future.


Great write up.

ClojureScript 102 should be how to get a REPL running ;)


Could you please do one that explain how to actually debug in CLJS.


Using `lein cljsbuild auto` should enable you to see problems in your clojurescript code as soon as you save it, and using the browser's js console enables you to see problems in the compiled/generated js. The stack trace printed out in case of an exception is usually informative enough. I experienced its helpfulness quite recently during my study of OP's example.


Debugging is a more straight forward now because of source maps, but it still needs a little bit of explaining - will think about it.


Nice. I spent like an hour today looking for a leiningen clojurescript template, so I'm happy to see Mies.

It doesn't show up when googling for "leiningen clojurescript template." I wonder if maybe mentioning leiningen in the readme would change that.


Hey swannodette, just curious about why you ended the <input> tag in your tutorial. Was this deliberate?


Hmm, I seem to have found a limitation/bug in go blocks. I didn't know about the nifty "." syntax for calling methods on js objects, eg: (. js/console (log "hi")).

Anyway, I thought this was great, so I tried doing it inside of a go block, like so: (go (while true) (. js/console (log (<! clicks))))

And this yielded a compiler warning: WARNING: Use of undeclared Var async-tut1.core/log at line 50 src/async_tut1/core.cljs

So there seems to be a problem with namespace resolution and the go macro? Or maybe a bug with "." in particular? Works fine when I do (.log js/console (<! clicks))

Either way, I would try to be consistent about your notation throughout the article. Either use (.log js/console ...) or use (. js/console (log ...)).

Core.async in the browser is a ton of fun otherwise! Kudos.


I think this is a typo in the article. If you remove the parenthesis right before log, this should work. You're using the dot form to chain log onto js/console. When you write "(log" instead of just "log", it tries to call log as its own function, not as a method of js/console. This is why it tells you it's never seen that variable before.

(go (while true) (. js/console log (<! clicks)))


(Even if this is a known limitation and not a bug, I would love to understand why one form works and the other does not.)


A bug in core.async, I've already opened a ticket for this. Thanks for the feedback!


Perfect intro to core.async in a web app - just enough of an example (jsonp within a click handler would require a nested callback).

It would be nice to update the tutorial with a final instruction on compiling with advanced mode so people can see that the end result is a reasonable sized js file.


Incorporating a browser REPL into the template would be fantastic.




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

Search: