I actually don't think Clojure is a good hobby language; it has specific opinions that are there to help you make good design decisions in large scale systems, but in small projects they just get in the way.
Running on a JVM opens up access to a huge ecosystem of drivers, platforms, and integrations. Any new libraries written for Java can be trivially accessed from Clojure.
* 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
It has a strong focus on immutable data structures and good concurrency primitives. True that you can get these in libraries for other lisps, but having the entire eco-system based on them is a big selling point.
I wouldn't try to sell you on it. Languages are tools, so your question is like asking "can anyone sell me on why I should use a flashlight instead of a cheese grater?" I don't know, because I don't know what you're trying to do. Perhaps neither Clojure nor Lisp is appropriate. Basic sales pitch: use the right tool for the job.
For many of us, Clojure(Script) is the right tool: a balance of performance, ecosystem interoperability, expressiveness, etc. on the platform we're targeting.
Some people probably like it. They've had a good experience with it, that they'd like to pass along. I feel this way about several thing and will happily evangelise to someone who seems willing. I was inviting others to do that . Several of them have responded as I expected and provided lots of interesting information.
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.
Associative data structures (maps / dicts) are super first-class in Clojure, in a way that they aren't in other Lisp-1's (at least from what I remember of dabbling in Racket). There's a literal syntax for them. Keywords are functions that you can call with a map as an argument. You can destructure them in `let` bindings. And so on.
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!
Also on the subject of clojure's data structures, they are all immutable by default (with structural sharing so changes are efficient, not just defensive copys). This greatly assists in writing functional, multi-threaded code.
>Hash tables in vanilla Common Lisp are a nightmare
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.
After using a Lisp for awhile I've come to feel that privileged symbols improves ability to mentally acquire some 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.
> Associative data structures (maps / dicts) are super first-class in Clojure
“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 am a big fan of all Lisps (you will pry my Common Lisp REPL out of my cold, dead hands!), but there is only one that which has elevated both my hobby and professional work into a realm I can honestly describe as fun and intellectually stimulating even years after the honeymoon period has waned. That Lisp is Clojure.
I am planning on learning Clojure for essentially two reasons: the community / ecosystem seems very active (which is really my primary reason for picking a programming language; I have no interest in writing something if a library exists), and I've heard good things about a couple of things that distinguish it from other Lisps, like maps and spec.
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.
The good thing about Clojure is not that it's not married to the JVM. I think the JS implementation has been carrying the torch for the past year because of the many excellent front-end frameworks based on React, which are oddly faster than just using React in JS due to the code-optimisations performed by the ClojureScript compiler and Google Closure. It also has one of the best front-end dev experience in the shape of figwheel, which has become the default for all front-end dev in the language.
The .net part hasn't released a version on parity with Clojure 1.9 yet, but it is up to date with 1.8. Not many people use it, but it is quite usable. The core was developed by Rich in tandem with the JVM version until Clojure 1.0.
>Can anyone sell me on why I should use clojure over say, any of the other nice Lisp-1s, given that I don't care about java.
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.
More disillusionment than condescending. I jumped on learning Clojure waiting for big, nice surprises, new stuff, or an enlightening moment, but there wasn't any.
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 think your description is fair, although I feel like you could have started with the Clojure/ClojureScript combo for making SPAs in your first comment. That is really the biggest driver of Clojure right now, the fact that it has a mature compile-to-JS implementation that can share source code with the backend.
I think ClojureScript is very nice and I often recommend it to any Javascripter.
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.
Clojure's just a bunch of specific pragmatic decisions bunged into a Lisp. If you've never had a problem selling, deploying and integrating Common Lisp apps for clients then it sounds like it would be of dubious utility to you. People will argue all your individual points because that's what people do, but honestly, you're fine, Clojure's fine, everyone's fine.
Npm (javascript's NPM) has over 500,000 libraries right now. Do this makes JS a better language than Clojure, or a better choice? No way. And everytime some javascript fan would go against Clj/ClojureScript use citing "Clojure has 25x less libraries", i will defend Clojure.
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.
If you need to force yourself, or if you need extra special motivation to use Clojure .. most likely you will never use it
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
Clojure is the best option for me, on my own projects, because I like it and I'm more productive in it (this applies to Lisp in general, I suppose) than any other language I've tried. And when I'm working alone, that's all that matters :D
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.
Yea, I'm in Satellite Beach. Think I even interviewed (just a phone screen, really) with Swarmify a couple of years back before I had really gotten into Clojure.
I'm quite surprised by your conclusion that Clojure "is not the best option" for Business Intelligence. What general purpose language is better for that in your opinion?
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).
Clojure as a data-oriented language is great in itself, but I can see why someone would choose say R or Python for BI/data science due to the ecosystem. Clojure can of course freeride on the Java or JS ecosystems, but I don't think those ecosystems offer the same breadth as R or Python either.
I think the age of convincing other people to use a language is over, you have to find that out for yourself, like I imagine many of use who love clojure have done.
+ exceptionally well designed "immutability by default": reduces accidental complexity to a minimum and lets you focus on the problem at hand
+ 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
Having spent 10,000 hours writing Clojure code professionally, I feel confident in saying that the main selling point for Clojure is that it's a simple dynamic JVM language with very little magic. If you don't need to use any Java libraries or have any hard requirements on deploying JAR files, then Clojure probably isn't what you need. Personally if using a Lisp-1 was a hard requirement, I'd be looking into Racket.
One very important aspect of Clojure is lock-less, STM(software transactional memory) based concurrency that just works. In clojure, shared memory is readily available and with a few lines of code, you can get rid of entire third party projects typically used for in-memory caching, message-passing and background job processing. This is a huge win if you are writing long running server applications.
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.
Yes! With this release clojure.spec is ready to go (tm).
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.
I'm looking forward to hearing a long-form rational of the :args :ret :fn function instrumenting decisions, and seeing if the devs see it as an easy choice or up for debate.
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'm not familiar with Spec, but what you're describing sounds like enforcing preconditions but not postconditions by default, which is what most DbC frameworks (or languages with intrinsic support) normally do. The idea being that most of the time, you're running library code, and you want to validate what you're passing in, but you trust it to do the right thing for any input that is valid.
That’s fine, and makes sense, but without hacks, it is not possible to turn on ret checking in your tests that are not generative tests - so no post condition checks in the rest of your test suite.
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.
At the moment, most of the things that are spec'ed in the box are the gnarly macro "languages" like destructuring, defn, ns, etc. These spec'ed macros do catch a far more complete set of error messages than the macro did with custom error handling. However, those error messages are in many case more challenging to read than before. This is an area of ongoing research - the reasons for the complexity are varied. In general, specs for core functions are much simpler and provide much better errors, but we have not focused on adding those yet. This is why some of this stuff is still alpha!
Do you have an idea of how far along clojure.spec is for a stable, non-alpha/beta release? clojure.spec is by far the most interesting and compelling reason for me to check out Clojure.
I would also love for it to apply to ClojureScript as well, although I understand that's a separate project.
You should try it now - the API has been out and stable for over a year. While it may change slightly, there is no reason not to use it now. https://clojure.org/guides/spec
The ClojureScript version of spec is largely API-compatible as well so the same specs should work in both.
Why focus on command line tools? There's already Leiningen which does an amazing job, and Boot for the few use-cases Leiningen doesn't take care of. Creating another CLI seems very strange.
Well, for the first time since the langauge was created you can now do `brew install clojure`, and then `clj` on the commandline and get a repl. I would have loved something that painless when I was learning Clojure.
Sorry but this whole conversation is a stretch. Anyone who looks into Clojure finds out about Leiningen. The official clojure.org even mentions it. No it's more than a stretch, it's rationalizing.
Previously, there was no brew formula for clojure and there was a hardcoded pointer to leiningen. When the clojure formula was added, that was removed.
Installing leiningen is not trivial on all platforms (Eg: Windows).Even on Linux distro repos may contain older versions of leiningen that subtly break things. Perhaps it would be better if leiningen is part of closure distribution rather than existing as separate entity.
The new tools.deps.alpha and command line tools are focused differently. They are specifically for creating classpath and launching Clojure programs, so do not cover use cases of building or deploying artifacts - for that you should still use a build tool like Leiningen or Boot. (But those tools may actually be able to use tools.deps.alpha.)
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.
Yea, I literally have never had a problem with leiningen breaking.
All people that I know that use Leiningen are also in general very happy with it. Curious what lein-specific problems you've ran into.
Last time I looked into Clojure, I found myself frustrated that I was expected to install leiningen, Java, etc. and get it all working together before I could write any code.
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.
I used Clojure before leiningen existed. It's very possible to run Clojure from the command line without it, to specify classpaths yourself, to manage dependencies by hand, even to build an executable JAR.
There's not much documentation for doing that stuff though, because (almost) nobody wants to do that. It's painful.
Is clojure.spec a replacement for static typing? I ask because I've taken a look at clojure in the past and generally like what I see, but after moving from JavaScript to TypeScript at work I don't think I can ever return to a dynamically typed language again. Types are just priceless when refactoring, integrating someone else's code with your own or just exploring new libraries or APIs.
It addresses similar problems as static typing, yes, but it's not a compile time check. Instead, it's an optional runtime check. The idea is to use it during testing and development, and have it be disabled during production.
Yup, specs can be used at runtime as well. Very helpful to validate data, or perhaps a set of business rules. Insures the spec you use to test is the same logic you use at runtime.
i think they're talking about production run time versus development runtime. think of it as turning asserts off on production builds. but spec can conform data and explain the reason for non-conformance so some might leave some spec in production for logging, messages, etc.
As with many things in life, it's a tradeoff, not a replacement :) Specs don't run at compile-time (except for macros), but they are more flexible than static types. Apart from runtime instrumentation, they can be used to validate or destructure complicated data (read from the wire or disk, for example), and they can generate tests automatically.
In addition to the sibling comments: worth noting that spec offers you a-la-carte typing - typing when/where/how you please. This is very different from the experience of having to constantly satisfy a compiler.
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.
I think it's better referred to as a toolkit for building more correct software.
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
Worked with static typing for about a decade primarily with Java in the enterprise. However, I've also used Haskell and Scala which have advanced type systems. I moved to work with Clojure about 8 years ago, and I don't miss types. If I did, I would've gone back to a typed language a long time ago.
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.
How is debugging support nowadays? Does it allow to spawn a repl at point of exception and examine locals? Is the Emacs based tooling become more refined?
I'm disappointed that spec didn't get beyond alpha for this release. This worries me a little just because in the past a lot of stuff in Clojure releases has felt abandoned after a while - transducers were new in 1.8, but couldn't use the parallelism of 1.7's reducers (do we use them still?!) because of the way stateful transducers worked. I don't see this having been worked upon for this release.
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.
There have been things that were alpha parts of Clojure for years in the past. They were eventually finalized or in a few cases deprecated. Given that few people try alpha software, it's better to release and let people use it while the work is finished. I have no doubts that spec will finish and leave alpha.
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.
120 comments
[ 3.2 ms ] story [ 185 ms ] threadhttps://clojurescript.org/about/rationale
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:
2. Whenever you need to do a STM transaction, just wrap the critical code within (atomic): 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: So yes, it is easy under Clojure, but not only in Clojure.https://www.youtube.com/watch?v=2V1FtfBDsLU
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.
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.
[0]: https://www.reddit.com/r/atwoodslaw/
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.
https://sekao.net/lightmod/
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:
https://github.com/arohner/spectrum
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...
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.
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.
Yes, for the reason you cite: parallelism. See Alex Miller's recent comment here:
https://clojureverse.org/t/reducers-what-are-they-for/1054/3
> it feels like an ongoing dialogue that's over my head
There's always Haskell:
https://www.youtube.com/watch?v=6GNDzrgFhGM&t=23m34s
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.