
Clojure is cool - jxub
http://ahungry.com/blog/2018-09-09-Clojure-is-Cool.html
======
haywood
Clojurescript has been the real hero for me coming from a heavy Javascript
background. The UI patterns are basically 1:1 with the functional standards in
Javascript land, but you're working with a language that has immutable data
structures by default (I can ditch Immutable.js) and the core library has all
those goody functional helpers I'd include ramda or lodash for.

But in addition to that, shadow-cljs is truly incredible, kudos to Thomas
Heller. When developing, my editor is connected to the browser app through a
repl, and I can switch namespaces and sort of TDD new code, or debug an issue
by cracking into the actual pipeline and interactively spelunking. In JS land,
everything is transpiled, so really you have to put debuggers, refresh the
page or action, catch the debugger, and do stuff that way. If you're
developing new code you can't test out a function if it uses some transpiled
feature, so you write a test or do the debugger thing.

It's takes some dedication to get there, and you don't need emacs even though
it's really fun to learn and get proficient in. I use Spacemacs and am
constantly learning some new package that's installed to help me. I recently
switched from parinfer to paredit, and it's reaalllllly cool. With the repl
driven development and structural editing you can achieve this kinda mind-meld
with your development process. I don't think that necessarily makes this
better than Javascript, but if you're into stuff like that there's a really
high skill-cap with how you can optimize your development workflow.

And really, at it's core to me Clojurescript is like my perfect Javascript.
There were not any new concepts for me to learn as I had been programming
Javascript functionally for some time, it's just everything I wanted in
Javascript without the friction and bolt-on libraries.

For developer happiness, it has a ton to offer, and there's always something
else to dig in to.

~~~
drcode
BTW I've been using the new figwheel-main testing features that were released
a week ago and love them:
[https://figwheel.org/docs/testing.html](https://figwheel.org/docs/testing.html)

------
jakeinspace
Every few weeks I see a Clojure post on HN, and almost always there's a battle
going on in the comments over whether the language is too clever, impractical,
a toy, poorly designed, etc. My experience with clojure (a ~1500 loc library)
has been quite good. I won't say it's intuitive for those coming from
imperative/OO backgrounds, but when it clicked it really clicked. If a team
decides to invest the time to learn clojure(script) for part of their stack
I'd say it's a phenomenal tool. Please don't knock a language because of a
team management failure.

~~~
dustingetz
Code-sharing between ClojureScript and Clojure is a killer app

~~~
saosebastiao
Not really. Although Clojure and Clojurescript are both dynamically typed,
Clojure is strongly typed while Clojurescript is weakly typed.

Clojure:

(+ "1" 1)

ClassCastException java.lang.String cannot be cast to java.lang.Number
clojure.lang.Numbers.add (Numbers.java:128)

Clojurescript:

(+ "1" 1)

"11"

They may be close, but that is all the reason for concern. There are a million
ways that small semantic differences like this can completely fuck you and
leave you in a debugging nightmare. I would rather use javascript, AKA the
worst language ever invented, than a language that claims to be cross platform
but with semantics that change depending on the platform.

~~~
jgalt212
You raise a good point. Is there a linter available that would flag such non-
portable code?

~~~
yogthos
you get a warning from the compiler in ClojureScript when type coercion
happens:

    
    
        cljs.user=> (+ "1" 1)
                ⬆
        WARNING: cljs.core/+, all arguments must be numbers, got [string number] instead. at line 1
        "11"
    

You can also use Spec and Schema to validate data at the edges, so that you
don't end up with unexpected inputs. I highly recommend doing that for any
non-trivial projects.

------
phakding
I like verbosity in programming language. It becomes pretty easy to read the
code, compared to lambdas or other concise languages.

If you are not working for a startup, majority of the time, you will be
maintaining legacy code or bug fixing. I would take easily understandable
verbose code over "clever" concise code every time.

~~~
ken
Studies have found that bug count is roughly proportional to program length,
across languages. Saying you prefer verbosity essentially means you prefer
more bugs. 500 lines is generally less understandable than 40 lines. There may
be cases where terseness can be too extreme, but I don't see it here.

Is there some particular aspect of the Clojure code here that you think is
overly clever, or hard to understand? This Clojure code uses only one lambda,
and in a straightforward way.

I've written a lot of Java, and a moderate amount of Clojure, and if I had to
place a wager on which version had fewer bugs, I'd definitely bet on the
Clojure. Especially if there were the possibility that it was related to
threads.

We could write this in assembly language, and it'd take 50,000 lines, and
probably have lots of bugs. The salient point is not (just) the lower line
count, but that when code is shorter, that's a good indicator that it's
written at a level of abstraction that fits the problem.

~~~
nescoiquid
And studies show that the number of lines of code a developer puts out in a
day is basically constant across all languages. This is usually cited as an
argument for more expressive languages.

But if bug count is proportional to LOC and LOC per day are constant across
all languages, then bug per day will also be constant across all languages.

You can write shit code in any language. I used to think Java made it harder
to write shit code, but the project I'm on right now has made be reconsider
this opinion.

~~~
bunderbunder
If bug count is proportional to LOC and LOC per day are constant across
languages, then bugs per day might be the same. But you'd still expect
features to be getting implemented at a higher rate, and you'd expect a lower
bug count per feature. That's ultimately the metric that's most interesting
from a business perspective.

IME, the bugs are also easier to deal with in the more expressive language.
They tend to be things like faults in the business logic or gross edge cases
that people are likely to catch in code review or QA. Whereas the bugs in
languages like Java seem to typically be really annoying things like off-by-
one errors, comparing Integers with ==, and goofy run-time type errors that
sail past the compiler because of weak static type checking when generics are
at play, and also past code review because people aren't expecting to have to
review for type errors when they're using a static language.

~~~
nescoiquid
> But you'd still expect features to be getting implemented at a higher rate

Yes, I'm inclined to agree, but I actually haven't ever seen a study which
compares the same project implemented in different languages to establish in
toto the variance in SLOC. It could be the case that in the main for the same
project the differences between languages wash out, as different languages may
have different advantages and disadvantages that are more likely to tell on a
substantive project.

What you suggest seems reasonable, but I simply don't know it to actually be
the case.

------
ktosobcy
So... awful and bloated Java code (with javadocs, why?!) compared to short
counter-examples from Clojure. IMHO quite terrible way to compare the two…

~~~
ivan_gammel
Exactly. Java code looks artificially bloated (who uses 8 spaces for
tabulation anyway?!). Class CookieMap is completely unnecessary, for example -
it extends the j.u.Map with couple operations, which could be easily done with
Java 8 streams (looks like it's older version of language - diamond operator
is not used).

~~~
3rdAccount
There could be a counterpart here that Java as a language is so complicated
that people often come up with these design patterns because they THINK they
are necessary.

~~~
dnomad
Except nobody actually writes Java code like this. Nobody implements the Map
interface. It's just stupid code. The myth of Java bloat only serves people
writing silly blog entries and others hung up on "best practices" from 15
years ago. It doesn't seem to have any practical basis.

~~~
emidln
I've implemented the Map interface maybe a half a dozen times in my career in
Java and many more in Clojure (using reify and friends). It's often very
convient to offer a map interface to some remote service or data store as well
as situations where I needed very custom caching or a specialized algorithm.
The harder the thing I'm working on, the more motivated I am to present its
API as something standardized if possible. This lets my users spend their
complexity budget on what feature I'm offering and not on some random API I
threw together.

~~~
dionian
Generally at least in Java or similar languages it's better to compose rather
than implement or extend, i.e. HashMap works fine for most cases, and you may
not really need to implement java.util.Map if you're building something
"special" anyway. For frameworks and library authors, I can see more of an
argument for it, I just generally have never subclassed collections except in
certain cases such as building an LRU map which Java doesn't have.

------
swlkr
This has a great explanation of the clojure REPL. I think a lot of people hear
about the REPL and think, oh my language has a REPL too, no big deal.

Maybe the clojure REPL should be renamed to something else to differentiate it
from other language REPLs

~~~
gumby
Maybe the other languages should rename what they are doing as Read Eval Print
Loop comes from Lisp; read, eval and print don't have the same semantics in
any other language (read especially).

------
Heliosmaster
I use clojure professionally exclusively over the last 4 years. It's awesome.
I can't wait to go back and code everytime.

------
SomeHacker44
Anyone who writes Lisp without taking a moment to add a docstring to every
definition would never get a job offer from me. (Not to mention comments as
appropriate.)

~~~
emidln
I don't think I would not extend a job offer for lack of a docstring,
especially given how trivial most of these functions are paired with their
self-explanatory names. I'd actually rather a namespace docstring explaining
intended scope and api than function docstring for trivial functions. I've
found that function schemas/specs and reasonable names eliminate 90% of
docstring material. Whether the remaining 10% is worth the string above and
beyond the fn name is a case by case deal.

Comments are fine when the code is doing something unexpected or that is very
terse. In other scenarios, comments are just a land mine to be armed when the
code changes and the comment isn't perfectly updated. Bugs largely come from
developer expectations being broken (mostly by one of: misunderstanding data
shape, some API detail, some language feature, or miscommunication on the
feature with the owner) and stale comments are a contributor to this which can
be avoided in many cases.

------
kristiandupont
LISPs seem to me the best candidates for an alternative to the traditional
text editor. I could imagine a mind-map sort of view which would allow you to
expand and collapse sub-trees etc. Does anything like this exist? To me, that
would be reason to experiment with something like Clojure.

~~~
gumby
There is a long history (going back to the 1970s) of structure editors for
Lisp and there was a discussion of them on HN recently. Interlisp and
Interlisp-D were the primary proponents of this approach; I used to use D-Edit
when I worked in Interlisp at PARC, though it wasn't really for me as it
heavily depended on the mouse.

IMHO structure editing emphasizes the _wrong_ part of code development. In
practice collapsing graph structure is less important in code as opposed to,
say, JSON. Whereas you want to be able to use your eye to hop around, and
free-form alignment sometimes helps to show parallel constructs.

Remember also that in a structure editor even your comments have to be part of
the code and can only be inserted in places where an expression won't change
the flow of control (e.g. you can't do (if (* this is a comment*) some-
condition result))

------
z3phyr
Similarly, I would whole heartedly recommend the Common Lisp macro system
(having myself discovering how to use it a few days back). The power it gives
to the programmer is unmatched by any other system I have ever used (except
maybe forth)

~~~
drcode
The Clojure macro system is very similar to the Common Lisp one, arguably with
some improvements- The Lisp languages in the Scheme/Racket family, on the
other hand, have a different (more safety-oriented) approach to macros.

~~~
flavio81
> very similar to the Common Lisp one, arguably with some improvements

Quite the opposite. Common Lisp macros are totally unrestricted, they don't
auto-qualify names with the package. Also, there are user-defined reader
macros in CL, unlike Clojure.

~~~
drcode
Well, auto-qualified names are a matter of taste. I agree 100% though that the
CL reader macro system is a dream.

------
sidcool
Clojure is something I have been wanting to experiment with, but not having
types makes it very difficult for me to reason while coding. It's just me. I
am used to thinking in types.

~~~
lostcolony
So speaking as someone who has been on both sides of that fence (and,
honestly, prefers static or optional typing; Dialyzer for Erlang is probably
my favorite approach there), I think a large part of that comes down to how
types are used.

In a very OO language, where you're encouraged to create a complex type for
every function/method contract (i.e., I have a type of RoomMeasurement, that
internally contains a list of Measurement interfaces, each of which is in fact
implemented as a MeterMeasurement, which wraps a double), static typing is
very, very necessary, because it's not at all obvious what a function takes.
And you need thorough API documentation because how another developer has
chosen to represent things is not obvious (that is, you are trying to
interoperate with a library that doesn't understand your RoomMeasurement, but
does work with just a list of measurements, but they have to be in imperial,
not metric, and how do you get the list of measurements from your
RoomMeasurement, and convert them? Do you have to write a function, is there
one already, does it take the RoomMeasurement, does it take the list of
MeterMeasurement, does it just take a single MeterMeasurement? Etc)

When sticking with simple types, though, it becomes a lot easier to reason
about, and you can get away with just comments, or very slightly more
complicated types. A list of measurements is just a list of ints...but maybe
dropped into a tuple where the first arg is the type (i.e., roomMeasurements =
{meter, [4.22, 5.7, 3.1]} ). And all you have to find/write (and since it's
just data it doesn't matter which because there's no hidden stuff that needs
tweaking) a meterToFoot function, and apply it as a map. I.e. (pseudocode), ->

feet = {foot, map(roomMeasurements[1], meterToFoot)}

Now, is that perfect? No; even if you as a developer choose to apply type
information, another developer can choose to ignore it. But I think that's
rarer; more common is when people don't think to supply type information at
all, and pass around just (per the example), an array of doubles. You can do
that in a statically typed language too, though.

I think the key difference is that people coming from a statically typed, OO
language, to a dynamically typed FP language, can either end up creating the
same complex data types (which are a nightmare to deal with even with static
typing, but doubly so without), or they see all the examples, embrace the
simpler data structures...and then don't actually supply the necessary typing
information.

The reason, then, that I think static typing -is- good, is because it makes it
harder for me to ignore/forget to handle the typing I have provided. That is,
I may have a 'metricToImperial' function, that takes in a type tagged array of
doubles, and a desired type, and determines and applies the appropriate
function. But I can still forget to include a necessary conversion in the
resulting case statement ('whoops, I called it with a lb to g conversion over
here, and I forgot to implement that one'). It's times like that I really like
optional/inferred typing; many places I don't need to check for typing,
because it's obvious, both to the developer, and to the compiler if it can
infer types...but I can still make mistakes, per that. Of course, that brings
me to unit testing...

~~~
sidcool
Thanks for the detailed explanation.

------
olieidel
For web projects, the benefits provided by Clojure on the backend side may not
be very large; there are already good languages with extensive libraries
available (Django, Rails, etc.). It arguably may seem hard to choose Clojure
with its minimalistic libraries when these frameworks provide an "easy" [1]
way to get running with a full-blown admin interface. Furthermore, backend
code by itself tends to be dependency-heavy in the way that you need
dependencies which you'll definitely not write yourself: You need a library to
interface with your database, you need something for cryptography / passwords,
etc.

Looking at the frontend (React) side however, things are different. The
JavaScript ecosystem is a _mess_. From a viewpoint of a React developer, there
are lots of libraries which vary widely in quality. react-router is an
interesting example here, it had 4 (?) breaking changes so far by replacing
the entire api. There's a ton of mental overhead for the normal React
developer trying to write a "simple" app.

Ironically, developers start rolling their own stuff. Instead of using a form
library which tightly couples your components to your redux state (redux-
form), you start writing your own. Instead of coupling your entire views to
graphql via apollo, you start doing it differently, your way.

This is where ClojureScript is a game changer. If your app differs just
slightly from a (very) vanilla CRUD app and whipping some libraries together
doesn't do the trick, you start writing custom stuff. When writing custom
stuff, you want a programming language which is a) well thought through (great
standard library, immutability, sane concurrency) b) predictable and c)
productive. ClojureScript has all three while JS has none.

We (Merantix) are currently developing a medical image viewer in ClojureScript
and had prototyped two separate versions: One in JS with React, another one in
ClojureScript with reagent and re-frame. Even though it is dependency-heavy
(webgl stuff), ClojureScript turned out to be the superior choice: Immutable
data structure at its core which ironically perform better than Immutable.js
and way higher developer productivity due to less random bugs and a more
interactive development (REPL).

Using Clojure on the backend now seemed like an obvious choice: We can reuse
and share code from the frontend and more importantly, all our developers are
"full-stack" in the sense that everyone can at least understand what's going
on "on the other side" (backend / frontend) of the stack as it's literally the
same codebase.

The learning curve is significant but the advantages are tremendous. I
wholeheartedly recommend learning Clojure even if you're not allowed to use it
at your job. It sounds cliché, but it will make you a better programmer for
sure.

[1]
[https://www.youtube.com/watch?v=rI8tNMsozo0](https://www.youtube.com/watch?v=rI8tNMsozo0)
(to understand the meaning of "easy" above)

------
amai
"Clear, readable, and concise code"??

I recommend everyone to once in a while try to debug Clojure code written by
somebody else. Afterwards you will understand that this is a write once, read
never language. It is an unmaintainable mess of overly clever recursive
subroutines. It has some nice experimental ideas for concurrency. But
basically all useful ideas are also available in Java nowadays. I wouldn't
waste my time on it.

------
hellofunk
I really like and use Clojure professionally, but I have become wary of the
extraordinary time I spend dealing with runtime issues because of the dynamic
typing. I hope the future of core.typed is bright. I know it is being very
seriously worked on. It can't come soon enough for me. Nowadays, I prefer to
write in any statically typed language even if it is more line counts, just
for my own sanity.

~~~
pka
For Clojure, check out Ghostwheel [1] - a lightweight DSL for writing specs.

If you want proper static typing though, ReasonML might be a good choice.
Static, compiles to js and native, super easy to learn, _and_ there’s an
experimental Lisp frontend with Clojure-like syntax if you can’t live without
paredit.

[1] [https://github.com/gnl/ghostwheel](https://github.com/gnl/ghostwheel)

~~~
hellofunk
To me, core.spec has valid uses, but not as a replacement for static tying.

~~~
gnl
Author of Ghostwheel here – clojure.spec is certainly not a replacement for
static typing, but it goes a long way to covering many of the same use cases,
in fact longer than one might think at a cursory glance.

With Ghostwheel you write your function specs similar to how you'd write a
type signature and you get automatic generative testing (including higher
order function support) and side effect detection which – when combined with
spec instrumentation (+ the upcoming evaluation tracing for the test
execution) – can often tell you quite precisely where you screwed up in a much
more immediate and granular manner than a simple unit test or mucking about in
the REPL could. It really is a quite different experience from plain Clojure.

That being said, I'd love types in addition to this and I'm keeping a keen eye
on ReasonML.

------
jacknews
The language may be cool, but the syntax highlighting scheme here is less
cool, more 'vibrant'

------
squaredpants
nil is not. nil punning is a band-aid. Otherwise, I love the lang.

~~~
james-mcelwain
On the contrary, nil punning seems exceptionally well suited to a dynamic
language. In general, I'm only burned by nil propagating into Java, almost all
Clojure code seems to handle nil appropriately.

I'm unsure of a good way to represent optionality in a dynamic language
without a static type system. Or rather, doing so with the tools available --
core.match vs real pattern matching -- seems rather un-ergonomic.

I'm curious about your thoughts of what would be better.

~~~
squaredpants
You are right regarding the dynamic language, well, dynamic. I haven't put any
thought into it and I can't come up with a viable solution of eliminating nil
without some type system strategy (Maybe/Either Monads in Haskell, Option in
Rust, etc). It's kind of a bummer, because I love the Lisp dialect of Clojure,
but really appreciate software projects that I've written in the past that
don't crash due to a forgotten potential nil value handling.

You do have a point!

------
zerr
Or just use Kotlin if S-expressions are not for you?

------
Go0the0gophers
Jvm is bloated. So no closure for me. I prefer Go

~~~
yogthos
The claims regarding JVM bloat are largely exaggerated, especially now that
the JVM introduced modules. However, if it's not your thing it's worth noting
that ClojureScript happily runs on Node. Here's an example of how easy it is
to get up and running with [https://github.com/yogthos/mastodon-
bot](https://github.com/yogthos/mastodon-bot)

~~~
dancek
I'm working on my first Clojure project and find JVM to be painful (it starts
very slowly and eats lots of RAM). I'd appreciate any tips on making JVM non-
bloated.

~~~
markc
Don't know about bloat fixes, but what does your workflow look like that JVM
starts are annoying? My Emacs CIDER REPL comes up in 4 seconds (MB Pro 16G)
and it's often many hours (even days sometimes) between restarts.

But yes, slow starts are annoying if you're starting a JVM frequently. Can you
change your workflow so that's not needed? Check out Component, Mount or
Integrant.

~~~
dancek
This is a relevant answer. Thank you.

We're using Integrant already so yes, I can mostly work in a REPL without
restarting. But the restarts are more like 15 seconds. I'll have to measure
and see what's so slow. My rdev platform is Cursive on a 16G MBP.

And while I'm commenting, yes, we have a reason to run on JVM (a lot of legacy
Java code we probably want to interface with later on). So thanks to other
commenters for suggesting Node.js and Common Lisp, but not possible. :)

------
shady-lady
Does error handling not exist anymore?

------
ronnier
Thankfully Clojure will go nowhere beyond a few edge places. I saw a decent
sized project written in Clojure script that had to be rewritten once the
original authors moved on as new hires struggled to get anything done. Small
features took enormous amounts of time.

~~~
sergiotapia
Literally interviewed at a company that is doing the same thing. B series,
clojure/scala codebase, moving away to Elixir Phoenix.

~~~
james-mcelwain
Seems pretty weird to suggest that if juniors are struggling with one niche
language, moving to another would ever be the solution.

~~~
TeMPOraL
I believe that if a company wants to do software by throwing as much bodies at
it as possible, they need to embrace it fully and openly. Going back to pure
Java might be a good idea, as the language enables progress to be made in a
"factory coding" environment pretty much by design.

(Not a judgement; this is a legit use case, even though not an environment I
like to work in.)

