What do beginners struggle with?

Hi folks,

I read this thread with interest, especially the replies by @HolyJak @PEZ and @seancorfield. As a complete #Clojurenoob, this thread really resonated.

But as I read the thread, I realise that there are beginners, and there are beginners. Some of the beginners here make me feel that they are misnamed: they might be beginners in Clojure but they definitely feel very experienced as developers!
And it dawned on me that, maybe it makes sense for us to clarify further what we mean by beginners.

I will speak first for myself: I am a beginner-beginner, in that I completed an online web dev bootcamp, so I know basic HTML, CSS and Javascript, but haven’t really practiced those because I have an instinctive feeling that I will develop a lot of bad habits… so I basically followed the web tutorials, and can do some very basic commands. But I really disliked the messiness of JS, which feels very unclean and untidy mentally: very hard to debug… I only started last year, so am really a complete noob not just in Clojure, but to the world of programming in general.

Besides me, there are people who have a couple of years’ experience in other languages. They are then exploring Clojure. I think they are the next segment.

Then there are beginners to Clojure, but who are highly experienced in other languages, some with 10+ years’ experience. I suspect their needs will differ from the above two categories.

So when we talk about beginners, it really spans a spectrum, each with different needs.

I suppose the corollary question in my head is, is there a segment of beginners that y’all want to focus on first?
Related to that question, is the question of which segment of beginners should y’all focus on first? (I don’t have answers to the questions I am raising btw)

I also recently read this (great) book on open source software and how it has changed a lot thanks to Github and the rise of Javascript: the book is called Working in Public. In there, the book talked about how the nature of OSS has changed a lot, and how the rise of JS has also meant that many maintainers struggle with dealing with HUGE numbers of low-quality pull requests.

What I thought interesting was the characterisation of the different types of communities:

  • Federations (high user growth, high contributor growth) → Rust, nodeJS, Linux
  • Clubs (low user growth, high contributor growth) → AstroPy, but also Clojure, Haskell, Erlang
  • Stadiums (high user growth ,low contributor growth) → Babel, webpack
  • Toys (low user growth, low contributor growth) → ssh-chat

I suppose the corollary question is what segment or direction the Clojure community wants to go, and what type of beginner growth aligns with that?"

Again, I’ve no answers, just posing the questions off the top of my head. My instinct, though, is that we probably don’t want to end up being a stadium-type community…


For me as a beginner-beginner, the hard parts are learning all the basics:

  • tooling (especially finding a suitable editor, which was much harder than expected, and then setting up a REPL. I have found the IntelliJ + Cursive so far to be the easiest and most straightforward for me: initially I found Atom + Chlorine, but sadly it does seem that Atom is dying…)
  • error messaging is super opaque: they sometimes read like exclamations from Marvin the Martian to me
  • I initially also lost a bit of time wondering if I should start with Clojurescript instead of Clojure, since I know some JS. However, it’s actually a lot easier to get the tooling setup and done for Clojure than Clojurescript, so I’m taking the one-fire-at-a-time approach, and focusing on learning the language first and foremost.
  • the lack of a structured pathway with examples that I can follow. For me, I found some of Eric Normand’s videos to be super good, but they also strike me as being a bit too deep into computer science concepts for some people (I love them btw). Concurrently, I am also working through The Clojure Workshop, which is actually a REALLY great way for me to learn (as I learn best from seeing how someone does something and trying to replicate it).
  • I think what will soon follow is trying to really understand HTTP (underlying Ring), Clojurescript, etc.

But can I just say that, while the learning is tough, it is also super rewarding and I really appreciate just how friendly and helpful everyone is, on the Clojurians Slack and also on this forum? It’s amazing to see the degree of generosity and general kindness. :slight_smile: I really appreciate that, and only wish my Clojure skills were better so I can give back and pay it forward.

Peace out.

7 Likes

Hello! And thanks for this elaboration. There are so many great observations and I will certainly bookmark and consult it while working with these things. You are totally right about that there are different meanings of ”beginner”, and you provide great tools for thinking about it.

Towards the question about where the focus can be: I can only speak for myself, of course. To me it is ”all of them”. While I certainly see that we need to cover different bases for different journeys, my driver is:

You want to pick up Clojure and you struggle?

I think it is super important for us as a community to try to figure out what kind of journeys there are, and then for each one of us, as individuals, to pick a thing or two to run with.

That said, it is really intriguing to think about what we can do to make Clojure a delightful choice for learning to code.

As for your own hesitation about picking up bad habits. I don’t think you should worry too much about that. It’s inevitable, and it will happen at every stage, and never stop happening. The remedy is to keep doing stuff and get feedback and adjust. Without feedback we are all quite blind.

6 Likes

Thanks @PEZ!

That’s a really generous starting point, and I really appreciate it. :slight_smile: Thank you!

I think I can really comment on this only after I have more experience. But just off the top of my head, I think the biggest differences I’ve encountered so far are really symptoms of the different way of seeing. Like immutable data. Or the lack of for-loops (or “this”). Or the norm of “asking the REPL” if you have any question if a function could work, resulting in the very rapid, bottoms-up, iterative approach of RDD. It is really very different from anything else I’ve seen out there.

And I wonder what will happen if we have programmers who start their programming journey/career with that way-of-seeing instead of the current norm.

To clarify, I meant picking up bad habits from the web bootcamps (e.g. using “this”, or using global variables in JS). I also found myself quite uncomfortable when I learnt those, because while they got the stuff done, it just felt like… a trap? I don’t quite know why, but it just felt mentally untidy.

1 Like

As someone who’s first general-purpose learned for real was Clojure (I started learning Python, didn’t click with me so I dropped it, then learned R for work and loved it) I still struggle with Java interop, because I have absolutely no clue about Java. The rest of the language made total sense because I was already doing FP without knowing it. Mutability, classes, types are the things I find weird and hard to grasp “what problem would I fix with that”. But I’m aware this is not the majority of people.

5 Likes

Crossposting:

3 Likes
  • core.logic should be archived: it’s a rabbit hole, nobody uses it in production, the only introductory book is written in Scheme, and the documentation looks abandoned. You’ll get a better price when you sell 10 intact coffee cups than when you sell 10 intact coffee cups plus 2 broken coffee cups.
    Datalog, in contrast, was a revelation.

  • laziness is a disappointment. It promises performance gains, but those are so meager that they don’t make up for dealing with he unexpected behaviour of your code

  • homiconicity and lists: lists are rarely used in Clojure. I don’t understand why the language keeps them. Clojure forms are data, in particular: lists. If a data structure is representative of Clojure then its vectors or maps. Should Clojure be rewritten one day, then it would be better to use vectors for forms.

  • core.async is tough and there are too few books or articles that go into depth. Also, newcomers are sent to documentatiin written for golang.

  • Explanations often reference the hosting language, e.g. protocols. I don’t want to learn Java in order to learn Clojure.

2 Likes

My team uses it in production :yum:

There are about 20% of the time where performance would be a blocker to not having them, so they made sense at the time. Transducers managed to solve that problem similarly elegantly now, but they were not imagined early enough in the design of the language. Now I’d say lazy-seq are still nice for certain things, but it’s true that a lot of people feel it be nice if transducers was the default more convenient syntax, and lazy-seq the thing you reached for in special cases.

You eventually outgrow their “unexpected behaviour of your code” and stop really having to deal with that, if that’s any consolation.

I also love them for the cool factor.:sunglasses:

Fair point, I think that’s true of a lot of things in Clojure.

You mean people explain Protocols by reference.to Java interfaces?

That’s also a fair point, I think a lot of people doing Clojure have Java expertise as well, which must bias them in how they explain things.

That said, I would say that it’s important to learn the runtime memory and computation model of Clojure to become an expert in it. I think it’s important to learn that of any language. And in the case of Clojure that is the JVM. So learning about the JVM and some of its tools like VisualVM is pretty useful and it makes sense to be a part of learning Clojure. That doesn’t mean you need to learn about the Java language though.

I’ve shared that sentiment but now I’m starting to question it. After having gotten used to transducers and realizing how neatly they can be integrated into existing code, they don’t seem as esoteric and such a long reach anymore.

The question is: If you could have a language that could act as both a scripting language and a high performance language, would you prefer its default mode be scripting or high performance? If you prefer high performance, is that worth losing general laziness too?

When gluing together different systems, would you rather have a flexible language that can easily talk about endless things, or a high performance system that can only talk about finite things (with a lazy escape hatch)?

I think I’d rather the default case be the more scriptable one, for easily gluing systems together, with the option use more performant capabilities when necessary in the core performance critical areas.

And is the alternative really possible? When dealing with unbounded sequences, would it really be as easy as ‘reaching for lazy-seq in special cases’ or would it be more complicated than that, involving an entire extra streams API bolted on to the side of the language?

It would be interesting to see what the default performance of a transducers-all-the-way-down Clojure/lisp/lang would look like. Maybe that would prove useful for some things. But I think laziness is actually a better on-ramp for beginners / new Clojure developers that are gluing streaming systems together - just a little longer of an on-ramp in the beginning, to learn about forcing consumption when necessary. And then you can move gradually away from lazy scripting semantics for hot code paths in a more à la carte manner.

3 Likes

Good points, I think between lazy sequences and transducers there are things that transducers make more convoluted as well, and there isn’t a transducer for every lazy-seq function, and I think part of that is because they aren’t always all trivial to implement as a transducer, and sometimes they don’t all compose as easily together or you need to be aware of the transducing context.

I guess the question would be more what it would look like against a hybrid of eager sequence functions and transducers. For the cases of modeling infinity and such it is still a good point as too what that would look like, I don’t know if it would be better, but its an interesting thought.

Another thing, which I’ve seen sometimes as a criticism in Clojure, is that sequence functions were in the core namespace to begin with. If they had been in say a sequence namespace, you could have had another namespace named transducer or something like that. So you could have both with similar interfaces. I can’t remember why Clojire didn’t do that and choose to overload arity for transducers, when it did do it for reducers.

Anyways, to beginners reading, remember all things have trade offs and pros/cons, and I think lazy sequences over iterator style streams like Java and C#, over eager functions over transducers don’t necessarily have a clear winner, just different qualities that sometimes make one better than another. Like John said, lazy sequences do end up being a really nice default with a good balance between performance, flexibility and convenience, and there is an easy path towards performant and eager variants.

1 Like

That’s interesting! If you had to do it again, would you use core.logic, one of the rule-based libraries, or one of the datomic clones (datascript, datalevin, datahike, …)?

Thanks @didibus and @John_Newman !

1 Like

I’ll have a look at that!

I want to provide an update to myself here. My day-job is still Typescript and SQL, but about half a year ago I got so fed up with IntelliJ loading times and ergonomics that I jumped back to Vim. I haven’t missed IntelliJ even once since then, for any of the languages I develop in. I think I t is healthy to try what’s on the other side of the fence sometimes, maybe get good at it, and then make an informed decision. That goes for languages, editors/IDEs, mostly anything really.

I started (again) on Clojure after I gave up on my last try about 2 years ago. After revisiting some of the books I read, I found understanding the language not that difficult as I learned LISP and Scheme during my studies in my earlier life. However, after I have no Java and no Java Script background, I found two things difficult:

  1. In most books or tutorial there a lots of references how this would be done in Java or Java Script to explain a certain issue or functionality. I mostly don’t find it helpful at all. Reagent is explained mostly for people who did work with React. That’s it. It’s like I try to learn Japanese, and I am explained by what is the word is in Chinese.

  2. I still fight 90% of the time to get somehow the dots connected between cljs-start, Leiningen, deps.edn, cli, figwheel, Calva, REPL. As stated, I never did Java or JS, so I don’t have any experience with maven or npm, or what is a typical way of setting up dependencies in project (I choose Clojure exactly because I don’t want to learn these languages or tools). None of the Clojure books I have or online resource I found does cover these things in a good way for beginners.

Example: Today I tried to add a Clojure project I found on github to my project. This was apparently set up with Leiningen. How do I get integrated this with my deps.edn? What is anyway the best way? Clone the github repository locally? Add a dependency? If yes, how? No idea. Struggled whole day on this one. I need a broader picture about this.

1 Like

Most Clojure libraries are published to Clojars or Maven Central. Both are available by default to tools.deps.

So most likely you just define it as a Maven dependency in your deps.edn.

If the author did not publish the library, and only offers the source code, you can try adding it as a git dependency, it might work, unless the lib needs some prep.

Finally, if the lib needs some prep, and is not published to Clojars or Maven Central, you’ll have to pull it down locally and figure out how to get it prepared to be used as a dependency. This is most unlikely, I’ve never had to do that for an open source Clojure library yet, but saying it could be possible.

1 Like

What I found works 99% of the time (I have not yet encountered the 1%, so it might be 100% of the time)

In Lein, you add this to the vector of dependencies: [org-name/lib-name "0.1.1"]
In deps, you add this to the map of dependencies: org-name/lib-name {:mvn-version "0.1.1"}

And in Lein, sometimes you’ll just have lib-name, no org-name, so you can try: lib-name/lib-name {:mvn-version "0.1.1"}

tl;dr: for beginners, learning the language is very easy, but learning to do something with it is very hard. Steep or not, it should be a single learning curve, not so many curves.

I am an eternal beginner, several times I have tried to get started with Clojure and ClojureScript, and several times I had to quit and move to more urgent matters. I know the language, I have even solved a good number of exercises in Codewars. But learning to use it is always too challenging. It would be better to:

  1. Have a tutorial from scratch, like start installing leiningen.
  2. Even better, have several similar tutorials, and an index/tree about the pros and cons of each path, e.g. leiningen will give you everything for Clojure, but if you want ClojureScript you may need shadow-cljs.
  3. Build simple applications (all starting from scratch):
    1. A script that processes files from a local folder.
    2. A backend that serves a few URLs.
    3. A frontend with a ToDo list.
    4. A container in the cloud.
    5. A lambda function in the cloud.
  4. Do not stop at having something that runs, consider: testing, CI/CD, linting, dependency management, etc. Learning Clojure is (IMHO) very easy. Clojure for the Brave covers the basics really well. But that is like the “how to draw an owl” meme. Eventually you want to commit to a repository, automatically run tests, and if all is green, push updates (both in backend and frontend) to the cloud, like using Terraform, lambda functions, Heroku, Docker,… Now that video-tutorials are monetizable, I think there is a lot of money on the table.
    • Note: This kind of tutorial is really important (IMHO) and nearly impossible to find, for any language. Doesn’t anyone notice this? I feel like I am taking crazy pills!
  5. You may even want to share what you create in clojars, npm, or something else. The friction for that is so strong that thinking that people do it for free with FOSS is mindblowing.

Today I am here just because I had ClojureScript parked for many months and I wanted to make a quick and dirty script to process a .edn file, just change some timestamps according to a new timezone. In Python (which I use more often) I would have done the script in the time to write this post. In ClojureScript I am still trying to find out if I should install Leiningen, Lumo, Shadow-cljs, Yarn, a combination of the previous, or something else. All that I want is something that works, if possible something that works in the frontend and the backend.

I have even seen references to Webpack, regularly, I still have no idea where it goes in the pipeline. I understand it should be part of a github action for CI/CD, but I may be wrong. I cannot even begin to imagine why React and Reagent components may be incompatible, or different, and this incompatibility may be in one direction or both.

4 Likes

Hum… I’ve been looking to try something to walk people through actually building applications, and not just doing exercises like data-structures and algorithms stuff.

My first artifact from that is this: GitHub - didibus/clj-ddd-example: An example implementation of Domain Driven Design in Clojure.

My question is, what would you prefer to accompany that code base example and maybe to expand on?

I’m thinking a video, but would you prefer a video where you see me implement that code base from scratch? I feel that can be tedious and less engaging to watch, as you’ll see me spending a lot of time just thinking and typing.

Or maybe a video walkthrough, where I walkthrough the code after the fact? And maybe where you see me setup my environment for cloning the repo, setting up my build, launching a REPL in it, testing it, and then maybe you’d see me configure CI/CD, database, and see me deploying it as well? And then using it in production?

And maybe I could do something similar for all the things you listed:

?

So instead of a video of me going through all that from scratch, I’d make a repo, and then a video walkthrough of the code and how to go about running it and deploying it?

No promise here haha, just wondering if me or someone else wants to take a jab at it, would you prefer the walkthrough/run/deploy after the fact, or literally hours of video of me doing it all from scratch?

3 Likes

No videos, please. Instead, write a a blog post.

Explain the process of building the application, and insert code snippets after the explanation as an example of how to write it. The repository is something to check later, to see everything in context.

PD: then if you want make a YouTube video, walking people through the blog post. It is an inferior format, but it may be more monetizable. That is for you to decide.

2 Likes

Hum… I worry a blog is too small a format to walkthrough writing a complete app, also when you get to setting up CI/CD, deployment, running the app, it becomes difficult to show in text form. Something like Web Development with Clojure 3rd edition the book kind of does what you want, but it takes a full book.

Still, maybe it is possible to somehow cut out a lot of details and still be useful. I’ll think about it.

Do you have similar examples of that done but in another programming language for inspo?