This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-05-26
Channels
- # aleph (1)
- # announcements (9)
- # aws (6)
- # babashka (18)
- # babashka-sci-dev (25)
- # beginners (79)
- # calva (30)
- # cider (34)
- # clj-kondo (25)
- # cljsrn (6)
- # clojure (26)
- # clojure-australia (1)
- # clojure-europe (6)
- # clojure-norway (1)
- # clojure-poland (6)
- # clojure-uk (3)
- # clojured (2)
- # clojurescript (14)
- # datomic (19)
- # events (1)
- # google-cloud (1)
- # gratitude (2)
- # helix (1)
- # hyperfiddle (2)
- # interceptors (1)
- # jobs (17)
- # joyride (96)
- # leiningen (5)
- # lsp (20)
- # minecraft (2)
- # nbb (5)
- # other-languages (1)
- # re-frame (34)
- # releases (2)
- # shadow-cljs (15)
- # spacemacs (1)
- # xtdb (19)
Good day, TLDR: What's the 20% of Clojure needed to build 80% of CRUD apps? I'm a novice programmer interested in web dev, basic sites with an index page, show pages, item filtering, search, auth, and a db. (zero plans for a career in development). I know basic HTML, CSS, JS, and SQL with a slightly deeper understanding of Elixir and Phoenix. I'm drawn to the simplicity of Clojure as an escape from frameworks, arbitrary syntax and OOP. Clojure wise, I've done a short udemy course and a few other small tutorials. For my main learning resource I've settled on (https://courses.packtpub.com/courses/clojure) I found it to be the most practical and up to date Clojure book, that doesn't teach frameworks. But at 700+ pages, with everything from hello world to macros it feels comprehensive to a fault. My naive intuition is that there's a subset of Clojure concepts that if learned, would give me out-sized returns compared to the rest. I'm including a list of the books contents and I'd be grateful for any feedback on which chapters/concepts could be strategically ignored from the POV of an aspiring Clojure CRUD maker? 1. Hello REPL! (def, let, booleans, repl basics) 2. Data Types and Immutability (maps, sets, vectors, lists) 3. Functions in Depth (destructing, higher-order functions, multi-methods) 4. Mapping and Filtering (map, filter, lazy evaluation) 5. Many to One: Reducing (zipmap, group by, reduce) 6. Recursion and Looping (loop, doseq, recur, take, repeatedly, iterate) 7. Recursion II: Lazy Sequences (lazy-seq, thunks) 8. Namespaces Libraries and Leiningen 9. Host Platform Interoperability with Java and JavaScript (exceptions, errors) 10. Testing 11. Macros 12. Concurrency (pmap, futures, atoms, refs, agents) 13. Database Interaction and the Application Layer (JDBC, SQL, Connection pools) 14. HTTP with Ring 15. The Frontend: A ClojureScript UI (reagent, HTTP endpoints) *cross posted on clojureverse
First off, I'll say that, unfortunately, Packt books are generally terrible. They're poorly edited, often contain a lot of bad code and mistakes, and can be pretty outdated.
"Getting Clojure" is heavily recommended here for beginners. "Programming Clojure" is also a good book to get you up and running with Clojure. And then, as a follow-on book to either of those, "Clojure Applied" (but don't get too hooked on the emphasis it gives to records -- that's something the author would change for a second edition).
For building simple web apps, backed by a database, I'd recommend either: β’ Ring, Compojure, Component, Selmer, next.jdbc, or β’ Ring, reitit, Integrant, Selmer, next.jdbc. There's an example of the former at https://github.com/seancorfield/usermanager-example and it links to an example of the latter, created from the former, by the community.
All that said, to your original question, I'd say for what you are aiming at -- simple, database-backed web apps -- you would skip 7, parts of 9, 11, 12, 15. I'd recommend learning the Clojure CLI / deps.edn
/ tools.build
instead of Leiningen (partly because they're are a set of small, composable tools -- so they have a more modern design -- and they're all from the core Clojure team so they're documented on http://clojure.org). The usermanager example is deps.edn
-based, but doesn't have an example of tools.build
for creating a standalone JAR to run with java -jar
... yet...
(it's updated to include build.clj
and show how to use tools.build
to run tests and build an uberjar)
I would disagree about packet, there are some really good books (but a few bad ones too -it mostly depends on the author) The Clojure Workshop book is a good general Clojure book and has accompanying videos that help convey some of the concepts in action. Some libraries may be considered a little dated (disclaimer: I recorded some videos for this book but didn't write any of it)
For a beginner learning to build a server-side web service, then ring (adaptor, handler, middleware) is the underlying concept. It translates http requests into Clojure maps. On top of ring there is routing, for which compojure is very common, with Reitit and other libraries offering a data-centric routing approach
Hiccup provides a simple approach to generating content by writing Clojure data structures. Hiccup syntax is very common. Selmer uses an html templating approach, which can be better for larger amounts of content or for people used to working with html/css
For relational database (SQL) next.jdbc library is the defacto library, writing queries using SQL syntax. with-open will manage dB connections cleanly and next.jdbc supports transactions dB connection pool libraries (although they don't seem to be needed in the case you define)
If your not deploying production systems, then it seems security is not relevant (so can skip things like buddy and friend)
From the list of chapters, I'd start with chapter 14 and only read small bits from other chapters if required next.jdbc has a very good set of docs, so maybe an alternative to chapter 13
There are some simple project walkthroughs of server-side projects at https://practical.li/clojure-web-services/
For the benefit of anyone else interested in this topic that doesn't frequent clojureverse. I'll repost my revised plan here: Thanks to the great advice here and elsewhere, I've put together a new plan that I'm happy with. 1. Per @U05254DQM suggestion replace chapter 13 with the next-JDBC docs. 2. Complete chapter 14 3. Switch from Leiningen to the Clojure CLI 4. Follow @U0K064KQV "Build it and you will learn" suggestion, back-filling knowledge gaps as they arrive . 4b. Study @U04V70XH6's demo repo and others I can find.
@U01HBARPCFL If you have any Qs about the next.jdbc
docs, feel free to ask in #sql. If you have any Qs about the Clojure CLI or deps.edn
, ask in #tools-deps. build.clj
in #tools-build. My demo repo -- DM me. Or ask any of these Qs in #beginners if you think they're more general, rather than specific library/tool-related. Good luck!
Oh any coming in here - make sure to use the newest hiccup. 2.0.0-alpha3 or something. O.G. hiccup has injection vulnerabilities by default hiccup2
does not
Like the way ?
is stuck onto functions that return a boolean is there some idiom for functions that perform assertions?
assert
Nothing I'm aware off. Assertions are mostly used for checking invariants and I don't remember seeing any special naming convention for t if that
like (Integer/parseInt "00ff00" 16) ;;=> 65280
?
no the output should be a string. https://www.online-toolz.com/tools/decode-hex.php this here does that
well what is the encoding then?
it's a string like this "\\x7b2246726f6d223a22307832663437656235623238316132363939646339363865643430366362653430653638626631303634222c22546f223a22307836653437663631666631646161643738376663303963353135366163323563373266373838646435222c2256616c7565223a3731393131333133353530353432323432313637337d"
it appears that is converting each pair of hex values to a character in ASCII encoding
I mean that's what the page is doing, not sure if that's what you want
(->>
payload
(drop 2)
(partition 2)
(map #(apply str %))
(map #(Integer/parseInt % 16))
(map #(Character/toString %))
(apply str))
Hi all, I am trying to write a cronjob and using sh
however I keep getting an odd error which I canβt get my head around.
Code:
(shell/with-sh-env {:PGPASSWORD (env :production-db-password)}
(sh "pg_dump"
(str "--dbname=postgresql://"
(env :production-db-user)
"@"
(env :production-db-instance)
"/amp-compute-products -W")
"--file=production.sql"
"--format=tar"))
Error:
(shell/with-sh-env {:PGPASSWORD "pass"} (sh "pg_dump" (str "--dbname=postgresql://" "username" "@" "localhost:5432" "/amp-compute-products -W") "--file=production.sql" "--format=tar")) - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
Could anyone possibly shine some light on what Iβm doing wrong here?it's good to always build from some small form that works
(clojure.java.shell/sh "ls" "-a")
for instance this still works
sh - failed: vector? at: [:fn-tail :arity-n :bodies :params] spec: :clojure.core.specs.alpha/param-list
(sh "ls" "-a") - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
think i found my issue - I was putting it in a function and didnβt have []
after the func name
we could probably enhance the docstring of shell/sh. it's not obvious that the commands should precede the options
there are very few places of unrestrained 'mutation' in clojure. Most 'mutation' uses something called the "succession model", where you apply a function to the contents of the box, not simply bang on the box
memoize makes no other guarantees (such as computing a function at most once for a distinct set of args)
delay is also thread-safe
As a Clojure user, my impression was that memoize, just like swap!, require you to apply it to only pure functions. So it doesn't really matter if you compute it twice with the same args, it will cache the same result. But delay can be used with side-effects, its one of the advantage of delay, because it will execute it only once and cache the result.
I guess it plays on what is meant by thread safe, you still need to be aware of thread concurrency issues as a user in how you use the constructs. I see them more as thread safety constructs, not thread safe functions of their own, but mechanism you can use to implement some behavior in a thread safe manner.
For side effect, and memoize like caching behavior, you can use core.memoize and core.cache instead, see: https://github.com/clojure/core.memoize and https://github.com/clojure/core.cache
> Naive cache (memo) that duplicates the functionality of Clojure's memoize function but, unlike the built-in memoize function, ensures that in the case of concurrent calls with the same arguments, the memoized function is only invoked once
You can see the doc for memoize says:
> Returns a memoized version of a referentially transparent function.
So it's meant to be used with pure functions only.
The doc-string of swap! also mentions that f should be side effect free.
Whereas the doc-string for delay mentions no such restrictions.
The doc-strings are not always the most consistent, but if there is no restrictions mentioned on f
, typically that means its fully thread safe in all cases. But if it mentions a restriction, well you have to make sure your f
abide by it or know what you are doing otherwise.
I'm connecting to a basic socket repl via an ssh tunnel using this as a "client": https://github.com/vlaaad/remote-repl/blob/master/src/vlaaad/remote_repl.clj and the following alias to start the server:
{
;...
:aliases
{:repl-server
{:exec-fn clojure.core.server/start-server
:exec-args {:name "repl-server"
:port 5555
:accept clojure.core.server/repl
:server-daemon false
}}}}
Randomly every few hours, my server jvm will die with no obvious error output. My current assumption that some connectivity issues with the client are causing the server to die.
I'm running a more complicated app than is describe here and this issue is mostly caused by something else in the stack, just want to sanity check that nothing obvious in this basic setup should result in an issue with a client killing the server, right?Nope, it definitely could be that, been stumped by this for a few days. It happens too infrequently and fails so silently that I haven't got very far figuring it out. Thanks for the tips that's a place to start!
I am fascinated by transducers. I have a question. Why, since the advent of transducers, doesnβt (map f coll)
get defined as (transduce (map f) conj coll)
Is this just a preserve-existing-behavior kind of thing?
that would change the behavior of (map f coll)
:
pivot=> (type (map inc (range 10)))
clojure.lang.LazySeq
pivot=> (type (transduce (map inc) conj (range 10)))
clojure.lang.PersistentVector
pivot=> (conj)
[]
so lazy-ness is the issue?
βtransduce will immediately (not lazily) reduce over collβ
hmm so whatβs the equivalent of lazy-transduce
sequence
is an incrementally computed transducer context, but it's still not as lazy as lazy seqs
ok, thanks for discussion
It fully computes intermediate results
A good example is an expanding transducer like mapcat - if the map part produces a coll of 1000 elements, with sequence you will compute all of them at the step (including potentially infinite intermediate steps), whereas lazy seq is fully lazy there (modulo chunking)
no. There's more info about this in https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/InsideTransducers.md