While I've used all of them, some of their listed libraries are a little dated IMO. Of course this true of almost all mature codebases. For the sake of the less experienced, I'd point them to these substitutions in particular: Compojure: Reitit (which they used in the front end too, so migration maybe in progress) would be my preference for backend routing. Component: Integrant takes Component's ideas, but prefers th…
In a lage codebase, the cost of switching is huge, and if older libraries work well, there is often no clear incentive to do so. Clojure in general being very stable and backwards-compatible makes it even more easier to just continue using older libraries. So what if the library is "dated"? If it works well, why not continue using it? (I speak from my own experience)
Tour of our 250k line Clojure codebase
211–220 of 237 posts
Re: Tour of our 250k line Clojure codebase
#212Earlier quoted context omitted.
Lexical shadowing isn't an imperative thing. Imagine the form: (let [a 10 a (+ a a) a (+ a a a)] a) as being like so: (let [a 10] (let [a (+ a a)] (let [a (+ a a a)] a))) Which you can now re-imagine as the functional composition: (+ (+ 10 10) (+ 10 10) (+ 10 10)) So it has evaluation semantics which allow you to reduce it as described in lambda calculus using α-conversion and β-reduction. And this is different to th…
Nice strawman. Now, how would you restructure the let bindings (without the println) at https://clojuredocs.org/clojure.core/let#example-542692c7c02... equivalently? Wouldn't the ordering of the function executions matter? Could you re-order the bindings arbitrarily and get the same result? I don't think you can. That makes it imperative-as-opposed-to-declarative, even though it may not be imperative-as-a-synonym-for…
(let [a (take 5 (range))]
(let [{:keys [b c d] :or {d 10 b 20 c 30}} {:c 50 :d 100}]
(let [[e f g & h] ["a" "b" "c" "d" "e"]]
(let [_ (println "I was here!")]
(let [foo 12]
(let [bar (+ foo 100)]
[a b c d e f g h foo bar]))))))
And now you can simply evaluate it using variable substitution again.The difference is that when you say `(let [a 10])` the variable `a` is a constant now, you can effectively replace all occurrence of it within the scope by `10`, and you can do this at compile time.
That's why people say the symbol `a` is bound to the value 10, and not the variable `a` points to the value 10.
Effectively you cannot change `a` within the scope anymore, all use of `a` in that scope will be equal, and so within that scope you can change their ordering freely.
What I think is confusing you here is that `let` gives you syntax sugar so all the scopes are flattened, but each new binding pair is actually inside a nested scope, and so `a` is still immutable. In this case, it matters almost never, but as I showed, it still can, because in an imperative language you could still try to mutate the variable even in the same expression, and that is impossible to do in Clojure.
Re: Tour of our 250k line Clojure codebase
#213we have a clojure codebase that's about 100k lines. Honestly I'm kinda fed up with it. Certain 3rd party libs we've used have been abandoned. We wrote our own libs for a major framework and it is failing behind. Too many "I'm very clever" functions that are hard to understand and also have subtle bugs.
Re: Tour of our 250k line Clojure codebase
#214I develop and maintain a 60k line Clojure+ClojureScript codebase by myself, so I can definitely confirm that Clojure does allow for smaller teams to maintain larger codebases :-) I also fully agree with this: > Detecting an error when creating a record is much better than when using it later on, as during creation you have the context needed to debug the problem. I made it a rule to perform integrity checks as early…
Love to hear more about this.
My approach has been to convert errors into data and have behavior that deals with conveying these errors in different ways, but it's always felt too complex for the task. I've just got a lot of stuff dealing with handling errors in the different environments (jvm / browser / nodejs / rn) and across async and non-async code.
Re: Tour of our 250k line Clojure codebase
#215Re: Tour of our 250k line Clojure codebase
#216Why hasn't Clojure provide any java.util.function interfaces integration? for language a that's suppose to "embrace the host" to have "ergonomic" access to the host libs because it itself lacks an ecosystem this seems weird.
Re: Tour of our 250k line Clojure codebase
#217Why hasn't Clojure provide any java.util.function interfaces integration? for language a that's suppose to "embrace the host" to have "ergonomic" access to the host libs because it itself lacks an ecosystem this seems weird.
Clojure precedes Java 8.
Re: Tour of our 250k line Clojure codebase
#218Earlier quoted context omitted.
Lexical shadowing isn't an imperative thing. Imagine the form: (let [a 10 a (+ a a) a (+ a a a)] a) as being like so: (let [a 10] (let [a (+ a a)] (let [a (+ a a a)] a))) Which you can now re-imagine as the functional composition: (+ (+ 10 10) (+ 10 10) (+ 10 10)) So it has evaluation semantics which allow you to reduce it as described in lambda calculus using α-conversion and β-reduction. And this is different to th…
Also, I don't think your (incorrect / incomplete) transliteration of the C code example to Clojure demonstrates anything we don't already know; namely that `(inc a)` in Clojure has different semantics from `++a` in C. Of course the Clojure expression you wrote has a different semantic interpretation from the C expression you wrote; they're different expressions. The fact that `++a` is side-effecting doesn't make Cloj…
The declarative nature is that you're declaring that you want `a` to mean 10 within some scope. That's why you say "let `a` be 10 in current scope", that's your intention here, for `a` to be 10.
After you've declared that, it holds true no matter what.
Where as in the imperative style you say: put 10 at place `a`. This is variable assignment, there's a place which is refered too as `a` and inside that place you can set values and change them at will.
At any point you can instruct the language to change what value is at place `a`, there are no restrictions to the instructions you can give.
It's a bit of a oversimplification to claim that imperative programming is distinguished from declarative programming by assuming a lack of ordering in the latter.
Functional programming is not prevented from expressing order, rather it is less able to express random accidental order at the operational semantics level.
At the end of the day, it's very much about the computational model, imperative is based on Turing model, and Functional on the lambda calculus. Because the latter doesn't depend on a global mutable running state, it is said to be declarative, in the sense that what you see is what you get, you don't need to keep track of what the memory currently has to proceed to the next step.
That said, you're right that Clojure supports some forms of imperative programming as well, but let isn't one, your parent commenter pointing to Atom was more on point. That's what you'd need to do to get back a more imperative `let`:
(let [a (atom 10)
a (+ (swap! a inc) (swap! a inc))]
a)
Will now give you 23.Re: Tour of our 250k line Clojure codebase
#219Why hasn't Clojure provide any java.util.function interfaces integration? for language a that's suppose to "embrace the host" to have "ergonomic" access to the host libs because it itself lacks an ecosystem this seems weird.
Re: Tour of our 250k line Clojure codebase
#220Earlier quoted context omitted.
Seconded. I work in a clojure codebase that were trying to get out of. There's just dead libraries everywhere and stuff that maintained by one person that gets no updates at all. That or we just end up making functional "wrappers" around Java libraries and at that point we might as well just write straight Java. Also yea everyone wants to be so damn smart having macros within macros within macros that no one knows wh…
I've been using Clojure for almost 10 years and writing macros has always been discouraged in the community. You don't see too many of them in the wild, and for good reasons. If you're writing macros on a daily basis, you better have a really good reason for it.
Its maddening to just be constantly mentally unpacking this stuff and searching through docs only to find out "Oh its not a clojure thing, someone wrote a macro for this"