Settings

Theme

ClojureScript 1.7: ClojureScript can compile itself

swannodette.github.io

536 points by programnature 10 years ago · 120 comments

Reader

krat0sprakhar 10 years ago

> We grabbed a string out of CodeMirror, read it via tools.reader into persistent data structures, passed it into the ClojureScript analyzer, constructed an immutable AST, passed that AST to the compiler, and generated JavaScript source with inline source maps and eval'ed the result. All of this happened inside of your web browser.

This is just fantastic! As David himself mentions - this might not be extremely pragmatic but it sure as hell as cool. For me it means that I can get back to writing my web-based Clojure book without being dependent on Himera[0], which although great, is really hard to work with.

Awesome work by @swannodette and team!

[0] - https://himera.herokuapp.com/index.html

  • fogus 10 years ago

    > without being dependent on Himera

    It horrifies me that anyone would have been dependent on Himera to do real work. While I'm proud that it filled a gap for a few years and consider it a successful experiment, I'm happy to know that it's effectively unnecessary now.

  • eggy 10 years ago

    I don't pretend to know front end dev too much; I don't program for a living, but it seems the 'bazaar' model of layering, patching and piling things up is making that world unnecessarily complex. If I understand this thread correctly, you lose Google's Closure optimizations(?), you go round about AST->compiler->JavaScript and you can eval the result. Is the eval equivalent to Scheme or Common Lisp? What are its limitations? I like what I see of ClojureScript as a language syntax and some of the underlying concepts. I am playing with PicoLisp and PilOS (PicoLisp running on a VM - not really a Lisp Machine), and the simplicity and directness of it is refreshing. The layer of abstractions are minimal. I guess that's the same reason I like the J programming language. I am astounded by the amount of work and the accomplishment here, but I have to wonder if we can't see the forest for the trees any longer in computer science. Currently reading 'The Architecture of Symbolic Computers', studying Shen, PicoLisp, Racket and the J programming language. Perhaps my naivete of not having to work in this field, and the luxury of being able to dabble across languages and systems has shifted my perspective?

    EDIT: How do you debug your work? What traces do you get back? Can you still do REPL (like Emacs/Slime) this way?

    • ballpark 10 years ago

      One thing that's great about Clojure and Clojurescript is that they run on Java and Javascript platforms respectively, and are easy to interoperate with those platforms. For a business that wants to crank out some code, being able to use the libraries that others have created for those platforms makes the decision to use them for production systems much easier.

    • weavejester 10 years ago

      ClojureScript isn't particularly different to how most other languages work. You have a parser (analyzer) that turns text into an AST, then a compiler that turns the AST into the target code. In this case it's Javascript, but the same principle applies if the target is assembly or bytecode.

      ClojureScript is now self-hosting, which means that you can eval any valid ClojureScript, so yes, eval in ClojureScript is now equivalent to the eval in languages like Clojure, Racket or any other Lisp.

      Usually when a language is bootstrapped, that becomes the new norm. For instance, Go was originally mostly written in C, but since 1.5 Go has been written in Go. However, the Java version of the ClojureScript compiler can leverage Google's Closure Tools, which include a rather useful Javascript optimizer. This means that we'll still want to use the Java version of ClojureScript in most cases.

    • delluminatus 10 years ago

      ClojureScript is probably not the best language to use if you want few layers of abstraction. I personally love it, though.

      The best way to think about this development is that it is a client-side compiler for ClojureScript. In client-side development, you are constrained by the limitation that all code must be in JavaScript to be executed by the Web browser, which is why this compiler "compiles to" JavaScript instead of machine code. JavaScript is the assembly of the Web, right now (until things like WebAssembly get more useful and popular). Once the ClojureScript is compiled to JavaScript, the JS can be executed by the browser (which of course involves an additional JIT compilation step). This might sound "dirty" if you are used to thinking about non-Javascript development, because it involves two separate compilation steps, but it is unavoidable, once again until something like WebAssembly becomes popular.

      So, this is a client-side compiler for ClojureScript. A server-side compiler already exists, which is what you would use if you wanted Emacs/Slime support, or if you were developing a "real" Web application for deployment, because in that case you would want to pre-compile to JavaScript on the server, and distribute the compiled JS to the client.

      It doesn't really make sense to talk about Emacs/Slime running on the client, until someone makes Web app for it. You could use a socket-based REPL to communicate from the client to the server but I don't really see the benefit, when you could just use the server-side compilation model and avoid the network overhead.

  • escherize 10 years ago

    Is your book up anywhere? Would be awesome to see it in action. Even if not, can you plug your book a bit?

jeletonskelly 10 years ago

I swear David Nolan does not sleep.

  • swannodette 10 years ago

    I do in fact enjoy sleeping immensely. As alluded to in the post while I tend to do a lot of "driving" - this release was the result of a broader effort by the growing Clojure & ClojureScript communities.

    • DanielRibeiro 10 years ago

      And we are very thankful for all your contributions David.

      Thank you so much.

    • jeletonskelly 10 years ago

      Take it as a compliment man. I just think you do great work. Hopefully I'll see you at the bar again at the next conj.

  • mfikes 10 years ago

    All difficult problems in the Clojure community are resolved by sleeping, preferably in a comfortable hammock.

  • marpstar 10 years ago

    This is one of those things that people imply when they talk about "rockstar developers". Those who get so much stuff (of relatively significant achievement) done and have time to blog about it, and have time to speak publicly about it, and have time to further engage the community around them.

    ...and eat and sleep, of course.

    • marcofiset 10 years ago

      And most of the time also work at a day job! And take care of their kids! I'm simply amazed.

    • jacquesm 10 years ago

      You can do that for about 25 years or so and then you're going to slow down no matter what.

  • agumonkey 10 years ago

    Let's try not to judge people based on their hair cut.

robohamburger 10 years ago

The most interesting case for me will be fast starting clojurescripts as the author points out.

The one area I have not been happy with is clojure's startup times so hopefully this fixes that going forward.

tbatchelli 10 years ago

This also means that we can use clojurescript as a scripting language. Will make some systems automation much more fun!

  • michael_fine 10 years ago

    I'm only vaguely familiar with clojure, but why would you want to use clojurescript over clojure?

    • notduncansmith 10 years ago

      For what GP mentioned ("systems automation") depending on the JVM (as one would with Clojure) is kind of a non-starter, due to the long startup time. By contrast, Clojurescript (essentially the same language, but compiled to JS instead of JVM bytecode) can run in something like Node.js, where startup times are considerably faster.

      Edit: this new development (CLJS compiled with CLJS) is remarkable because previously, compiling CLJS required a running JVM. One could still compile automation scripts and run those, but that wouldn't really be true to the "scripting" style anymore.

      • mfikes 10 years ago

        You can dynamically compile and run ClojureScript scripts so long as you have properly hooked things up to a JavaScript engine. I've started on an effort that makes this easy OS X, wrapping JavaScriptCore and I could see similar efforts for Node.js. Here's what I've been working on: http://planck.fikesfarm.com It can run scripts you have on disk, etc, and it has basic file I/O capabilities at the moment so you can write simple text processing scripts, for example.

        • tbatchelli 10 years ago

          planck is a really exciting project. What IO capabilities do you think are missing at this point?

          • mfikes 10 years ago

            Just for DX / ergonomics, I'd like to mimic more of the file I/O stuff that you have in Clojure, especially the ability to cope with streams. And then perhaps having network I/O would be interesting as well.

            Then there is the `clojure.java.shell` namespace that could prove very useful for scripting.

      • mej10 10 years ago

        It actually isn't the JVM startup time that is the problem. The JVM starts up pretty quickly these days.

        Clojure itself is the bottleneck. Which means it is at least fixable... but seems to require a huge effort to fix.

    • KingMob 10 years ago

      Since none of your other responders said this, I will also point out the most common/obvious reason: you're building a website in Clojure, and want to continue using it for your front-end work, too.

    • JBiserkov 10 years ago

      "Because Clojure rocks, and JavaScript reaches"

      -- Rich Hickey

      (or so I've heard)

  • Turing_Machine 10 years ago

    Sure. There are a lot of scenarios where the 300k overhead hit isn't going to matter all that much.

    Not that 300K should be considered a huge amount by today's standards anyway.

    • reitzensteinm 10 years ago

      Well, that's 300k of gzipped code, so while the bandwidth isn't huge (300k is barely a large image these days), that's a ton of code to be parsed, and might be too much of a CPU hit for anything mobile.

      • mfikes 10 years ago

        It is actually pretty good for mobile and startup time isn't an issue. We have variants that run on iOS https://itunes.apple.com/us/app/replete/id1013465639?mt=8 and Android is in the works http://tahmid.me/posts/2015-07-15-bootstrapped-cljs-repl-for...

        • reitzensteinm 10 years ago

          Oh, my comment wasn't at all clear, but I was talking about inclusion on web pages, where you'd be flicking forward and back between them. In which case even 50ms on top of the rest of a page load would be hurting you.

          I'm sure it's fine for a SPA, and for a mobile app it's a total non issue. Actually I'm planning to use it for one with React Native myself.

          Luckily, people will probably not be including the full compiler lightly; I just wanted to point out that it's not as cheap as a jpg of the same size would be, an impression I thought the parent comment was giving.

          • mateuszf 10 years ago

            > Oh, my comment wasn't at all clear, but I was talking about inclusion on web pages, where you'd be flicking forward and back between them

            On the other hand - in case of single page webapps it would only be loaded once and could be made to load only once thanks to browser caching.

          • fnordsensei 10 years ago

            You'd probably not want to use this on websites, unless you want to be able to compile code directly in the browser, such as when you're making an interactive tutorial or similar. Otherwise, you're better off with stripping out all of those parts.

      • mfikes 10 years ago

        Also, check out http://clojurescript.net which is running bootstrapped ClojureScript. It starts up instantly on an iPhone.

      • Turing_Machine 10 years ago

        There are a lot of commonly used mobile libraries that are larger than this. Some of them (e.g., JQuery Mobile, which weighs in at several megs) make this look tiny by comparison.

dominotw 10 years ago

I have been looking at functional languages for frontend. But, I guess I am one of those people who just can't unsee the parentheses in lisp languages .

How does clojurescript compare to elm/purescript ?

  • elangoc 10 years ago

    In the last week, I've done this 2 or 3 times: I've opened some Clojure/CLJS file of mine in a text editor, did a search-and-replace of all open- and close-parens to a single space.

    Then I ask if they've seen Python before, to which most say 'yes', and I say, 'there you go, it's Python now'. And I can immediately see the look of surprise when they see that _similarity_, because the similarity is then hard to unsee!

    I recommend that trick to any others trying to open minds and challenge preconceived notions in their surroundings. :-)

    Side note: I've made the point that nested SQL expressions have many nested parens, too, which we've all done. And that the sum of parens+brackets+braces is similar to other languages, just the distribution is different. Neither of those 2 arguments work as well -- I guess because it's not visual and striking?

    • serve_yay 10 years ago

      Hmm, I feel like such a revelation would break down fairly quickly. What happens when you check equality somewhere, or add two numbers? It's not only parens everywhere (although that is quite the eyesore), other things like prefix notation also makes lispy code unpleasant to read.

      This approach reminds me of when people say, hey JS has classes and constructors just like Java does! And then proceed to write Java in JS.

      • loganmhb 10 years ago

        I think it's interesting how much people's aesthetic preferences differ on lisp syntax. Personally, I think the parentheses are visually appealing, but it's probably an acquired taste. Even more than that, I actually now find it much more difficult to parse infix notation for math, equality and so on (and not just operators in syntax-heavy languages like Haskell). Prefix notation, while requiring a different reading style, is incredibly clear and unambiguous as long as no one's gone macro-crazy on you.

  • lsdafjklsd 10 years ago

    I use Clojurescript and love it, and also really like Elm. I think the difference is that the Clojurescript community is really on the leading edge of web application development techniques. Elm does it all right, in a similar way, but more basic. In Elm you're also encouraged to develop UI's with no private state and a single Immutable app data structure, but that strategy only gets you so far. Om has the concept of cursors to deal with this, and Om Next will have the evolution of that in a GraphQL type solution which I think is the future.

    So basically I'd do any serious development with Clojurescript. Elm is a really cool thing with a small community that's fun to mess around with.

    • maxthegeek1 10 years ago

      I disagree strongly. I think we'll see more and more developers moving towards Elm's model for exactly the reason you've mentioned. It's simple.

      Not only that but having a single source of truth for your app's state, using only pure functions and non-stateful components to render your view allows you to do things like:

        - build a time traveling debugger
      
        - serialize your app's state and store it in your session so that users see exactly the same view when they log back in
      
        - trivially do server side rendering
      
      I have yet to be convinced that stateful react/virtual-dom components and/or GraphQL add any tangible benefits in the majority of cases.
      • lsdafjklsd 10 years ago

        Sorry if it wasn't apparent but I agree / advocate strongly for what you mention, and we use that model at my company. If you look at how Om evolved though, it started out with that simple model of passing the state object down to child components. Then cursors were implemented to make it easier to pass around parts of your global state without having components needing to know everything.

        GraphQL doesn't mean moving to stateful components, it's just a nicer way to have a singleton data store because instead of chunking and passing a map around, components can declare which data they need. In that system components are still read only, and dispatch to external pure functions.

        David Nolen does a great job talking about the trade offs and the evolution of everything in this talk https://www.youtube.com/watch?v=ByNs9TG30E8& I highly recommend it!

  • mateuszf 10 years ago

    You have to use it for some time, and then the parenthesis will just disappear.

    • mbillie1 10 years ago

      This is really true. I've recently moved to CLJS from Ruby/vanilla JS, and it took a couple weeks to be able to make sense of it. Now I don't notice it at all. It's just an exposure / getting-used-to-it thing.

    • mfikes 10 years ago

      Yep, and within a few months you will find yourself accidentally typing `(if` instead of `if (` in a C-based language.

    • lgas 10 years ago

      And then when you go back to a language not based on S-expressions you'll really miss them.

    • akilism 10 years ago

      plus an editor with a nice paredit mode.

      • gizmo385 10 years ago

        Having paredit makes me miss LISP when I'm not writing it. It just makes moving around and restructuring code so much easier than it is an a C-based language.

  • KingMob 10 years ago

    If you learned to unsee the angle brackets in HTML, rest assured, the same would happen in Lisps after a couple weeks.

  • erokar 10 years ago

    > How does clojurescript compare to elm/purescript ?

    Clojurescript is a Lisp with dynamic typing, Elm/Purescript are ML dialects where (static) typing is much more important, in Purescript more so than Elm.

    I think Elm is interesting because it takes a rather uncompromising FRP approach to web development. Clojurescript does not strive for the same degree of functional fundamentalism.

  • masklinn 10 years ago

    CLJS is a dynamically typed mostly-functional strict uncurried lisp, Purescript is a very Haskell-inspired languages, Elm is syntactically similar but less welded to Haskell, it's strict, it doesn't implement many features (where clauses, pattern guards) and concepts (e.g. typeclasses, HKT) and strongly focuses on FRP and frontend interactive programming instead.

  • spion 10 years ago

    PureScript is a very principled pure functional language with a design based on Haskell but adapted to be compatible with JS (it adds extensible records, which are a great fit for JS objects). The type system should really pay itself off in the long run in terms of significantly lowering the cost of refactoring pretty much anything [1]

    Additionally, I find that algebraic data types are by far the most natural and complete way to model your data and pattern matching is a powerful way to write succinct and readable code that manipulates that data. The fact that Lisps don't normally come with ADTs (incl. clojure) is a real shame.

    PureScript got its own Hoogle [2] recently. It lets you search for functions by stating their argument and result types. This has worked far better for me than any IntelliSense, and its miles ahead compared to rummaging through the docs of a dynamic language hoping to hit a jackpot.

    Although the community is small, the speed at which the language and its libraries are moving is quite amazing. For the cases where a package doesn't yet exist, its very easy to write FFI bindings to an existing JS library

    On the minus side, there is no "template purescript" yet, and generic deriving is WIP, so some things (like JSON serialization) are still a bit more tedious than absolutely necessary.

    The efficiency of the generated code may also be a concern. For example, currying is implemented in such a way that a function call with 3 arguments always generates both the result and 2 temporary JS closures. This can be very costly in certain situations. I find that library documentation is also sometimes lacking, however types and typeclasses do help with that somewhat (the latter only once you learn what each typeclass means).

    Finally, something to keep in mind if coming from JavaScript: unless you know Haskell the learning curve for PureScript is far steeper than ClojureScript. Its about as steep as Haskell's. There is a great book available to help with that [3], and almost all of the the knowledge gained there will easily transfer to Haskell. Since PureScript compiles to readable JS, looking at the compiled code made it much easier for me to understand the mechanics of certain features (Eff <-> thunks in particular was quite a revelation) which in turn helped me understand those features

    I haven't used ClojureScript much, but it does seem to have more mature, battle-tested persistent data structures (vs purescript-maps) and UI library (om vs purescript-halogen, although halogen seems very promising). There is also a type system (core.typed) which while not as principled as PureScript's, its still amazingly expressive - and since its gradual, you can start fully dynamic and then add types whenever you feel like doing that to parts of the codebase.

    [1]: https://www.reddit.com/r/haskell/comments/3e10ea/til_instant... - last paragraph also applies to PureScript

    [2]: http://pursuit.purescript.org/

    [3]: https://leanpub.com/purescript/

    • codygman 10 years ago

      > The fact that Lisps don't normally come with ADTs (incl. clojure) is a real shame.

      Was going to say Shen does, but then again I only see pattern matching. Anyway, you might want to check it out:

      http://www.shenlanguage.org/

      I do agree purescript is awesome though :)

      • emidln 10 years ago

        Shen is executable sequent calculus. It's data types are sequents, the conditions specified as arbitrary lisp (shen). Wikipedia seems to classify Shen as having algebraic data types, although it's type system isn't derived from ML or Cateogry Theory.

pacomerh 10 years ago

I've been wanting to get into ClojureScript (to clean my development practices, etc) but I'm wondering if I need to be a Clojure user. Does it make sense for a Javascript developer to just jump straight to ClojureScript?. I've seen PureScript also.

  • stijlist 10 years ago

    Short answer: yes, jump right in!

    Check out David's other blog posts, ClojureScript synonyms[1], and http://clojurescriptmadeeasy.com.

    http://himera.herokuapp.com/synonym.html

  • retrogradeorbit 10 years ago

    After learning Clojure and ClojureScript I got really excited and was ranting about it to my friend. He skipped Clojure and went straight to ClojureScript and has been having the time of his life.

    One thing that's simpler in ClojureScript is that it only has one type of shared mutable reference, the Atom. Atoms are uncoordinated and synchronous. But Clojure has more types, with different behaviour characteristics. So in a way ClojureScript is simpler and could be the introduction.

  • freshhawk 10 years ago

    As has been said, they are basically the same language.

    In terms of learning, it's probably easier to learn Clojure at the very beginning.

    It's simpler to get repl working and get a project building with Clojure right now. Not much simpler, but simpler. And at the very beginning, when the toolchain is magic to you, that matters.

    As early as you like you can switch to Clojurescript, learn the handful of small differences and go from there.

  • mfikes 10 years ago

    Clojure and ClojureScript are practically the same language. Both are "hosted", relying on Java and JavaScript for low-level things and providing clean interop for those things.

    So, I see no problem with learning either. Especially when you first jump in you are concerned more with the language "proper".

netcraft 10 years ago

Does this mean I could use compile and then require clojurescript modules in node? Today I use babel to "transpile" es2015 to es5 and then load those in, could I do something similar with clojurescript?

  • mfikes 10 years ago

    None of this recent work facilitates compiling ClojureScript into CommonJS modules for use with Node.

    But, the opposite is in flight: ClojureScript now has support for depending on libraries that are packaged using the CommonJS system.

    • klibertp 10 years ago

      > None of this recent work facilitates compiling ClojureScript into CommonJS modules for use with Node.

      http://www.matthewstump.com/misc/2012/06/04/writing-nodejs-m...

      Should be really very easy to do, you only need to study what cljs compiler emits and the docs for Google Closure Compiler JS libs.

      • raju 10 years ago

        Can someone this to me? It seems that mfikes (parent) comment, and klibertp's comment seem to contradict each other. Not to mention the examples that glenjamin highlighted.

        I have heard similar comments in other places, in that it's not possible to package ClojureScript as a NPM module.

        Is it because of the size of the module, or something else?

  • glenjamin 10 years ago

    This was already possible, see https://github.com/swannodette/mori or https://github.com/glenjamin/checkers for examples.

truncate 10 years ago

I'm curious if ClojureScript uses the original clojure library code (eg, drop, take, map...) or re-implements them? I tried to find something in codebase, seems like it uses original.

  • swannodette 10 years ago

    A non-trivial portion of Clojure (data structures, compiler) are written in Java. In ClojureScript these are written in Clojure or ClojureScript so there's really not as much to share as it would seem.

Turing_Machine 10 years ago

This is fantastic. It will be a lot easier for noobs to get their feet wet if they can use use the full language without having to fool around with installing a Java toolchain.

agumonkey 10 years ago

How far clojurescript is to be clojure/js. By that I mean how close the JVM and JS are semantically. There were discussions about that few months ago, because of differences deep down between platforms builtin types (js integers, etc). Swannodette also explained that cljs and clj don't share as much code as the team would like. How much clojure code would run correctly as-is on cljs ? Without having to use specific reader conditionals.

intellectable 10 years ago

Thanks, for all the fish ( by fish I mean awesome code ) @swannodette and team! Can't wait to use Om Next.

Also looking forward to seeing some code examples of Demand-Driven Architecture[0]. Does anyone know of any edifying clojure code examples?

[0] http://www.infoq.com/presentations/domain-driven-architectur...

nextos 10 years ago

I wonder if one of my favorite libraries for Clojure, Anglican [1] a probabilistic programming language, can be ported to ClojureScript without much effort. One could do amazing things with this.

Keep up the good work. Clojure & ClojureScript are awesome.

[1] http://www.robots.ox.ac.uk/~fwood/anglican/

dgreensp 10 years ago

How does the output of the ClojureScript-compiler-in-ClojureScript compare to the real thing? Do you lose the Google Closure optimizations, and if so, does that mean it isn't a good idea to compile your web app using the ClojureScript compiler running in Node (for example)? It's nice if you can compile ClojureScript on a machine without Java.

  • gw 10 years ago

    Correct; you do not get the benefit of Google Closure when using the bootstrapped compiler. The JVM-based compiler will continue to be the best way to compile ClojureScript projects for production.

retrogradeorbit 10 years ago

This is a huge milestone! I had a problem I was solving with Clojurescript six months ago and having access to the reader would have solved it perfectly, with lisp elegance. Instead I had to make a mess.

I think this opens huge computer science educational opportunities. Imagine a LOGO DSL inside your browser. With all the power of cljs. I am very excited!

hencq 10 years ago

Wow, this is quite amazing! Does this also mean that we will be able to write macros in Clojurescript?

Are there any plans to use mostly the same backend for Clojure and Clojurescript? It seems that since the languages are almost the same, it's only the code generation part that would be different?

  • mfikes 10 years ago

    Yes and no. You can write macros for which the source is ClojureScript, but they need to be kept separate. Here is a stab I took at explaining that subject: http://blog.fikesfarm.com/posts/2015-06-19-portable-macro-mu...

    There are no plans for the same backend AFAIK.

  • sbensu 10 years ago

    Using the same code for the backend is not realistic since Clojure leverages JVM libraries. A new ClojureScript backend ecosystem would need to start wrapping Node libraris to reach Clojure parity

    • maehwasu 10 years ago

      I did this late last year for a traffic analytics backend. As you said, I had to wrap a lot of Node.js libraries, and then wire them up with core.async.

      It was a fun project, and I learned a lot, but I ended up reimplementing the whole thing in Clojure+Java because performance and DB access was so much better in this use case. Having only a single event thread kills Node relative to the top JVM servers.

bmillare 10 years ago

I think an article outlining the details behind the non-trivial example, specifically on configuring how library names are resolved and how it can be modified dynamically, would be instructive.

DAddYE 10 years ago

Awesome!!! Now we want Clojure in Clojure :)

jimmcslim 10 years ago

When will the module support be released? Soon I hope! Thanks for the great work on all this.

  • swannodette 10 years ago

    Module support is already in 1.7.28 however it requires a corresponding release of the Google Closure Compiler.

edem 10 years ago

For some reason I feel like ClojureScript starts to transform into a new Clojure dialect (which runs on node.js and not on a JVM). Am I correct?

likeclockwork 10 years ago

Hm. This allows putting functions into edn that gets sent to the browser now, then doesn't it? Since ClojureScript can eval now?

  • mfikes 10 years ago

    If you put the function source into edn and the function doesn't depend on anything but core constructs, it would be nearly trivial to eval using the new cljs.js.

    If those functions need to call into other ClojureScript namespaces that you've already compiled into your code then the analysis cache would need to be populated so that eval can operate properly.

    For reference, here is cljs.js/eval: https://github.com/clojure/clojurescript/blob/v1.7/src/main/...

avodonosov 10 years ago

Finally, thanks god

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection