ClojureScript 101
swannodette.github.io
swannodette.github.io
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!
$ 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.
$ lein cljsbuild auto async-tut1
Note the one instead of the ell.
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?(you'll have to include a (.preventDefault e) in the (listen) callback though)
> goog.dom.getElement
function (element) {
return goog.isString(element) ?
document.getElementById(element) : element;
}
> goog.dom.getElement("query")
<input id="query" type="text"> 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/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....
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.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).
[1] https://github.com/jduey/protocol-monads
[2] https://github.com/jduey/protocol-monads/blob/master/src/clj...
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...
> 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...
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...
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 102 should be how to get a REPL running ;)
It doesn't show up when googling for "leiningen clojurescript template." I wonder if maybe mentioning leiningen in the readme would change that.
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.
(go (while true) (. js/console log (<! clicks)))
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.