Elements of Clojure
elementsofclojure.com
elementsofclojure.com
OT: Does anyone know of other projects in the spirit of Pixie? To be more specific I'm looking for existing work on a clojure->rust project, would contribute or start one if none exist. Sorry for the offtopic!
Speaking of python, there's also hylang: use python functions with lisp s-expr syntax. It's very cool
I'm not sure that I completely agree, but this is a great conversation to be having!
Forgot to mention, clojure community isn't doing too badly at StackOverflow:
http://stackoverflow.com/research/developer-survey-2016#tech...
So transducers are a great hack, but not a great feature of the language, more of a necessary evil.
I would say don't bother. The language is a nice lisp (nice simplifications, great immutable ADTs, edn, pretty good async libs, etc), but the ecosystem tooling is still miserable IMHO - this is coming from someone who has worked around 6-12 months on a couple of clojure projects, so I'm not a die hard expert but I've worked enough with it to build with/have used clojurescript,clojure,java bindings,leiningen/boot,clojars etc.
I really wanted to like clojure. The idea that I could run my code on the JVM and in the browser is alluring. And, I would add, if you like using mvn or ant or gradle or in general the java way of doing things, you will probably think leiningen is good enough.
But I have zero tolerence for annoying/unnecessary headaches, and so I can't take it, and don't program any of my projects in it.
What do I consider good? Well an analogous project is Mix for Erlang/Elixir, and I think its an excellent example of what I wish leiningen were. Anyone can learn Mix in about 10 minutes, you can easily use it for erlang & elixir projects, I have had zero problems and stuff just works. This is not the case with leiningen, which makes building and deployments unnecessarily complicated in clojure.
I don't see clojure getting mainstream until someone starts concentrating more on the user experience in the tooling, though this may be more of a java/jvm originated problem, still it affects your whole experience while using clojure.
Yes, I can see your point. And there are some really good innovations clojure has made I think - specifically the immutable data types are really beyond compare, anyone wishing to learn more about immutable data structs should definately read their source in clojure its clear and understandable.
Still, if anyone wants to learn what lisp is all about. I'd just recommend common lisp (clisp or sbcl) for getting the special forms, repl, and beauty of macros bits. It also means you don't have to know anything about java to get the key concepts of a lisp, which is less overhead overall.
Regardless of common lisp or clojure, I'd always recommend anyone listen to Rich Hickey's talks on clojure, they are very good, especially his talks about values, composition, and simplicity. (http://www.infoq.com/author/Rich-Hickey)
I have used both professionally and find the dev tooling for clojure very immature. Debugging tools is not a modern feature yet clojure jas very little
The cider debugger is one of the best I've seen. I've been able to stop, inject code and create a test case for failing tests.
Cursive has similar functionality as well if you are afraid of emacs becoming a lifestyle :)
I don't have experience with Elixir/Erlang/Mix but I've heard great things about them.
One thing I'm sure though is that Leiningen and all its guides are second to none - read them https://github.com/technomancy/leiningen/blob/master/doc/TUT... .
Here's how your start a project with Leiningen:
lein new app
Here's how you build the whole project:
lein uberjar
Here's how you run your Clojure app:
java -jar my-clj-project.jar
Incidentally, I've been putting more effort into Elixir/Erlang recently, I honestly see more of a future there (for me) than in Clojure.
Clojure was definitely a mind-expanding language for me though, no regrets learning it.
On the other hand, Clojure REPL workflow (although some assembly is required) is phenomenal. Clojurescript is a serious option on the client. And for runtime-level tools (profilers etc), you have the whole Java/JVM ecosystem, which is simply bigger, for better or worse, than the Erlang equivalent.
So for my money, it's definitely a two horse race.
$ time lein --help
...
lein --help 2,16s user 0,49s system 141% cpu 1,880 total
$ time mix help
...
mix help 0,51s user 0,51s system 129% cpu 0,789 total
This difference compounds and it gets rather annoying quite fast.I find Clojure is a lot more approachable if you start with Clojurescript / Reagent and go from there. You don't need to be running Clojure on the backend to make use of how awesome Clojurescript is, and it's about 10x more approachable.
Where did you get this impression? The State of Clojure 2015 survey indicates that the Clojure developers are very interested in making the developer experience better, particularly around error messages, startup time and documentation.
I cannot recall seeing a single Clojure developer suggest that Clojure's tooling is good enough. However, it is a lot better than it was.
Don't need to install anything.
https://github.com/amontalenti/elements-of-python-style
"Elements of Python Style": goes beyond PEP8 to discuss what makes Python code feel great.
Looking forward to reading Elements of Clojure. Python is my primary language but Clojure is my hobby language.
Examples:
> If a value is an arbitrary Clojure expression, it should be called form. If a macro takes many expressions, the variadic parameters should be called body.
A value is an expression? what is a Clojure 'expression'? Why should it be called a 'form'? If it is arbitrary, why not call it 'expression'? What does it mean to 'take an expression'? What is 'many'? A macro, the variadic parameterS, which should be called 'body'? The macro has more than one variadic parameter and they all should be called 'body'?
> These defaults derive from the idioms of the Clojure ecosystem and common sense.
'Common sense'? What?
> If a value can be anything, we should call it x. This is fairly rare, as usually the only things we can do with an object involve =, hash, and str.
What is a value? what is 'anything'? The next sentence talks about 'objects'. Is 'anything' always an object? Why don't we talk about 'any object', instead of 'anything'? What does the number of possible actions have to do how 'rare' a name is?
> Let’s consider a student datatype, which is represented as a map whose keys and values are well defined, either using documentation or a formal schema. Anything called student should have at least these entries, and sometimes only these entries.
A data type is 'represented' by a map? What does that even mean? 'Anything'? What is 'anything' in this context and what means 'entry' in this context?
This goes on and on.
> At runtime, our scope is any data we can see from within our thread.
What is this 'we' at runtime and what does it mean for 'we' to 'see' data?
> Conversely, we can think of sending an HTTP request as sailing across an ocean; any transfer of data, pushed or pulled, requires effort.
What?
> If a function crosses scope boundaries, there should be a verb in the name. If it pulls data, it should describe the datatype it returns.
What does it mean for a function to cross scope boundaries? What is a scope boundary?
> There are two kinds of macros: those which we understand syntactically, and those which we understand semantically.
There are two kinds of macros, those who begin with WITH and those who do not. What does it mean to 'understand syntactically'/'semantically'?
> To use with-open effectively, we must macroexpand it in our heads whenever it appears in the code.
what?
> syntactic macros
What is that exactly?
> Transforming arbitrary code is difficult and sometimes impossible
What?
Macros: ... > Readers must not only understand the semantics of the transformation, but also its exceptions and failure modes.
Personally I, as a reader, am more interested in the semantics of the generated code.
> macroexpanded syntax
How can I macroexpand syntax?
> Naming is a problem which cannot be solved, but also cannot be ignored.
What kind of problem is it and why can't it be solved. But then I can not ignore it? It can't be solved, but I can not ignore it?
Worse: much of the advice assumes a static text program where the only option I have is to read the text. Not a Lisp system with data<->code.
Probably also a good idea to write a glossary, where the basic terms (form, value, expression, scope, scope boundary, data, parameter, argument, variadic argument, syntactic macro, failure mode, ...) are defined/described.
I'd like to add that if you find yourself needing to mentally expand a macro every time you use/read it, you have a poor abstraction. Consider going back to the drawing board and coming up with a good abstraction instead.
For a macro which only implements a 'simple' syntactic transformation, I would expect that I need to understand the semantics anyway. Additionally I need to understand the syntax it implements.
What I don't want at all, is to macroexpand macro forms. If I need to macroexpand those, then I would do this for debugging, when there is something wrong during expansion or with the generated code. I would also do the expansion not in my head, but using a function called 'macroexpand'.
For example as a reader, I don't want to macroexpand the following form in my head:
(with-open-file (foo "bar.text")
(read foo))
What I really want is to understand the syntax of WITH-OPEN-FILE and semantics of such a form. To what it transforms itself is the least important thing I (as a code reader) want to know when dealing with macro forms.* it's a WITH- type macro which sets up a scope.
* a WITH- type macro usually expects a variable, something which helps creating an object that is bound to the variable and a bunch of options
(foo-var "the-file-name.text" :direction :input)
* it then expects a body, a sequence of forms, where the variable FOO-VAR is bound to a fileThat's the syntax.
The semantics is that at runtime it opens a file as a stream and makes sure that on all forms of exit, the stream gets closed.
To what form this actually expands (even if it is a simple transformation) is uninteresting and may be different from implementation to implementation.