Fork me on GitHub
#beginners
<
2020-11-08
>
Daniel Östling10:11:31

Is there a commonly accepted way to structure ”large” programs with multiple namespaces etc? Is there documentation/guides? :)

valerauko10:11:34

I don't think there's a generally applicable way, no. I have my own pattern for web apps and try to split namespaces/folders by areas of responsibility in general. Here some examples: • a smallish library https://github.com/valerauko/vuk • a smallish website (actually the first clojure one i ever made) https://github.com/valerauko/books • a way bigger service https://github.com/valerauko/kitsune I hope you can use these for reference. (All are using leiningen so source code lives in src.) If you have questions ask away!

seancorfield18:11:59

@danielostling Look for talks and other material out there about "functional core, imperative shell" and Domain-Driven Design (with Clojure) for some guidance on structure. Also, Clojure Applied is probably the most useful book in this area. But, yeah, overall there's not a lot of material out there -- partly because Clojure has historically attracted more senior developers who've already internalized a lot of architectural thinking.

seancorfield18:11:13

At work, we have over 100K lines of Clojure, stretching back across a decade. We were all learning in the beginning so we hadn't really figured structure. Over time, our code organization has changed -- which means, unfortunately, the various apps in our code base are not very consistent, and as we continue to add functionality, we are constantly refactoring, and trying to improve the structure.

Daniel Östling19:11:20

Thanks @seancorfield 🙂 Yeah, I’ve been doing “hexagonal design”/DDD in other languages, I guess that could work in Clojure as well.

Lemonski16:11:14

A weekend of Clojure and i feel very overwhelmed still dont know how to create a working programm even if its only say hello and work as a very unnecessary calculator 😄 I am reading the "brave Clojure" book. are there more practical examples out there?

dorab17:11:11

Hi @U01EENTKD0C do you have a REPL working? Since you say you are using Brave Clojure, I presume you have lein installed? If so, does lein repl give you a usable REPL? Just trying to figure out where you are in your journey.

vncz22:11:55

@U01EENTKD0C I had the same problems initially, and it turns out that my initial idea "I'll learn the basic Clojure stuff and jump directly in trying to create a CRUD App" did not work at all. It takes some time to learn the language itself and the best baby step after figuring out the basics is to implement "data" only programs (such as algorithms and stuff) to get acquainted with the collection library. Once you're there, you'll likely understand what more and be able to write a web app with Pedestal for example. Ultimately, although the Clojure for the brave is a good book, it did not help me that much. What really helped me was this video: https://www.youtube.com/watch?v=P76Vbsk_3J0 I hope that's gonna help in your journey! 🙂

Lemonski08:11:30

@U0AT6MBUL i have leiningen installed and working. Lein repl and cider repl in emacs also working. I am basically trough chapter2 of brave. And was able to create simple stuff like enigma decription in repl etc. Rly only simple stuff. But still not sure where to go from there

Lemonski08:11:02

@U015VUATAVC thank you for your advise i have the feeling brave is more like a reference book which is also good to have in the beginning, but yah i will take a look into the video.

dorab17:11:57

@U01EENTKD0C So I probably misunderstood your question. I thought you were having problems writing something simple. So, I presumed it was because your setup was not complete. It now sounds like you have the setup just fine, and are looking for what to do next. In that vein, what @U015VUATAVC says is good advice. If you're not already experienced in functional programming, then writing functions that can be composed together is a good next step. Previous expose to OOP can be detrimental since it causes you to un-learn some habits in order to do good FP. Most of my programming is just functions which I just call from the REPL. The only time I ever make a stand-alone command-line program out of the function is when I need the function to be run by someone else who is not familiar with a REPL and does not want to learn about the REPL. If you have not already, you could try reading/watching articles/videos about REPL-driven-development. You can start at https://clojure.org/guides/repl/introduction and https://clojure.org/guides/repl/annex_community_resources for other resources. FP/REPL are sometimes harder to learn because of previous exposure to OOP. But, once you persevere and get to the ah-ha moment, you will be changed! If I'm telling you things you already know, you can ignore me 🙂

seancorfield18:11:59

@danielostling Look for talks and other material out there about "functional core, imperative shell" and Domain-Driven Design (with Clojure) for some guidance on structure. Also, Clojure Applied is probably the most useful book in this area. But, yeah, overall there's not a lot of material out there -- partly because Clojure has historically attracted more senior developers who've already internalized a lot of architectural thinking.

cdpjenkins18:11:39

Is there an integer division function in Clojure that truncates towards negative infinity (rather than towards zero, as quot does)?

andy.fingerhut19:11:41

I found a StackOverflow question via a quick Google search that links to this Java method that may be of use to you: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorDiv-int-int-

andy.fingerhut19:11:04

Google search terms I tried: java division round to negative infinity

andy.fingerhut19:11:47

I am not aware of anything built into Clojure that does what you wish.

cdpjenkins19:11:26

Awesome thanks, I’ll take a look… I didn’t think to look at Java 🙂

andy.fingerhut19:11:01

I believe that Java's integer division also rounds towards 0, which might be the reason that Clojure's quot does so.

👍 3
seancorfield19:11:17

Even in Java, it's a fair bit of work, depending on whether you need it to work with long as well as int.

seancorfield19:11:49

(based on that same SO answer that Andy found)

andy.fingerhut19:11:08

There is also a variant of the floorDiv method that takes primitive long args instead of int: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#floorDiv-long-long-

cdpjenkins19:11:19

Well the best I could come up with prior to asking here was (int (Math/floor (/ x y)))floorDiv feels marginally less painful 🙂

andy.fingerhut19:11:24

I would guess there are versions for float and double args, too.

andy.fingerhut19:11:15

The Java math libs are pretty extensive, even the stuff that comes with every JDK without requiring a separate Java lib. If you go for 3rd party Java libs, it gets even more huge.

👍 3
cdpjenkins19:11:57

Many thanks for the help… I really appreciate it 🙂

Buidler23:11:30

I'm coming to Clojure from Django where I've learned to love the ORM. What is the recommended approach for using SQL in Clojure? Is something like Toucan the right place to start? https://github.com/metabase/toucan

practicalli-johnny23:11:19

I don't believe there is a single approach (no sliver bullet... and why would there be?) https://github.com/seancorfield/next-jdbc - if you want to use SQL code https://github.com/seancorfield/honeysql - if you want to write Clojure data structures rather than SQL https://www.hugsql.org/ - aims to make a clean separation between Clojure and SQL code. An toucan is already mentioned in your question. which as is says is as close as it gets to ORM without the O

seancorfield23:11:47

@U05254DQM You shouldn't really recommend clojure.java.jdbc these days. I'm not updating it any more.

practicalli-johnny23:11:22

pasted the wrong link...

3
seancorfield23:11:16

@randumbo Since we don't have objects, we tend not to use any sort of ORM in Clojure.

seancorfield23:11:18

https://github.com/seancorfield/next-jdbc is the best JDBC wrapper to start with. You'll see https://github.com/clojure/java.jdbc in a lot of books/tutorials (which I also maintain), but that isn't getting updates any more.

seancorfield23:11:59

There's a #sql channel where you can ask SQL/JDBC-related questions. If you want to work with SQL in .sql files, take a look at HugSQL (which also works with next.jdbc and clojure.java.jdbc). If you want a DSL to programmatically build SQL from data structures, take a look at HoneySQL. There are #hugsql and #honeysql channels for those too.

Buidler23:11:42

Which approach would you recommend, of those two?

seancorfield23:11:50

At work, we use HoneySQL very heavily because we have a lot of complex queries that are built programmatically.

seancorfield23:11:07

Some people like to keep all their SQL in separate .sql files which is why I mentioned HugSQL.

Buidler23:11:05

Do those tools have protections against low-level stuff like injection attacks?

Buidler23:11:40

What are your thoughts on Toucan? It says it's built on HoneySQL, although still seems to use clojure.java.jdbc.

seancorfield23:11:21

I think it adds a layer of unnecessary complexity, personally.

Buidler23:11:05

Understood. I get the sense that it's against the Clojure way.

seancorfield23:11:47

I'd definitely recommend starting with basic libraries so you understand what's going on and then composing the libraries you like best. Choose simple over easy.

Buidler23:11:56

Looking at HugSQL, it's a bit concerning that it doesn't look like it's actively maintained. Last commit was Sept. 30, 2019.

seancorfield23:11:26

I've worked with a lot of ORMs in other languages -- dating back to full-on object databases in the '90s -- and I much prefer Clojure's approach.

seancorfield23:11:30

@randumbo You'll get used to a lot of Clojure libraries seeming to be "unmaintained" -- Clojure favors small, composable libraries, that tackle one problem and therefore can often be "done".

seancorfield23:11:23

Backward compatibility is also very important in Clojure, so don't be put off by pre-1.0 versions, or things seeming to stay alpha for a long time 🙂

Buidler23:11:49

heh, I've read that in many places, but I still find it kinda hard to believe.

Daniel Östling08:11:45

It’s true for many Common Lisp libraries as well.

seancorfield23:11:48

At work, we started with Clojure 1.3 and went to production on alpha 7 or 8. We've run pretty much every release of Clojure in production in alpha versions. Stability is important in the Clojure world.

Buidler23:11:02

If I don't need complex queries for now, would HugSQL be a better choice for the time being?

seancorfield23:11:14

(we're running Clojure 1.10.2-alpha2 in production right now with 1.10.2-alpha3 in QA and 1.10.2-alpha4 in dev)

seancorfield23:11:37

Start with just next.jdbc and see how you get on.

seancorfield23:11:00

If you decide you prefer your SQL in external files, then take a look at HugSQL.

seancorfield23:11:22

It's not an approach I like, so it's not really my place to recommend you do or don't use it.

Buidler23:11:22

Going to have to brush up on my SQL a bit. Been a while since I've written raw SQL.

seancorfield23:11:07

For simple CRUD, you won't need it: next.jdbc (and clojure.java.jdbc) can save hash maps to tables without SQL and read them back.

Buidler23:11:28

I'll do so anyway, but that's good to hear.

seancorfield23:11:54

(next.jdbc.sql/insert! ds :table {:name "Sean" :country "USA"}) for example

seancorfield23:11:28

(next.jdbc.sql/get-by-id ds :table 123) (defaults to using :id but you can override that) -- returns a single hash map

seancorfield23:11:15

(next.jdbc.sql/find-by-keys ds :table {:country "USA"}) returns a vector of hash maps for everyone WHERE country = ? and "USA" as that parameter.

Buidler23:11:30

Looks pretty straight forward indeed. Thanks @seancorfield, I'm going to work on integrating that into my Fulcro project on the backend.