
Clojure is Capable - ahungry
http://ahungry.com/blog/2018-12-26-Clojure-is-Capable.html
======
lopatin
So Clojure and CLJS are amazing. If I were to pick a Lisp for a real world
project, Clojure is the obvious pick. The thing is, I'm curious to hear from
people who DO use Lisp in the real world.

I would characterize my experience with it as 1: Really fun. 2: Productive on
a small scale due to the tooling, interop, libraries, and community being
innovative and awesome, and 3: Infinitely flexible, because you know .. it's a
Lisp. Development becomes like doodling on a piece of paper, like a stream of
consciousness.

But I ended up choosing Scala for a recent, fairly large, project of mine and
I'm convinced it was the right decision. Without a static type system I would
have simply lost a handle on the complexity of the project. And this is while
working alone (the case where Lisp traditionally has the upper hand).

A few questions for people who use Lisp in production. No need to answer all,
just things I'm curious about:

* During refactoring, do you feel more flexible, or less? I can see both sides to the argument. On one hand, static types are more rigid, and getting something to compile during a refactoring can be a pain. Often for the right reasons, but often it's due to some annoying technicality in the type system. But once you get there, "if it compiles, it runs" is a cliche that is pretty accurate in my experience. On the other hand, with a dynamic type system, you can slowly evolve a code base and see incremental progress during a refactoring, but I can never get over the dreadful feeling of "what did I forget?". Has clojure.spec worked out in terms finding bugs during refactoring/maintenance and general QA?

* Do you make use of the Lispy-ness (Homoionicity, macros, code is data, etc ...) or is it generally an anti-pattern to use these language features in daily development?

* Do you wish you had static types? If so, have you tried gradual typing like Typed Clojure? And if you don't miss types, why? I remember someone saying of Typed Clojure "We went to Clojure to get away from types!". And I'm still trying to understand the mindset.

I often have the feeling that I'm not smart enough to use Lisp. If I had 2x
the brain power, I would be able to use macros to conjure up some black magic
in 100 lines what would take a mortal 5k in a lesser language. But even if I
manage such a feat, I couldn't imagine bringing on another developer to that
codebase.

~~~
badsavage
We've used Clojure in production for years.

1\. "Has clojure.spec worked out in terms finding bugs during
refactoring/maintenance and general QA?" \- Spec worked out as a much more
useful library than we expected. You could describe any data structures with
it, then validate, generate, regenerate it. I think spec is a must have for
any Clojure programmer from 2018.

2\. "Do you make use of the Lispy-ness" Macros are super useful sometimes, but
in general we try to avoid them, because we prefer to solve everything with
just pure functions. Homoiconicity and the "code is data" aspect are must
haves in any programming language after you realised their power.

3\. "is it generally an anti-pattern to use these language features in daily
development?" macros are feel like cheating sometimes, and we fear the
consequences, but homiconicity is useful for us on a daily basis.

4\. "Do you wish you had static types?" We prefer to create our own "types" up
to complex data structures. A built-in type system seems like a downgrade from
here.

5\. "I often have the feeling that I'm not smart enough to use Lisp." I think
you are just scared from the unknown, Clojure is an alien technology compared
to most of the currently popular languages.

Any kids could learn programming in Clojure due to its simplicity and the REPL
driven workflow. Its an interactive experience and very likeable.

~~~
pvinis
Can you elaborate a bit on your own "types"? I am very interested and
intrigued.

~~~
badsavage
Yes, I could show you a simple example:

Let's create our own 78 digits long string type:

(def hex-digit (set "0123456789"))

(defn hex-digit? [x] (contains? hex-digit x))

(defn hex-str? [s] (every? hex-digit? (seq s)))

(s/def ::hash (s/and hex-str? #(= (count %) 78)))

::hash is now registered, now we go to the REPL:

(s/valid? ::hash "23179372")

;;=> false

(s/valid? ::hash
"nope93721907914920047210715459933122004671648400678953445710500236944435987060")

;;=> false

(s/valid? ::hash
"231793721907914920047210715459933122004671648400678953445710500236944435987060")

;;=> true

That's cool, but we want to generate too:

(defn hex-str-gen [n] (let [digit (g/elements hex-digit)] (g/fmap
clojure.string/join (g/vector digit n))))

(s/def ::hash (s/with-gen (s/and hex-str? #(= (count %) 78)) #(hex-str-gen
78)))

We extended our ::hash spec with a generator function, so we can do this now:

(g/generate (s/gen ::hash))

;;=>
"310584715385467847758653938894742415543975445356609397864862925839413265904779"

(+ Pro tip: you could spec functions too)

Learn more about spec here:
[https://clojure.org/guides/spec](https://clojure.org/guides/spec)

~~~
dpg23
I don't fully get the syntax (I've had a tiny bit of Scheme experience) but
wow that's awesome.

------
akhilcacharya
This is wonderful! Just what I was looking for in terms of real world Clojure.

------
cube2222
I really like clojure as a language, but it seems like the bus factor is very
problematic.

Observing the community I see rich hickey, as well as cognitect as a whole
SPOFs, which hold me back from devoting more time to it.

~~~
yogthos
There are many companies that have invested their core business in Clojure,
and the language has been around for a decade. There is absolutely no risk
here. If Rich disappeared tomorrow, there would be no lack of people ready to
step in and continue working on Clojure. In fact you can already see that
happening with ClojureScript where there is a lot of community involvement in
the development.

------
agumonkey
Interesting blend of raw clojure, java interop, os integration... very very
nice

------
StreamBright
I still miss the proper Java interop. Calling SAMs is not fun and I could not
figure out the proper way of make it work from Clojure. We ended up needing to
write a Java wrapper.

~~~
unlogic
Do you mean having to reify java.util.function.* classes?

~~~
StreamBright
Like this for example: [https://stackoverflow.com/questions/53731940/using-
static-me...](https://stackoverflow.com/questions/53731940/using-static-
methods-from-clojure)

------
sidcool
For any project bigger than a certain threshold, I always prefer Scala over
Clojure or even Java. For smaller projects, Clojure fits well.

~~~
yogthos
Every large project can, and should, be broken down into small isolated
projects that are developed and maintained independently.

Furthermore, I think that writing software using monolithic design is bad
practice. It creates coupling within the project, and it makes it harder to
reuse code. It's harder to onboard new people, and so on. However, the biggest
problem is that it creates a lot of communication overhead for the team. The
more people you have working directly with each other, the more emails,
meetings, and other kind of communication you end up incurring.

Splitting things up into small components that are each maintained by separate
teams of 5~6 people is a far more productive way to maintain large software
projects than to create monoliths.

~~~
sidcool
Your arguments hold true for a regular run of the mill software project. Many
real world projects are hardly ideal.

~~~
yogthos
Using static typing as a crutch to work around poor project architecture
doesn't actually solve the problem. Also, there is a question whether static
typing is what contributes to projects being written in a monolithic style in
the first place. I often see this to be the case for projects written in
statically typed languages, while ones written in dynamic ones tend to be
broken up much more aggressively from the start.

------
shadowmint
> Interactive REPL based workflow (think of programming in Clojure like
> playing a difficult video game based on trial and error with emulator save
> states vs without).

What?

I'm not sure I understand what that's even trying to say?

Is it _trying_ to say, think of _programming_ as playing a difficult computer
game, and using a REPL like having save states?

...because it sounds like you are saying programming in Clojure is like
playing a difficult video game based on trial and error, which is a really
rubbish endorsement.

> To touch on the video game analogy - imagine playing one of the old Mega Man
> games, Dark Souls, or Battletoads. Now, imagine that instead of getting
> feedback (a death) that results in a hard restart at the beginning, you
> instead just get taken back one action / event that caused an error (a
> single failed function call in REPL). That's like loading a save state, and
> if you like easy mode, you'll love the Clojure REPL.

?

mmm... but if your analogy is 'this is like doing something _really hard_ ,
but you can timestep back to save state so it's not so bad', then you're still
basically saying writing Clojure is really hard.

I don't think that's really a fair thing to say about Clojure, it's just a
terrible analogy.

Expressing things in Clojure is what it excels at; if you want an anology the
REPL is more like a minecraft sandbox that you just continually keep building
in, rather, than planning out your structure on paper before you start
playing.

~~~
StreamBright
Well most of the people write code in small chunks that yields to the
following workflow:

\- write code

\- compile & run, see if it works

Just keep repeating this until you achieved what you want.

In REPL this is exactly the same you just have a much faster way of trying out
a concept.

I think this is pretty simple to conceptualise.

~~~
shadowmint
Obviously, but that's not the point I was making.

...but how does that (what you wrote) == "think of programming in Clojure like
playing a difficult video game based on trial and error with emulator save
states"?

~~~
StreamBright
\- each iteration in the repl is a state that you can preserve ("save")

\- playing the vide game is trying out new states

I guess this is what OP was thinking about.

