Hacker News new | past | comments | ask | show | jobs | submit login
Lumo – A fast, standalone ClojureScript REPL that runs on Node.js and V8 (anmonteiro.com)
218 points by rcarmo on Nov 10, 2016 | hide | past | favorite | 75 comments



Sometimes I think Clojure should have been developed on top of the ErlangVM instead of the JVM. I think that the ErlangVM solves a lot better the problems that Clojure is trying to solve. Also, seems like targeting the ErlangVM is an achievable feat, since Elixir in its version 1.3 has proven to be extremely solid.

Crazy idea: if I wanted to implement Clojure on top of the ErlangVM, where should I start?


I think shared memory is foundational to Clojure's approach to concurrency, and that is something the Erlang VM cannot provide. So some main fixtures of Clojure's standard library, like atoms, STM, agents, become either useless or nonsensical.

It's also not clear to me wether the Erlang VM provides what Clojure needs to efficiently implement its user-defined types, protocols, etc

That said, a Clojure-like dialect of lisp built on Erlang is a fine idea and, as others have said, I would probably start by seeing how the Lisp-Flavoured-Erlang (LFE) guys built their lisp.


Mnesia is sorta like STM in Erlang.


Actually no, it is completely different internally with different properties.


How about targeting Go instead?


Like via transpilation or what? LLVM would be a more natural choice if looking to compile to native.


Yes but it doesnt have a concurrent garbage collector.


One of the main objectives of Clojure was interoperability with Java because of the ecosystem. Java and Erlang ecosystems are not comparable.

Also, JVM is great. :)


That's not true. One of the main objectives of Clojure was to be a hosted language. Not just 'language hosted on JVM'. So idea to make it work on top of Erlang VM is perfectly valid and consistent with original design assumptions.


Whether it's as important now or not is a different matter I suppose, with various targets for the language (including other VMs), but I don't think it's in dispute that originally the extensive java ecosystem was core to what clojure was, and was designed to be.

...but hey, don't take my word for it:

    The net result is that the prospects for Clojure going forward are
    very good. The core model of Clojure has held up well and continues to
    appeal - accessible, robust, thread-safe, efficient dynamic functional
    programming, on a world-class infrastructure, with a huge set of
    libraries. Oh yeah, and it's great fun! 

    - Rich Hickey, 2008
    https://groups.google.com/forum/#!topic/clojure/2CA_H58Cbo0


That might have referred to the CLR and the JVM though since Clojure has targeted both runtimes from the start (although the JVM implementation is leading of course.)

This talk should go into a bit more detail about this: https://channel9.msdn.com/blogs/charles/emerging-langs-cloju...


Except then it might not have had as much uptake (tooling, existing Java libraries, installability to various providers and interoperability with existing systems etc.).

Now that Clojure has a varied and excellent set of libraries of its own maybe some of this is less important, but not all (the ability to interact with existing Java systems is still very useful).


> One of the main objectives of Clojure was to be a hosted language. Not just 'language hosted on JVM'.

Uhm... I always thought it was meant to be a Lisp for JVM. Where did you get your information from?


The erlang ecosystem has new-found life with Elixir. The ecosystem for Elixir is rock-solid. Very high quality libraries abound.


Absolutely. The level of robustness is outstanding for such a new language / ecosystem.


To be fair, and I find this to be a bit of a shame, but most of that robustness and ecosystem comes straight from Erlang, which was always solid. Though I do enjoy the consistency that Elixir brings and the improved macros. I'm a Clojure guy, so I also love the protocols.

Just wanted to say that Erlang vanilla is robust and has a great ecosystem. So Elixir builds itself on top of a solid foundation.


Take a look at LFE [0]

[0] http://lfe.io


If I remember correctly, LFE is a Lisp2

Joxa (just like Clojure) is a Lisp1, thus maybe more apt http://joxa.org


I'm curious, what are the practical differences? I know what the theoretical differences are, but do you have any experience with how lisp1 and lisp2 are different in daily use?


I'm afraid I don't have a satisfactory answer for you.

I just prefer to pass around functions/closures as values, and being able to invoke them like any normal function, instead of having to call an helper `apply`/`invoke`/`call` function on them (depending on the language)

That's why I said: "maybe more apt"... if OP doesn't care at all about that difference, and it's not among the details that they'd want to replicate from Clojure, it's perfectly ok to ignore


I never found the funcall argument terribly convincing, because there is no reason it couldn't be much more concise. And it allows you to avoid silly variable names like lst or fst.

In Common lisp to refer to a function as a variable you do (function f) or as (read-table) syntactic sugar #'f. You could easily use destructuring to write HOFs without funcall.

So instead of requiring people to write

  (defun bad-map (f list)
    "Maps function `f' over `list', inefficiently."
    (when list (cons (funcall #'f (first list) (bad-map f (rest list)))))
  (bad-map #'- '(1 2 3))
A hypothetical lisp-2 could just as well allow destructuring like this:

   (defun bad-map (#'f list)
     "Maps function `f' over `list', inefficiently."
     (when list (cons (f (first list) (bad-map #'f (rest list)))))
   (bad-map #'- '(1 2 3))
The fact that higher order functions stick out a bit by the extra #' is not necessarily a bad thing IMO; it helps when reading unfamiliar code to know immediately what arguments are functions and which ones are just plain objects.


In Common Lisp in your first example it's not

   (funcall #'f (first list) ...)
but

   (funcall f (first list)) ...
f is already a function object.

We could write a macro for that or a new version of defun. Here just a macro lisp1fy:

    (defmacro lisp2fy ((&rest calls) &body body)
      `(flet (,@(loop for (f . args) in calls
                  collect `(,f ,args (funcall ,f ,@args))))
         (declare (inline ,@(loop for (f . nil) in calls collect f)))
         ,@body))


    (defun bad-map (f list)
      "Maps function `f' over `list', inefficiently."
      (lisp2fy ((f a))
        (when list
          (cons (f (first list))
                (bad-map f (rest list))))))


Yeah, braino -- thanks. A custom binding form would be another way to do it, but I'd prefer the pseudo-destructuring -- cleaner and more concise. I'd hope a new lisp-2 dialect would come with better destructuring support for defun and binding forms anyway (destructuring-bind, multiple-value-bind and flet could all be subsumed by let if (values ...) (function ...) where valid left hand patterns).


I just felt that as you can many functions with the same name but with different arities (number of args) at the top-level why shouldn't I be allowed to have it deeper down as well. Also this means that I can only sometimes refer to a function with just its name, when it is at the top-level you need need both name and arity.

This means that Lisp-1 doesn't really fit anyway so why let it limit you?


I would start with lisp flavored erlang, and add clojure's immutable yet fast-copy data structures. They are based on work by Phil Bagwell,

https://en.wikipedia.org/wiki/Hash_array_mapped_trie http://infoscience.epfl.ch/record/64398/files/idealhashtrees...

You might do better to create a Lisp Flavored Haskell (if one hasn't been started already). I'm sure haskeller's would be offended by the idea, but it already uses HAMT.


Why do you think Haskellers would be offended by this?


Because they hate parens so much they spent a $ to eliminate them.


Read up on Lisp Favored Erlang, there's a lot of reasons why Clojure doesn't work well on BEAM, and they're covered by the author of LFE.


Crazy idea: if I wanted to implement Clojure on top of the ErlangVM, where should I start?

I would look at retargeting the ClojureScript code generator. Clojure implements a lot of primitives in Java, but ClojureScript is largely written in Clojure and ClojureScript itself so should be easier to port.


Suggestion: Have a look at Lisp Flavoured Erlang [1], a Lisp-2 on top of Erlang/OTP that might be 90% of what you want to achieve. :^) I am sure they are happy for new members of the community!

[1] http://lfe.io/


It would be very interesting to have pointers here. I would have liked to do the same thing, but for Golang.


Maybe here: http://lfe.io/


I think I'd have a look at ClojureScript and/or Clojure CLR (https://github.com/clojure/clojure-clr). The former is Clojure on top of Javascript, the latter Clojure on top of .Net.

It will not be easy, but I'm no expert. I'm also not sure why you'd really want that? Just to solve the long startup time?


Forking Joxa (https://github.com/joxa/joxa) could be a good place to start.

Might I propose Cloxure ("clock-jur") as the language name? :)


Hmm hmm study the internals of Lumo maybe? Here's a question, would you be able to build non-actor based concurrency primitives like async's go blocks on a actor based erlang framework? (sorta shooting the shit here)


The startup time is of course very low, so that's good.

I think you're underselling the tool for beginners like myself - it's not just a ClojureScript REPL, it's actually an executable which can run node.js scripts written in clojurescript.

For example, here's the typical node http server example: https://gist.github.com/anonymous/c14b2b57184c650d9f3ff0a9e1...

You can run it with lumo test.cljs


Yeah, we have access to the whole Node.js ecosystem :-) it's also possible to npm install a package and use it from Lumo. The Node require resolution algorithm just works.


How do you specify which npm packages / versions do you want to use?


Just like you'd do in Node. Add deps to a package.json and npm install. Then just `(js/require "your-package")`


That's great. Thanks.


Considering it's running as a standalone script, how does it resolve its dependency on cljs.nodejs? That's a Clojurescript library, no?


There are a number of pre-bundled libraries in Lumo. `cljs.nodejs` is actually part of ClojureScript so it's included too.


How do I add Clojurescript libraries to my script? Doesn't that require a lein/boot cljs project?


Lumo, just like Planck, implements classpath emulation. You can specify colon separated paths to JARs/dirs in which Lumo will resolve requires. Take a look at `lumo -h` for all the available options.


Neat project, but back when I was using Clojure a lot, I lived in Emacs and the time spent for booting up my interactive environment was OK because once booted up, I would work for a long time without restarting from scratch. Same for me now with Emacs+Intero for interactive Haskell development - once everything is loaded, Everything is fast.

This project seems more suited to being able to write fast starting scripts in Clojurscript.


Came here to say exactly this. The use case is very limited, although technically it's something that will push the state of the art forward.


Author here. Happy to answer any questions.


Any canche to see ARM binaries pre built? Would love to run this on raspberry pi / pocket chip. Also planck had io support. Are you planning to add support for io in idiomatic clojure to your project?


Why not?! Sounds like a great idea. Happy to see someone help me with that


I've been using Planck (and Replete for iOS) when I need a fast-starting clj[s] REPL for checking things quickly. What would be the use case for Lumo for me?


The difference is that both Planck and Replete run on JavascriptCore, while Lumo runs on Node.js and V8. One of the advantages of piggybacking on Node is that we get access to its whole ecosystem for free.


This would be cool for ClojureScript scripting


Really hope one day we can build ClojureScript projects without JVM. Lumo is one more step further than Planck.


I totally though this was about the WebGL tile rendering library: https://github.com/kbirk/lumo

I was like "Clojure... V8, what?!"


any chance of a npm/yarn package ?


Happy to consider it, but I'm unsure what the benefits would be? Probably something like Homebrew would be more suitable I believe.


With all due respect we are on Linux. No homebrew for us. Since you are already on the node platform..this makes sense for all of us.


Makes sense, I didn't specifically mean "just Homebrew", but I'm definitely open to uploading it to the NPM registry.


Yes, there's homebrew for Linux -- linuxbrew (http://linuxbrew.sh/). I've found it extremely valuable because where I am, "normal" users aren't allowed root access but can compile things in their home directories. Linuxbrew, being a version of homebrew, makes this much easier than doing it manually.


Erm. No. I'd pay not to use npm at all.


Why? You can use it not at all for free!


Because the whole npm system is broken, and I wish yarn would break apart and start anew.


npm would be substantially more suitable. Don't forget about Windows developers, please!


s/Clojure/ClojureScript


I was confused as well. The article compares the standard Clojure REPL with two ClojureScript REPLs (one being the one introduced in the article).

Interesting nevertheless, but still confusing.


For those unfamiliar with the differences between Clojure and ClojureScript:

https://github.com/clojure/clojurescript/wiki/Differences-fr...


Please change the title to ClojureScript, not Clojure.


'fastest starting Clojure REPL'... which is still slow compared to alternatives.


Serious question: Why are you such a hater?

Every time Clojure comes up I can rely on you to appear and attack something about it (but never the core merits of the language, as far as I can recall.)


Unfortunately, Node.js and V8 seem to be about 2.5X slower than JavaScriptCore for bootstrapped ClojureScript. I haven't found a way around it. I also don't mean to trick anyone and this is explained in the post.


To me 'fast' means more than startup time. The headline is just clickbait.

The time you are reporting is also not that 'fast'. Other tools start in a fraction of that time.


sbcl isn't Clojure, is it? Or am I mistaken? I don't see the article or its title comparing it to repls in general.

Edit: original comment mentioned sbcl and included sbcl repl start-up time.


Better title would be:

'less slowest starting Clojure REPL of three'

Mentioning 'fastest' at a time of 3GHZ multicore machines with SSD and a duration of 0.1 seconds is just absurd. That's >100 million instructions just to start a language prompt and evaluating a symbol to itself. The Java version is even more absurd with around 1 billion instructions just to get a REPL...

A shell on a 1 MIPS DEC VAX 11/780 started faster, while the machine had a hundred users.


What tools? What are you comparing it to?


Basically any self respecting scripting language starts at a fraction of the time of the 'world fastest Clojure REPL'.




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

Search: