> I'd look to languages like Clojure and Python to get better expectations on how releases work.
Both Python and Clojure had several release this year. Both of these languages are mature languages where a slower release cycle is to be expected.
Elm is still in development and it is normal to expect a shorter release cycle. Not counting dev releases, there were 6 releases in 2014, 4 in 2015 and only 2 last year. This year will probably be the first year without any Elm release. Not even a patch release. This does not look healthy.
> And finally, with the amount that I am working, I'd say the concern is not warranted
I beg to differ. The concern is warranted but it has less to do with how much work you and others put in development of Elm and more to do with how transparent and well communicated this work is.
I wonder if maybe concerns like these have less to do with how healthy Elm is and more to do with how healthy Elm appears to be. The OP was trying to convince people who don't know Elm and, for such tasks, appearances might matter.
I've been using Clojure for past 6 years professionally, and I still enjoy working with it immensely. I find that the language does a really good job of getting out of the way and allowing me to focus on the problem.
In my view, the biggest advantage of Clojure over other choices is its simplicity. Clojure is a small language with a strong focus on pragmatism. I find that it allows you to apply a small number of concepts to a wide range of problems. This results in code that's easy to read and maintain, and prevalence of common patterns accepted by the community mean that it's easy to read code written by others as well.
The pragmatism also extends to the choice of runtimes. Clojure applications commonly target the JVM and JavaScript runtimes. This gives Clojure app access to many popular domains, and provides a full stack language for web development.
Another big aspect of Clojure is its interactive nature. There is a tight integration between the editor and the application runtime. Any code that you write can be immediately executed in the context of the application. I gave a talk last year where I illustrate this workflow starting at around 15 minutes.
Finally, while many people see lack of static typing as a deficiency I haven't found this to be the case myself. Dynamic typing removes a lot of the complexity in the language, and Clojure provides tools like Spec and Schema for addressing many of the same problems types address.
The tool's usefulness depends on your understanding of it, I suggest reading Rich's paper on the history of clojure https://clojure.org/about/history to get a sense of why he built this tool.
Rich had been doing enterprise java development for a long time before he spent the time to build Clojure, your concerns are addressed in the paper.
Clojure 1.10.1 is a small release focusing on two issues: working around a Java performance regression and improving error reporting from clojure.main.
Recent builds of Java 8 (u202), 11 (11.0.2), 12, and 13 included some changes that drastically affect optimization performance of calls from static initializers to static fields. Clojure provides support for loading code on startup from a user.clj file and this occurred in the static initializer of the Clojure runtime (RT) class and was thus affected.
This issue may eventually be resolved in Java, but in Clojure we have modified runtime initialization to avoid loading user.clj in a static initializer, which mitigates the case where this caused a performance degradation.
clojure.main is frequently used as a Clojure program launcher by external tools. Previously, uncaught exceptions would be automatically printed by the JVM, which would also print the stack trace.
This release will now catch exceptions and use the same error triage and printing functionality as the Clojure repl. The full stack trace, ex-info, and other information will be printed to a target specified by the configuration. See clojure.main docs for configuration details.
See the change log for a complete list of all changes in Clojure 1.10.1.
Clojure 1.10.3 is now available
Hi and welcome to Clojure! "key work" is in fact not a spelling error, it is referring to the fact that [the other methods of accessing by key] work in the context of maps. Hopefully the single spelling error left doesn't put you off from learning a wonderful language (though I agree it doesn't look very nice). Btw, I highly recommend Clojure for the Brave and True in combination with 4clojure and the Clojure cheat sheet for beginners.
This article repeats a mistake of the main Clojure.org documentation on metadata: both begin by talking about how "Symbols and collections support metadata", which is insufficient framing.
It leaves out a common daily use of metadata, probably the most common. Most metadata will be on vars, not symbols or collections, hence all the use of #'
in the examples.
E.g., this is probably the order many newcomers first approach retrieving metadata:
(defn- my-fn [] :bar) ;; where's the private metadata? (meta 'my-fn) => nil ; not on the symbol (meta my-fn) => nil ; not on the resolved fn obj
;; ... goes off, reads the docs, looks at examples
(meta #'my-fn) => ; aha! now it works {:private true, :arglists ([]), :line 1, :column 1, :name my-fn, :ns #object[clojure.lang.Namespace 0x3d223aea "user"]}
For me Clojure beats Python in expressiveness, but I've never used Clojure to work on something with a GUI.
There's ClojureScript, which comes with every GUI framework from the JS world, but that's a different story...
I am in the static typing camp, but I have to say this was one of the best defences of a dynamically typed language I've seen.
Basically, IMHO his point was: the important thing is to make data easy to work with, using functional programming and immutability to do that... types help with stuff that in the grand scheme of things make very little difference.
My counterargument is mostly about discoverability (auto-complete, basically) and refactoring, which static types make much much better/easier, but he argues that those things are hard in statically-typed languages due to their rigidness, and a dynamically-typed language like Clojure does not even need those things because you don't need to discover things (there's a large number of common functions that work on nearly everything as opposed to thousands of types that you can't know beforehand) and refactorings as we know them are mostly unnecessary (adding a new field won't break existing code, for example, so requires no refactoring... renaming should be easy as well due to Clojure's edn and focus on identifiers/naming). He also says that types are proofs and imposing the burden of proof on everything is not helpful in any way (it usually proves something that is not as relevant as the semantics, which cannot be proven anyway), and when needed can be done anyway in Clojure using Spec. I am unfamiliar with the concept so cannot comment, would be nice to see if someone familiar can give more context.
As joinr says, no need to worry about that warning (yet) but 2.9.6 is the latest version of Leiningen and it contains a number of bug fixes since 2.9.1 so it might be worth upgrading. See https://github.com/technomancy/leiningen/releases for details.
Most of the tutorials and books still feature Leiningen but you will also see books and tutorials -- and clojure.org itself -- refer to the Clojure CLI and deps.edn
files (instead of Leiningen's project.clj
file) so at this point I think it's worth learning the official clj
and clojure
commands as well as Leiningen since that's where a lot of the focus is now with newer tooling. See https://clojure.org/guides/deps_and_cli if you want to go down that path, assuming you're on macOS or Linux, or you're using WSL2 with Windows. The Clojure CLI doesn't have a good story for Windows CMD or Powershell yet.
> The cheatsheet is generated by running an HTTP server using Clojure and Ring and then converting the produced web page to a PDF using wkhtmltopdf.
jesus
Whatever you do, please don't repeat my mistake and start writing text files and then check the result in the browser or something. Even if you use figwheel, do REPL-driven development!
You can tinker around with the application state in the REPL with almost instant feedback! Use that to figure out how to make the details of your program work. Once it works, you can test it right in the repl with some sample data your make up. You can also easily do some quick performance checks. Once your're done, you can copy your code into a source file. You can also copy the test.
Writing your code like that from the start will have a great impact on how you design your stuff, how you represent your data, how you manage your state and also the granularity and testability of your functions.
It's a bit of a hassle, you have to learn how to properly use the REPL first. and you may think: "hey, my time is limited and I want to learn the important stuff first". But trust me, learn to do apply the workflow first. It will pay off every second you do the actual work that way later.
Super quick guide: Let's assume you have loaded some clojurescript source file "file.cljs" with the namespace "xy" that contains some (def x "y") How can you interact with it in the repl?
Check out: https://github.com/matthiasn/talk-transcripts/blob/master/Halloway_Stuart/REPLDrivenDevelopment.md
The next level is probably to use the Spec library (https://clojure.org/guides/spec). The idea is really neat: you use it to write composable expectations about data or functions ("specs"). You write them once and then you can use for three things:
spec
doesn't parse the data, it "parses" the structure of the data. It offers constructs for predicate concatenation, alternation, and Kleene star. By the time spec runs, the code has already been parsed by the clojure runtime.
If your initial goal is finding out if you want to learn Clojure, you should probably not be looking for ways to improve startup time, but learn how to use the REPL: https://clojure.org/guides/repl/introduction
Sure, but you also have tools, such as threading macros, to help keep code readable. For example, in Clojure you'd typically write:
(->> (range 10) (map inc) (interpose 5) (reduce +))
instead of
(reduce + (interpose 5 (map inc (range 10))))
The Documentation covers this somewhat:
https://clojure.org/reference/datatypes
Basically you are right in that you can do nearly everything with maps. Types and Records create java classes, so there can be some performance gains on the JVM. They also help create clean interface based api's for Java consumers to interact with your Clojure data.
The documentation also indicated structs are pretty much superseded by records. Clojure values backwards compatibility, so it will stay around even though there is now a better way..
If your project uses leiningen then `lein uberjar` will create an executable jar file containing all dependencies.
If you are using the deps cli then I recommend looking into the new tools.build library https://clojure.org/guides/tools_build#_compiled_uberjar_application_build
I think this is a fair point and is mostly historical. Some libs, like spec, are dependencies of the language itself and should really be considered core, whereas things like core.match or core.cache are not. Something like core.async is somewhere in the middle. Unfortunately, it's not convenient to rename things. Hopefully the new doc at https://clojure.org/community/contrib_libs is a little clearer about the important buckets. On the upside, it's unlikely we'll make the problem any worse by making more "core" libraries. :)
>Note that recur is the only non-stack-consuming looping construct in Clojure. There is no tail-call optimization and the use of self-calls for looping of unknown bounds is discouraged. recur is functional and its use in tail-position is verified by the compiler.
These are some old references: - https://clojure.org/reference/lisps - Rich Hickey - Clojure for Lisp Programmers part 1 - part 2
There have been a few attempts.
In our team we have been very happy using spec. Initially we were using it just for documentation and slowly we have introduced generative testing which has helped us to discover quiet a few unexpected behaviours.
I've been writing Clojure for over a decade (in production) and we started with Leiningen because there was no choice, then switched to Boot in 2015 because we wanted something more "programmable" with tasks being just Clojure functions, but we switched to the new CLI and deps.edn
sometime in 2018 and we're very happy with that change.
We switched away from Boot because we were running into performance issues with the fileset abstraction and also bugs in the pod machinery.
Even before the new CLI from Cognitect, Boot was already very much a niche tool compared to Leiningen. According to the State of Clojure 2020 https://clojure.org/news/2020/02/20/state-of-clojure-2020 85% of Clojure developers are using Leiningen still with the new CLI up to about 50% of Clojure developers (it's multiple choice). Boot was down to about 6%.
First, install the command line tools on your machine:
Then create a file named "cx" with the following contents:
#!/usr/bin/env clojure (print "x")
chmod +x ./cx
Then just run ./cx as normally.
Just in the interest of historical clarity... Stu Halloway is one of the founders of Relevance (along with Justin Gehtland), a consulting company that specialized in a variety of technologies. Stu became interested in Clojure as a post-Java language, made contact with Rich, and wrote the first Clojure book. Relevance eventually steered towards making Clojure their primary focus. Separately, Rich and Stu began the work on Datomic, under the banner of Metadata Partners. Ultimately, these two entities combined, forming what is now Cognitect.
Rich didn't write a book because Stu was writing one and they were working closely together to express the ideas of Clojure via that book.
I'm new, too, but the functions have started to stick. I use the same strategy I used in school: Don't memorize. If it's important enough to be used frequently, you'll remember it. If not, it's not worth remembering, as long as you know where to go to find it when you do need it.
As for where I go: https://clojure.org/api/cheatsheet and https://clojuredocs.org/clojure.core
Most of Rich's "content production" time for the last couple of years went into the History of Clojure paper if you haven't read that yet...
Can you implement fizzbuzz?
How about a simple text game like "guess the number"?
Any small, trivial task that requires you to express solutions (crappy or not) "in the language" is how you get experience and form connections. It's no different than learning a human language; you need to leverage cognates where possible, learn basic vocabulary, and start trying to form phrases. The nice thing is that you get to have a raher immediate conversation with the REPL.
How would you figure out the 13th odd number a collection?
These little things end up exposing you to the vocabulary and concepts of the language. I find solving little puzzles (combined with a cheat sheet of common functions and forms aka vocab) is of great pedagogical value. It also forces you to get mileage at the REPL, toy with stuff, see how it responds, etc. As you make progress on the little mundane stuff, the neurons start to click and you can refer back to your previous solutions to build up. You can even port solutions to problems you've solve in other languages, and see how they look in Clojure (hopefully following Clojure idioms and not trying to write mutable Java OOP in clojure just because).
I maintain a list of per-alpha dev releases at https://clojure.org/community/devchangelog. Really the only other work so far is prepl.
We are still working on the 1.10 plan but I suspect at this point that it will be shorter and focused more on bugs and compatibility than big new features.
They do, it's basically the same language and the main difference is in how interop is done. Anything that's pure Clojure is completely portable. The dialects are intentionally designed for sharing code, and you use reader conditionals to express any platform specific logic. Many Clojure libraries cross-compile between Js and the JVM.
I get that your gripe with the video is they didn't give any examples of the utility of lisp, so please don't take this as an attack. To fill in what the video didn't, there are tons of success stories involving the lisp family of languages (which includes scheme and various dialects of lisp) since the first lisp was standardized in 1958.
It's been used in everything from military supply chain and logistics management to AI research to satellite control systems to "mundane" stuff like text editors, spreadsheet programs and game engines. It was big in the 70's and had a surge in popularity in the 90's and the new kid on the block is a lisp dialect called clojure, which runs on JVM and can natively interact with java libraries. Here are some of the clojure success stories.
Clojure is probably the most used lisp today, you can see a partial list of users here: https://clojure.org/community/companies
Elixir also has much in common with lisps and it powers stuff like discord.
1 and 3 are the same notation, but you're wrapping it into an anonymous function literal and added a type hint which helps avoiding reflection.
I don't know what the hash is doing in front of the type hint though. This does the same thing:
(map #(.length ^String %) v)
memfn
is a macro which expands to a function like examples 1 and 3, while (.foo x)
itself expands into (. x (foo))
which is the dot special form: https://clojure.org/reference/java_interop (special form means primitive baked into the compiler).
I don't see memfn
used a lot, but that's matter of taste. You can also add type hinting with memfn
:
(map (memfn ^String length) v)
I haven't used it yet but clojure.spec has really piqued my interest in Clojure and is the reason I started to learn it last week. It's worth a look:
I'm also curious what these strange syntax constructs are too. I've only ever dipped my toes into Clojure, so all I've ever needed to use are keywords :key
, sets #{1 2 3}
, and thread-last ->>
.
Are there special characters or weird forms out there that aren't on the Clojure cheatsheet? How about strange, unintuitive combinations of them in idiomatic code? Something else?
What you're doing was how I learned through 4clojure. Viewing what others had done was the most enlightening way to learn of functions I had not come across. I also recommend checking out the cheatsheet for seeing many of the core functions all in one place.
Clojure/Clojurescript both have channel implementations in the core.async library, but they're usable exactly as it would look like in Go.
https://clojure.org/news/2013/06/28/clojure-clore-async-channels
I'd say Boot is more simple (in the Hickeyean sense - see https://www.infoq.com/presentations/Simple-Made-Easy) and composable, Leiningen is more widespread and easier to get started with but may fall short for advanced use cases.
If you're justgetting started with Clojure, I'd say start with Leiningen, and when you need to make some more advanced builds learn Boot.
Also, note that it's more and more viable to just use the CLI tools; Edge is an example app that does that.
Note that static typing is only one approach for providing a specification. Racket contracts and Clojure Spec focus on providing a semantic specification via runtime contracts. My experience is that it's much easier to encode intent using these systems. At the same time, you can do things like property based testing using these specifications without additional manual work.
I think each approach has its own trade offs, and they can be combined. However, the question is of the cost. There's a case of diminishing returns when it comes to trying to get more and more guarantees for your code. Where you draw the line depends on the situation.
Yes
source: https://clojure.org/news/2016/05/23/introducing-clojure-spec
> Improved Developer Experience
> Error messages from macros are a perennial challenge for new (and experienced) users of Clojure. Specs can be used to conform data in macros instead of using a custom parser. And Clojure’s macro expansion will automatically use specs, when present, to explain errors to users. This should result in a greatly improved experience for users when errors occur.
Also see https://clojure.org/about/spec
>If you just use a map from string to int directly in clojure, or even in a statically typed languaged, you don't get any such guarantee.
You simply use different tools to provide these guarantees in Clojure. Schema and more recently Spec are two popular ways of ensuring data integrity at the API level.
The big difference with immutability is that it inherintly limits the scope of the changes making them local. When you work with objects that may be referenced in many places, it's difficult to say whether the change will corrupt another part of the application or not. However, you typically know what shape of data you want in the local scope.
It's worth noting (as the article does) Clojure's beauty lies in more than just its "main paradigm". It also has some best of breed feature for concurrency (CSP, Immutable Data, Transnational Memory), "typing" (protocols), and transducers.
I think that's really more of an issue with Haskell than FP in general. Most functional languages tend to be more pragmatic about side effects and mutation. For example, Clojure has transients for working with local mutable data. You're still writing code in FP style, but you can always do thing imperatively when that makes sense.
The problem with a lot of OO languages is that they're at the opposite end of the spectrum from Haskell. Just like Haskell makes you jump through hoops to solve problems that are trivial in imperative languages, many mainstream languages lack the tools to provide elegant FP solutions. So, you end up jumping through hoops when the solution doesn't map neatly to the imperative style.
Each year there is a community survey that asks Qs like these.
2021 State of Clojure full results: https://www.surveymonkey.com/results/SM-S2L8NR6K9/
Here's the clojure.org post about it, with links to every year's results: https://clojure.org/news/2021/04/06/state-of-clojure-2021
My own setup is:
deps.edn
stuffI haven't used Leiningen for about six years at this point (switching to Boot in 2015 and the CLI / deps.edn
in 2018).
Linux has about 37%. Windows with WSL has about 5%. Windows (without WSL) has about 5%. macOS has the rest (53%).
Did you come across transducers yet? They could help you understand the order of arguments in a practical way. After that you could dive into transducers in combination with pipeline. This blog post could also be helpful.
The CLOS model has one really great property, which is that you can adapt a foreign type — one you didn't author, for example from a library — to your own interfaces, without needing to subclass or change the foreign class definition at all. This is possible because the method implementations are separate from the class, so you can do something like:
(cl-defmethod frobnicate ((this bippity-bop)) …)
And your in-house frobnicate
generic function now works with the bippity-bop
class, even though its author doesn't know or care that you need to regularly frobnicate it.
Clojure's Protocols have the same property.
Unfortunately, while the ability is objectively awesome, the applicability is relatively low, because lol who even does this level of stuff in Emacs Lisp? And most of the elisp out there doesn't use EIEIO anyway.
It's still great, though.
Your problem is first with the section of the code that states
((dec n) b (+ a b))
(dec n) returns a number, which you've put in front of an open parenthesis, so that number will be called as a function.
The broader problem with your code is that it would lead to a stack overflow exception with bigger aguments. Clojure cannot make use of tail call optimization with your code. You should take a look at: https://clojure.org/about/functional_programming#_recursive_looping
That said, simply replacing the recursive call to fibonacci-it with recur
should skip over that problem.
I did not design Clojure, so cannot give an authoritative answer to your "why" questions. The main reason I am responding is to point out that there are ordered set and map implementations for Clojure provided by the 'ordered' library here: https://github.com/clj-commons/ordered. It enables these to be printed and read using Clojure's tagged literals: https://clojure.org/reference/reader#tagged_literals
I think it's the Clojure information model, the overarching thesis of state and identity offers a different way to accomplish the same objectives
https://clojure.org/about/state
So "value oriented programming" (map, reduce, loop/recur, transformations ... things where you care about the shape of the value) would be a lower level mechanic than "identity oriented programming" (namespaced keywords, EDN, graphs, datoms, entities, datalog).
FWIW, I implemented something similar in Java using standard ThreadPoolExecutor, but the idea came from Clojure's Agents.
I'm no D expert but I really like the look of it. Does it have a standard ThreadPool impl that you can submit work to?
>Clojure running on JVM is a mildly interesting implementation detail.
I think this dramatically understates how central the JVM is to Clojure's fundamental nature. From clojure.org's Rationale:
>Clojure meets its goals by: embracing an industry-standard, open platform - the JVM; modernizing a venerable language - Lisp; fostering functional programming with immutable persistent data structures; and providing built-in concurrency support via software transactional memory and asynchronous agents.
The JVM is first in that list for a reason. More succinctly, Clojure is designed to be a hosted language, that host is the JVM (and JavaScript, and sort of the CLR), and working with the host (such as to use libraries) is central to Clojure's reason for being.
> Rust's version uses Rust's syntax; what's the difference beyond that?
That's not the case, which is the point. Outside of a macro, this is not syntactically valid to say nothing of having semantics at all.
I think some lisps (e.g. Scheme) can do that with reader macros (so that a custom reader is used to convert a form into a lisp structure), and clojure does have a way to dispatch to alternative reader macros but I don't know that it is possible for users to define their own reader macros.
It might help to look at the form Clojure (a Lisp) takes:
{ "type" "person", "username" "rrmckinley", "color" "blue", "subreddits" ["concatenative", "kittenlang"], }
Not much different.
Think the same would be true for a concatenative language. JSON is a serialization format based on Javascript syntax, but Forth (as the unit example of a concatentative language) doesn't have much in the way of literal data structures that would lend itself to a particular format. So you are basically free to implement anything you like. However given this freedom and the era in which Forth was developed, those formats were usually binary "records", to save on space. Think Amiga IFF files.
I guess the best modern example might be from Factor. While I could not find a real example (Factor documentation sucks), I surmise it looks like this:
H{ "person" "type" "rrmckinley" "username" "blue" "color" ["concatenative" "kittenlang"] "subreddits" }
You can see the matrix of performance guarantees for Clojure data structures here, bottom line is that you end up with O(log32n) complexity for most cases. The important thing to keep in mind is that immutability is just the default. Clojure provides transients as a way to opt into mutability locally in cases where you need the additional performance. Ultimately, you can even drop down to plain old Java if you really wanted to.
The key part here is that Clojure makes it natural to write code without shared mutable state, and it takes a lot less discipline on the part of the user to structure applications in a way where you can safely do local reasoning.
Never tried or seen namespace aliasing done to import, I believe best you can do is (:import [some.namespace Class1 Class2])
and use Class1
without namespace.
You can do both (new Scanner *in*)
and (Scanner. *in*)
, see Java interop for cheat sheet. There are other fun things like memfn
and doto
that will be handy.
I've hit bumps in the road where I've told myself, "Wow, I wish I knew what was in this map" before. Working with Lacinia for instance, there is a large context map passed into all of the resolver functions in order to work with the graphQL engine. However, knowing that it's a HashMap<?, ?>
would not help a bit. What I was hoping for was either a document that said something like, 'Context is this ... blah' or a specification of what the map contained.
I'm not a fan of static type checking because it's rarely ever helped me as a human, mostly just helped my IDE (when I was using Java). However, I'm not against having some kind of type specification, i.e. Clojure.spec or docs, to make large structures more palatable.
Another tool that has helped me (and many others) is the REPL. Everyone talks about it but it's not always super clear on how to take advantage of it. The user
namespace, which you can define yourself on a 'per-project' basis, will be your best friend in development. I've used this to store example requests, context maps, data, etc... for development purposes without having to run a local DB to allow data to persist. A strong combination I've found is a dev-resources/dev-data.edn
file and something in the user
namespace to read it in and convert it to a Clojure map.
Most of my work revolves around parsing requests and GraphQL however, so 'big' maps are the thorn in my side. If you have any more specific questions (function input and return types are on many beginners minds') let me know!
> This has been canvassed elsewhere (it's fairly prominent on clojure.org - https://clojure.org/community/success_stories)
Do you mean specifically web programming? Or Clojure in general? I may be missing what you mean by "funnel."
One thing that I'm not seeing on that page is a lot of big names using Clojure for web programming, and even fewer (maybe none, I'd have to go back and count) using Clojure to create the sorts of CRUD-focused sites that frameworks like Rails and Django excel at.
And I don't think anybody is going to look at that list of success stories, and from that conclude that the web programming ecosystem around Clojure is as competitive as that around Rails or Django or even - say - Laravel.
One small thing is that lines 11 to 15 are ripe for associative destructuring.
Another is that doseq
lets you do binding and filtering like for does (with :when
and :let
).
Another is typically instead of single-branch if
forms we use when
.
Oh, and I'm not a huge fan of :refer
, especially when you're referring so many fns that you don't use.
Other than those stylistic / syntactic things, if you're doing this asynchronously for performance, you might want to have the relatively expensive operation of matching the regex to be inside the go loop rather than the main thread.
Would love to see a revised version if you can be bothered :)
Usually, no, you'd need your own version of max
.
That depends on the language, though. Clojure has protocols, which effectively allow you to define your own type-specific max
.
You will still need 3 jars for most things. The load of core specs is now delayed till your first macroexpansion though. The new deps and Clojure scripts will ease this however - https://clojure.org/guides/deps_and_cli
You're only partly correct. What you wrote used to be accurate, but now you also have the aforementioned Lumo and Planck if you want to run ClojureScript directly, without the whole transpile phase. They're standalone tools that can run cljs directly, with no need for Java or Clojure to be installed.
Planck's completely standalone, whereas Lumo requires node.js to be installed, but can also import and use node modules, and using either allows you to run cljs scripts via shebang just like you would Perl, Ruby, Python, or even Clojure.
In fact, if you restrict yourself entirely to Clojure and avoid calling out to the host (JS or Java), it really is as simple as replacing the shebang and calling it a day. The caveat there is that Clojure (and thus Clojurescript as well) is made to interop with the host platform heavily, so the more complex your program, the more likely it is you'll have to resort to interop. (Though there is a way to define functions so that they run different code depending on platform, which means you can write mostly shared code with a few wrappers.)
Many companies use Clojure. Look at the career page for any of them and you will find that many have open positions for Clojure (and functional) devs. A surprising amount are household names.
> dubious quality
Although the JVM has its flaws, I don't think they are big enough to justify ignoring everything associated with it. It is always an educational experience to understand its flaws and its strengths.
If you want to experience Clojure and are adamant about staying off the JVM, you can always try ClojureScript. Runs where JavaScript can run, even Node.js.
> The fact that clojure.spec is explicitly opposed to type checking the corresponding values of keys in maps may be a deal-breaker for me...
The fact that "map specs should be of keysets only" does not mean that the values are not checked in spec, since we tend to tie the keys themselves to specs. In fact, validating entity maps is probably one of the most common use cases for spec: https://clojure.org/guides/spec#_entity_maps
That helps, but you're still going to end up with a lot of boilerplate in practice. Here's another common example of where static typing gets in the way. My experience is that using something like Clojure Spec is simply more effective in practice.
Since it focuses on providing an actual specification, it acts as a semantic contract for what the code is doing. Ultimately that's what I really care about. Meanwhile, Spec can be used selectively, so I can apply it where it delivers the most value, which is typically at the API level.
I don't think I could do without it now that I know how expressive it is. I use it all the time to extract the keys I'm interested in using :keys, extract the first (and second and so on) element in a list in a concise way, and to find everything I want from a tuple-like structure (like a point).
I think the destructuring guide does a pretty great job of explaining how it works.
From: https://clojure.org/reference/datatypes
> fields can have type hints, and can be primitive > note that currently a type hint of a non-primitive type will not be used to constrain the field type nor the constructor arg, but will be used to optimize its use in the class methods
I verified with a quick gen-class test and using javap on the .class it spat out. As long as the type hint is for a primitive, a public field will be generated in the resulting class.
Really, though, especially in the case of defrecord, you should not be banging on the fields directly. You treat defrecords like a map on the Clojure side. If you need to hand a Java class off to someone else who's looking for explicit types, if primitives don't cut it you may need to write a small Java shim (which is a totally legit thing to do, many libraries that need sophisticated Java interop do this).
Hopefully that helps, let us know if you were asking a different question :)
Right, since Clojure is dynamically typed it would just be a single function. The type of the argument is not checked at compile time. You'd have to check the type in the function if you wanted to do different things with it at runtime.
However, in cases where you want polymorphism you'd generally use either protocols or multimethods.
There really isn't a concept of a pair in Clojure. You just use a vector if you want a pair. The compiler can tell whether something is a symbol or not however at compile time. So, if you tried to do:
(let [1 1])
you'd get an error telling you that 1
is not a symbol:
val: 1 fails spec ...
You couldn't pass a vector to a let
using a function. Something like this will produce an error:
(defn foo [bindings] (let bindings))
However, you could do that with a macro:
(defmacro my-bindings [bindings] `(let ~bindings))
A macro is able to manipulate the arguments before they're evaluated. So, in this case we can create let
bindings dynamically by passing them in. However, if you try to pass invalid bindings, the compiler will once again give you an error:
(my-bindings [1 1])
[0 0] val: 1 fails spec
I'm not sure that it's true to say "in Lisps before they've always been unqualified". Here's an example in common lisp of redefining the "unqualified" let.
CL-USER> (symbol-function 'let) #<FUNCTION (:SPECIAL LET) {1000C4205B}> CL-USER> (defpackage #:my-package) #<PACKAGE "MY-PACKAGE"> CL-USER> (in-package #:my-package) #<COMMON-LISP:PACKAGE "MY-PACKAGE"> MY-PACKAGE> (common-lisp:defun let (x) (common-lisp:1+ x)) LET MY-PACKAGE> (let 10) 11
I think my biggest frustration with the way special forms work in Clojure is that it changes the way backquote has to work in order for macro expansion to work properly.
user=> `(aef bef cef def eef fef gef) (user/aef user/bef user/cef def user/eef user/fef user/gef)
All of the symbols get namespace qualified, except for def
. That's potentially annoying, because it means that some symbols are treated differently and now my code might need to adapt to special cases in the Clojure compiler.
For extra fun, here are all the special forms listed on the Clojure site, in a backquote. What do you think the result of this will be, and why?
user=> `(def if do let quote var fn loop recur throw try monitor-enter monitor-exit)
For Java interop, take a look here: https://clojure.org/reference/java_interop.
When I want to finding something in JVM standard library, I just Google the solution in Stackoverflow in Java and adapt it into Clojure. But I would be interested in getting deeper understanding of JVM overall.
The -T stuff is only in the current prerelease of the Clojure CLI (at this moment, latest = 1.10.3.929). If on Mac, you can `brew install clojure/tools/`. On Linux, use the getting started instructions at https://clojure.org/guides/getting_started with that version instead. We expect this to be moving into stable soon.
I would highly recommend using the clojure command line program directly with a deps.edn file. It’s one of my favorite parts about clojure. You can use lein instead, but you would be missing out on some cool stuff. At least read this before you commit to lein https://clojure.org/guides/deps_and_cli
You can call Clojure code 'dynamically' from Java, i.e. without compiling and packaging all the code in a jar.
Look here: https://clojure.org/reference/java_interop#_calling_clojure_from_java
It can be a little fidly. If you don't come right respond here, and I will see if I can find a complete example.
If you're concerned about startup time during dev due to reading and compiling Clojure files, consider https://clojure.org/guides/dev_startup_time as an approach to minimize that. The mechanics will be a bit different in a mixed Maven project but the idea is the same - include some path with compiled Clojure on your classpath and compile Clojure code/libs to that location to avoid needing to recompile every time you start.
Mainly documentation. I was, and am, a java developer professionally. This got me very, very comfortable with the JVM. When I stumbled upon clojure, I was already very familiar with the JVM and the entire ecosystem, so the syntax was basically all I really needed to learn since clojure has perfect interopability, and compiles to java bytecode. The thing about clojure syntax is that there basically is none, which makes it really, really easy to learn anyway, and even easier when you are very familiar with the ecosystem it belongs to, which I was.
You can check it out here: https://clojure.org/guides/getting_started
? signifies a predicate. There are a couple more conventions in names i.e. ! signifies mutation of data in place etc. Have a look at names in Clojure stdlib, most regular functions are just named "foo-bar" no need to overthink it :-)
IMHO the solution below with transducers is vastly over-engineered for beginners to learn from. For starters, focus on lazy sequences, understand how they work, learn most common idioms as this is one of the major differences for somebody coming from langs like C (your iterative approach is a clear sign you come from such a background).
https://clojure.org/api/cheatsheet
Section "sequences".
in general you just have to get familiar with the standard library, most of it are the same functions you'll find any functional language
I added the Clojure key info at https://clojure.org/community/download_key in case anyone is interested. All Clojure and contrib jars are signed with this and signatures available in Maven central repo.
That's a perfectly fine example! Thanks for the reply. They remind me of the threading macros from Clojure. How would you handle functions with more than 1 argument?
I don't know the "official" way if any. Probably there is none. Hacky and likely very slow way: clone it using get
and print the clone.
;; (class (transient [])) ;; => clojure.lang.PersistentVector$TransientVector
(defmethod print-method clojure.lang.PersistentVector$TransientVector [x ^java.io.Writer w] (let [x' (mapv #(get x %) (range (count x)))] (print-method x' w)))
Example:
(loop [v (transient []) i 0] (if (= 3 i) (persistent! v) (recur (doto (conj! v i) println) ; binding v to the conjed version -- see note below (inc i)))) ;; [0] ;; [0 1] ;; [0 1 2]
Why did I write my example that way instead of like yours? Per https://clojure.org/reference/transients , transient data structures are not supposed to be "bashed in place", which is what happens here:
(let [v (transient [])] (dotimes [n 3] (conj! v n) ; would this work with a normal vector? (println v))) ; no, because v would still be bound to []
So Clojure wants you to act as though this would print [], [], []
and stick to something more like what I wrote.
As a matter of fact, it does not print [], [], []
. It prints [0], [0 1], [0 1 2]
just like my "correct" version did. But I assume this is an accident and should not be counted on to work in future versions of Clojure, with other values, etc.
Glad it helped. Another tip... While clojure.org's docs can be helpful, I find clojuredocs.org much more useful for seeing examples of how the language should be used. Poking around there usually answers more questions for me than the standard docs. Looks like there are also some nice starting points there.
What is a TDD tool? I mean you can write tests like in almost any language. Further you can look at https://clojure.org/guides/test_check_beginner if you want your tests to have not only branch coverage, but also value coverage.
cljs is mostly compatible, with some differences due to the JS platform.
https://clojurescript.org/about/differences
CLJS is also implemented differently (I think with the benefit of experience), in that most of its stuff sits on top of clojure protocols and types (e.g. the core library is nearly a fully bootstrapped clojure in clojure). Clojure JVM has more legacy ties to java, and the original design was different.
You can use reader conditionals to write code that will work across multiple implementations and account for specific platform differences. These files (.cljc) are typically usable in either cljs or clj without issue, so there's room for significant code reuse (I found reuse in my experience porting some libraries too).
There are other platforms (clojerl, clojureCLR for example) that can similarly be targeted for cross platform code in theory.
That "gorgeous" T-shirt is a copyright violation (in addition to being spam).
The official, legal swag for Clojure is here: https://clojure.org/community/swag
> with def can never be certain that the value wasn't redefined, so if there is a bug in your code you'll have to check to ensure that the variable wasn't redefined
Idiomatic Clojure is not written in a way that this is a problem. The entire language was built from the ground up around not writing code in a way where this is a problem.
If you show us a specific code snippet you're working with, we could explain by way of example. Maybe this reference page about functional programming in Clojure could help explain in the meantime.
By the way, I don't think people should be downvoting you for being honest about your reason for asking. Folks can get a bit overzealous with rejecting the kind of programming you describe. It's very un-idiomatic :)
Take for example that thread-first
macro from the blogpost (scroll a bit or search for ->
to see macro definition), which transforms this:
(-> 1 (+ 2) (* 3 4) (/ 5))
into this:
(/ (* (+ 1 2) 3 4) 5)
It is quite often used macro to write deeply nested constructions in Clojure (more info). This is possible because we can iterate over AST and rearrange it producing new AST at compile time. The example above may not seem like much needed feature, but with more complex code it became more and more apparent that this is useful thing. For example:
(-> ws (plant "far-field" "Old McDonald's Farm") (harvest "Old McDonald's Farm") (grind "stone-mill") (prepare-dough "sea-salt" "white-sugar" "ev-olive-oil") (make-base) (add-toppings "rosa-tomatoes" "mozzarella" "ham") (bake "stone oven") (enjoy))
Taken from this blogpost. This looks like a set of things to perfrom in order by passing the result of previous one to the next one. The code that macro produces, and what you essentially would write without it is quite long, but this is general data flow of that program:
(enjoy (bake (add-toppings (make-base (prepare-dough (grind (harvest (plant ws "far-field" "Old McDonald's Farm") "Old McDonald's Farm") "stone-mill") "sea-salt" "white-sugar" "ev-olive-oil")) "rosa-tomatoes" "mozzarella" "ham") "stone oven"))
Are you tied to sets? You could always switch to another collection type that allows repetition of the same element to simulate what you want, e.g. [1 1 1 2 3 4 5] to make 1 three times as likely as the other choices.
If that's too finicky/limited for what you need, you probably want a custom generator that takes a set of elements and the desired frequency of each element.
As per the stack overflow answer:- ^
is a shortcut to add metadata to the next symbol, e.g. (def ^String s "hi")
adds a type hint to the metadata of the symbol s
indicating to the clojure compiler what java type it is.
The colon indicates a keyword, e.g. :private
or :no-doc
. The previous two keywords are commonly added to function definitions.
Therefore ^:no-doc
means "add :no-doc
to the metadata of digest-engines
". I'm guessing this indicates there is no documentation for this var, unsure though, never used it.
If you don't know what symbols, metadata or keywords are then you need to spend some more time learning the basics. All the reference articles would be a great start https://clojure.org/reference/reader
Clojure if
doesn't specifically allow a set as a function. Rather, a set is a function in clojure, and you can use it anywhere as a function.
From https://clojure.org/reference/data_structures#Sets: "Sets are functions of their members, using get"
So (#{:a :b :c} :a) ;=> :a
and (#{:a :b :c} :x) ;=> nil
as if you called get
on the set with the argument :a
.
List do not have a function implementation, hence the first exception in your image.
Maps do have a function implementation similar to set. Using a map as a function is also like calling get
. So ({:a 1 :b 2} :b) ;=> 2
Vectors are associative like maps, but with their indexes as keys. So your second exception is an IndexOutOfBounds because ([2] 1)
attempts to look up the value at index 1 of the vector, which is out of bounds. But you can call ([2] 0)
to get 2
or ([1 2 3] 1)
to get 2
.
Personally I'd like to see something more analogous to Clojure Spec (https://clojure.org/guides/spec). It's written in clojure obviously so it's not the easiest thing to read but the basic gist is the ability to define arbitrary 'validators' for the input and output of functions that can either be evaluated at 'compile' time or 'runtime'.
I'd say newcomer resources are an area ripe for improvement. It's probably best to see if you can contribute a guide for clojure.org since it's the first place people are likely to look. There's a contribution guide available here. Adding stuff like instructions for Windows setup would be great. Little frustrations can really add up and people can give up before they start doing anything enjoyable. The bootcamp idea seems reasonable to me as well, I can even see employers paying for training and materials potentially.
What's more, the good parts of OOP work perfectly fine in FP paradigm. For example, polymorphism is much more flexible in Clojure with multimethods and protocols than it is in Java. I agree with the core message in the talk that there are useful abstractions in both paradigms. However, even Brian says that you want push your OO abstractions to the edges meaning that majority of your code should be written in FP style. At that point you should be choosing a functional language in my opinion.
There's a number of different convenient options depending on what you want to do with your class. The one I use the most personally is defrecord, which creates a class with fields that also implements the map interfaces so you can use get, assoc, etc. You can read in detail here: https://clojure.org/reference/datatypes
Clojure's OO features also add another thing that Java doesn't have: Protocols. You can read about Clojure's protocols in many places, one of which is here: https://www.braveclojure.com/multimethods-records-protocols/
> Runtime contracts? as in, assert statements at the beginning of function bodies? Those are hacks/word-arounds for languages that don't have static typing. I used to do this in Javascript because I switched to Typescript.
No, as in Racket Contracts (https://docs.racket-lang.org/reference/contracts.html) or Clojure Spec (https://clojure.org/guides/spec).
> Rule systems? I'm not familiar with those but I have a gut feeling they only exacerbate the problem.
Obviously you're not.
In case you would consider that decades of hard work from experts could provide better insights than your gut feeling, check out Forward-chaining rule systems and logic engines.
> I don't understand the value you see in removing compile time type checking, honestly.
There are many well-known shortcomings to static type systems, as shown by the fact that we keep seeing new ones. Martin Odersky himself acknowledges that types introduce complexity, and it is well known that a type system can lead to over-specificity.
Sorry if my answer seems unfriendly, but I sincerely believe that our industry really suffers from the kind of attitude shown in your comment - being fixated one just "one paradigm above them all", and dismissing alternatives out of ignorance. If only the only people who made comparisons were those that actually tried all the alternatives...
To be clear, I'm not against static typing - I'm against lack of nuance in technological choices.
I don't explicitly verify function inputs, nor do I see others' code do it very often. Some of Clojure's own functions use <code>assert</code> to give better error messages; this is also done by some libraries.
I do find <code>clojure.spec</code> useful when moving data across systems, e.g. conform
ing messages from Kafka.
Overall it's a basic tradeoff w/ dynamic programming languages that you don't get some of the compile-time checks that statically typed languages provide. In return you get to write more succinct, polymorphic code 🤷♂️.
There's also https://clojure.org/community/community_stories which has a good list of questions.
If you make it super-super easy to submit the answers (please don't ask me for a PR using markdown, I don't have time for that) you might get more contributions than what clojure.org has above.
Look at the IO section, in the official cheatsheet: https://clojure.org/api/cheatsheet
The gist is that, spit and slurp can be used to load a file to a string, and a string back into a file, but anything more complex and you'll need to use Java's IO, so you'll need to read on how the Java standard lib does IO.
That said, for scripts, I'd check out: https://github.com/Raynes/fs Its a pretty neat filesystem lib, which makes IO for scripts a lot simpler then using the Java IO directly.
I think lgstein hit the nail on the head upthread. Getting nil
from the empty collection is the point of seq
's role in Clojure, which may be described best in this context by parts of [Clojure's] Differences from Other Lisps:
> - Empty collections are distinct from nil. Clojure does not equate nil and '(). > - There is more to collections than lists. You can have instances of empty collections, some of which have literal support ([], {}, and ()). Thus there can be no sentinel empty collection value. > - Sequences are not specific collections, esp. they are not necessarily concrete lists. When you ask an empty collection for a sequence of its elements (by calling seq) it returns nil, saying "I can’t produce one". When you ask a sequence on its last element for the rest it returns another logical sequence. You can only tell if that sequence is empty by calling seq on it in turn. This enables sequences and the sequence protocol to be lazy. > - Some of the sequence functions correspond to functions from Scheme and CL that there manipulated only pairs/conses ('lists') and returned sentinel values ('() and nil) that represented 'empty' lists. The Clojure return values differ in not returning specific empty collections, but rather another logical sequence. > - It helps to distinguish collections/data-structures and seqs/iteration. In both CL and Scheme they are conflated, in Clojure they are separate. [giant table comparing clojure/lisp/scheme/java in this regard]
Emphasis mine.
That said, if you really want the specific behavior you seem to seek (ha!), consider sequence.
New to Clojure, never hear of those libraries. My culture is composed of the clojure.org web page, some chapters of the Brave book, some videos in Youtube and articles.
Never resonated in my bubble.
There are always trade offs. Advantages of having services is that they can be shared by many applications. For example, at my work we have a service bus. The service bus fetches a lot of common data from different sources. Applications then focus on application specific functionality. Apps tend to come and go, but services have a much longer life span.
As a side note, having worked with both typed and dynamic languages, I haven't found static type systems to add a huge amount of value in practice.
If you're breaking your code up into small independent components, then tracking types within those components shouldn't be problematic. Meanwhile, you want a more rich specification for functionality than most type systems can provide. In Clojure, I use Spec to provide a semantic specification for what a component is intended to do.
All that said, I don't really find dogmatic microservices to be a great approach. I think services need to be broken up at the level where it makes sense. Typically, if you have any interdependencies between your services, they should probably be a single application.