Clojure 1.9 is now available
blog.cognitect.com
blog.cognitect.com
It adds a ton to Clojure(script), including better error messages (yes!), data-structure shape declarations, generation of data, writing quickcheck (generative) tests for a function by just describing the args and return values of functions, and more.
It feels really uncomfortable declaring a full spec for a function & instrumenting it, then getting feedback if the args don't conform to spec but Clojure being A-OK if the return value doesn't.
I understand well that in theory the functioning of a function can be fully checked in a proactive test framework, but the process for testing return values is way clunkier and less fun than testing argument values. It just seems weird that the function has a spec, the function doesn't conform and Clojure doesn't seem to be bothered.
I can see it will only be a matter of days until I have Orchestra installed for all my projects.
I believe this is a missed opportunity to find bugs.
The things that are really the new big drivers are pieces that are not inside the language (other than a few spec integration points). Those things are significant pieces of work that are imminently usable now, but not quite final yet. In the interest of getting this work into more hands, we made a new release of the language with those parts marked as alpha.
The modularized spec allows us to continue releasing new versions (and for you to use them) with Clojure 1.9.0. Eventually spec will leave alpha and we will consider that a finalized part of the language. Similarly, tools.deps is still evolving as we work on it but the tools should remain fairly stable.
What are some good examples of this?
I would also love for it to apply to ClojureScript as well, although I understand that's a separate project.
The ClojureScript version of spec is largely API-compatible as well so the same specs should work in both.
As it is, spec is pretty slow, and the implementation is a massive black box, with a ton of `(case op...)` on the inside making it almost impossible to extend in any sensible way for the moment. Beyond that I'm still really not clear on what the workflow with spec is supposed to be. I just don't verify code in the repl and then move on with my life, and there's no clear guidance on what a productive developer workflow for this stuff is. It doesn't help when every example of generative testing I see online omits the bit where you actually check a function does what it's supposed to.
Anyway, love Clojure still, but in a way it feels like an ongoing dialogue that's over my head, rather than a programming language.
spec is one of the fastest libraries available for validation (http://muhuk.github.io/validation-benchmark/). spec is not designed for extension by making new kinds of spec. It's designed to utilize any predicate function on the bottom and by combination from the pieces available. By analogy, is LEGO bad because it's hard to make your own bricks? That's just not how you use it.
There are some known problems that make generation slow (mostly having things that grow too quickly in combination). One known problem was fixed in the late stages before release and we will continue to work on the sizing issues.
There is more to the generative testing story that has not yet surfaced. We've got another whole project related to this that is still a work in progress. Even without that, I think there is a lot of value to be had in spec right now.
We're running some of our specs in production to enforce guarantees on data coming in and out of the app :)
What do you mean "as well"? I thought it was only a runtime check at all.
This Spectrum thing purports to evaluate your specs at compile time though:
Generally I decide to 'spec' a tiny percent of my code - just the most critical one, or APIs with complex method signatures, etc.
When doing so, if the spec'd function used N other functions, one could argue that those N functions are also being implicitly checked.
One of the things it can be used for is to check variable types at dev time, just as static type systems check a type compile time. It can also be used for runtime validation, randomized generative testing, more advanced checks/rules than current type systems can specify(eg checking outputs are correctly related to inputs).
Also an important philosophical difference with type systems is an open approach to data structure, where types are generally closed. The concept is that it allows more flexible systems that are easier to adapt to new needs.
Here's the best intro to spec I've seen, it's long and sound is terrible, but it's a great talk. https://vimeo.com/195711510
My experience is that dynamic typing is problematic in imperative/OO languages. One problem is that the data is mutable, and you pass things around by reference. Even if you knew the shape of the data originally, there's no way to tell whether it's been changed elsewhere via side effects. The other problem is that OO encourages proliferation of types in your code. Keeping track of that quickly gets out of hand.
What I find to be of highest importance is the ability to reason about parts of the application in isolation, and types don't provide much help in that regard. When you have shared mutable state, it becomes impossible to track it in your head as application size grows. Knowing the types of the data does not reduce the complexity of understanding how different parts of the application affect its overall state.
My experience is that immutability plays a far bigger role than types in addressing this problem. Immutability as the default makes it natural to structure applications using independent components. This indirectly helps with the problem of tracking types in large applications as well. You don't need to track types across your entire application, and you're able to do local reasoning within the scope of each component. Meanwhile, you make bigger components by composing smaller ones together, and you only need to know the types at the level of composition which is the public API for the components.
REPL driven development [1] also plays a big role in the workflow. Any code I write, I evaluate in the REPL straight from the editor. The REPL has the full application state, so I have access to things like database connections, queues, etc. I can even connect to the REPL in production. So, say I'm writing a function to get some data from the database, I'll write the code, and run it to see exactly the shape of the data that I have. Then I might write a function to transform it, and so on. At each step I know exactly what my data is and what my code is doing.
Where I typically care about having a formalism is at component boundaries. Spec provides a much better way to do that than types. The main reason being that it focuses on ensuring semantic correctness. For example, consider a sort function. The types can tell me that I passed in a collection of a particular type and I got a collection of the same type back. However, what I really want to know is that the collection contains the same elements, and that they're in order. This is difficult to express using most type systems out there, while trivial to do using Spec.
[1] http://blog.jayfields.com/2014/01/repl-driven-development.ht...
$ brew install leiningen
$ lein repl
That's literally all it took.And then you learn that not everyone use leiningen ... and things become a bit more confusing than necessary for some people, especially beginners
In fact it used to find leiningen instead of clojure; possibly clojure didn't exist as a brew tap before.
That isn't always trivial.
I’m not sure I understood your comment. Parent’s code block has two lines: the first one installs Leiningen; the second one starts its repl.
This is just another layer in the toolchain to contend with, and since Clojure is built on the JVM, the toolchain is already quite bloated.
or for windows: https://raw.githubusercontent.com/technomancy/leiningen/stab...
However, tools.deps.alpha doesn't restrict itself to a Maven-artifact-only dependency system. It supports an open system of coordinate and provider types of which Maven is one, but also local (no-artifact) dependencies, local jars, and eventually others as well (like using projects directly from Github). All of that vision is not done yet but it will enable many new capabilities.
Clojure is a language that runs code from Clojure source files. It does not inherently require "building" or "deploying" anything. Those are useful tools to interoperate with the Java ecosystem, but we should not limit ourselves to only that means of program building.
I must note, I like clojure for its cross vm support. One lisp to rule them all.
Mind expanding on that? My 4-years experience using Leiningen has been great so far.
I like to know how a toolchain works, how libraries are linked, etc. I don't want to rely on "magic" that can be incompatible with alternative "magic".
Clojure didn't provide a good low-level interface, and I ended up giving up on it for that reason alone.
Now that there are some minimal command-line tools, I think I'll give it another look.
There's not much documentation for doing that stuff though, because (almost) nobody wants to do that. It's painful.
That is what I encountered when I first tried to learn Clojure.
I essentially gave up at that point. Installing the JRE is enough of a pain on its own.
It's also a nice Lisp, tho not as pure or quite as powerful as others.
But I'm not very much up-to-date on anything new in the past few years, and I was never a guru anyways, so maybe there are better pitches.
* it doesn't do tail call optimisation unless you explicitly mark the self-call using by using recur (the lack of implicit TCO is a JVM limitation) * it doesn't use macros as much, although they are they're there and still used pretty often * it has language literals for the built-in data structures that look odd to other LISP'ers, e.g. {...} #{...} [...] * it's error messages are ecosystem dependent, e.g. Java stack traces for Clojure or and JS ones for ClojureScript
For many of us, Clojure(Script) is the right tool: a balance of performance, ecosystem interoperability, expressiveness, etc. on the platform we're targeting.
Lots of support
Great interactive workflow (with Emacs, Intellij, or Atom)
Data oriented (Most of your code are pure functions making
simple operations on datastructures)
Do you want to be sold on it? Is that what you want your interactions on this site to be?
Since I achieved my aim and stimulated some useful and on-topic discussion, yes, this is what I want my interactions on this site to be. Thanks for asking.
In my opinion, this turns out to be a pretty major win. Code is still data, it's just that this data is a mix of lists, vectors, and maps, and now you can type associative data structures into your code as easily as you can lists. Hash tables in vanilla Common Lisp are a nightmare (there is a nice library that at least gives a Clojure-y initialization option via Reader macro), and a glance at the Racket documentation suggests the story there is probably not much better.
Clojure code in the wild uses the destructuring on maps a lot like, e.g. Haskell code uses pattern matching, and Spec is an interesting core library aimed at allowing libraries to insist that keys be present in maps and so on.
Anyway, I had occasion to interact with some Common Lisp code I'd written several years ago, and I was struck by how annoyed I was at it for not being Clojure. Part of that is the Lisp-2 issue, but I also really missed how lightweight and easy Clojure maps are, and how easy they are to use for control flow. (I will say that Clojure's clunky recursion annoys me, but much less than trying to deal with Hash Tables in CL)
Also, and I know you say you don't care about Java, but ... sometimes you have to interact with some annoying outside system (like, a database or a web browser), and there is a high-quality Java(Script) library that makes this task trivial and lets you work on what you want!
For original asker, see here for more details: https://clojure.org/reference/data_structures
I seriously disagree with such a statement.
Also, i feel confused that a lisp being a Lisp-2 could be an "issue". Separate namespaces for functions, symbols, classes, labels and so on is great, because they dramatically simplify naming things, which is one very important task for writing clean, readable, maintenable code.
It was an okay learning journey, but it left me hungering for an easier FP language, and it also solidified my intuition that simple is both easy and hard at the same time, and Lisp is simple.
“First-class”, you keep using that word. I do not think it means what you think it means. See here for the details: https://en.wikipedia.org/wiki/First-class_citizen . tl;dr: First-class-ness is about semantics, not syntax. And, while every language (including Racket) is broken in some ways, it's not broken enough for associative data structures to be inexpressible in it.
> There's a literal syntax for them.
Why does this matter in a language with macros anyway?
> Why does this matter in a language with macros anyway
If you don't have this, you end up with a bunch of read table syntaxes for the same thing. These usually don't play nicely together. See Common Lisp libraries (since CL doesn't provide this syntax in the standard) for all the subtly incompatible hash table read syntaxes and printer implementations you can stomach.
I'm somewhere between indifferent to unhappy that it runs on the JVM, but it's not a blocker for me. And as someone else mentions, it means there's pretty good access to the Java library ecosystem.
Not sure how usable the .net part is.
It's the same question I make myself, since after learning Clojure here I am, still using Common Lisp happily ever after.
Well, if you think that data should be immutable by default,
if you really need readymade syntax sugar for hash table and the likes,
if you don't have any need for object oriented programming, not even once a year, and if you particularly don't feel any appeal for what is by far the most advanced OOP system out there (CLOS),
if you love the Java ecosystem and need to be reminded of the J2SE libraries by using them directly all the time to do things other languages have built inside their own stdlib (but Clojure does not),
if you don't feel wrong knowing that your code won't be compatible with the .NET CLR (or other platforms) without rewriting substantial parts, despite the language still being called "Clojure",
if you don't know that basically all you can do with Clojure, including talking to Java (and also C) libs, and all sorts of concurrency things, you can also do with good old, high performance Common Lisp...
... then welcome to Clojure! Clojure is easy to learn, fashionable, well supported, and popular.
I guess that if you are into developing single page applications, Clojure can be a neat choice.
I do, however, find it easy to learn and that's a good thing.
On the other hand i am not really knocking down Clojure. If somebody wants me to do a project that requires interacting with lots of Java classes, I'm really glad Clojure exists, since it liberates us from plain Java. I am also glad that it's a good "gateway drug" to Lisps in general. I'm also glad that ClojureScript exists, perhaps this is the best part of Clojure.
But Clojure is not really my cup of tea. I want wide options for mutable data (for performance reasons). I want multiple paradigms, including a powerful OOP system. I want easy recursion with tail call optimization. I want explicit, detailed error messages, etc.
I sincerely thank Hickey for liberating us from Java and plain Javascript. I think that if Clojure was pitched more as substitutes of Javaxx and less as "the improvement over Lisp", many of us Lispers would be happy. As I mentioned, it isn't an improved Lisp; it is a different language with a different (valid) philosophy which some will like.
How many libs do we really need? So far i have found all libs I needed for Common Lisp. Many of them have high quality, at least in the sense that the code is easy to understand.
Most needed stuff is there - db access, key/value store, cryptography, authentication, html templating, JSON, xml, web servers, software transactional memory, parallelization, compression, networking, foreign interfaces, music, midi, all sorts of data structures, and of course tons of AI related stuff. This is not like NPM where there is a lib solely dedicated solely to left-padding a string, and when you take down this lib the whole ecosystem collapses big-time (true story).
Also, just as Clojure can easily call Java code and thus have access to the huge and mature Java lib ecosystem, also Common Lisp can very easily (see 'CFFI') access the huge and mature C lib ecosystem, mind you, and this feature is leveraged a lot in CL. This also has performance benefits, since tuned Lisp code is as fast as tuned Java code, but C can often go faster. With ABCL (JVM implementation of Common Lisp), you can even call Java libs and C libs in the very same source code, which I find amazing.
You might spend sometime learning it out of curiosity, but you will never use it
The languages I used, are ones I had to use, either because they were the only option, or clearly the best option for my solution (C#, VB.Net, Powershell, SQL)
Clojure will never be your only option, and .. it being the best option is highly subjective
I tried to like Clojure, because of all the blogs and talks, and the reputation of it being a smart language for smart people (I obviously, wanted to belong to this group, and still do) .. but for my work, and use cases (Business Intelligence), it will never be the best option, or the only option
The same list that include Clojure, for me also include F#, D, Dart, Ada 2012, Idris, Julia, Rust .. I am sure since you asked this question, you have your own list too .. and you know, nothing will really work to motivate you
Now, when I'm working on a team... I must tell you I live in Florida and if I've ever met another Clojure/Lisp programmer in meatspace I'm not aware of it (and I'd be surprised). In fact, most of the Java developers I work with only really know Java (and some are helpless without Eclipse). So recommending anything that isn't Java probably wouldn't be wise. So, yea.
There's a difference between "best tool for the job" and "most widely used tool for the job" at this moment in time. People's habits sometimes take years or decades to break (if they ever do).
Amen. Learning is best a journey of desire, not compulsion.
+ ClojureScript: allows you to write Clojure for any JavaScript platform (web, ReactNative iOS/Android, node.js) while writing 97% the same language excluding any platform-specific differences between the JVM and the JavaScript runtimes (V8, JavaScriptCore, etc)
+ piggyback on the entire mature+stable+performant JVM ecosystem of
... + tools (Maven >> npm, cocoapods, et al)
... + libraries (Netty, JodaTime, etc)
1. Declare the classes you want to work under transactional memory as transactional... by wrapping them in such a way:
(transactional (defclass ...))
2. Whenever you need to do a STM transaction, just wrap the critical code within (atomic): (atomic (do-stuff ... ))
Is it hard? Nope. The above example is one easy way to do it in Common Lisp, using the STMX library, which of course you can download, compile and load with one line of code: (ql:quickload "stmx")
So yes, it is easy under Clojure, but not only in Clojure. clojure.core.server/start-server
Will start a socket REPL at any point in your Clojure program.Unravel is a terminal-based REPL client you can use to connect to that REPL:
https://github.com/Unrepl/unravel
Alternately, for a smooth interactive development experience, including the ability to set breakpoints on lines, there is Cursive:
https://github.com/cursive-ide/cursive
Cursive is a plugin for IntelliJ. There's a free community edition of IntelliJ that works great.