This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-08-06
Channels
- # aleph (1)
- # announcements (2)
- # beginners (109)
- # calva (48)
- # cider (25)
- # clj-kondo (38)
- # cljdoc (13)
- # clojure (203)
- # clojure-europe (23)
- # clojure-gamedev (3)
- # clojure-nl (3)
- # clojure-uk (7)
- # conjure (2)
- # data-science (1)
- # datalog (2)
- # datomic (7)
- # deps-new (16)
- # depstar (2)
- # docker (2)
- # fulcro (67)
- # graalvm (58)
- # honeysql (16)
- # java (2)
- # jobs (2)
- # jobs-discuss (2)
- # kaocha (4)
- # lsp (82)
- # malli (23)
- # off-topic (35)
- # polylith (18)
- # practicalli (5)
- # releases (1)
- # remote-jobs (1)
- # shadow-cljs (15)
- # sql (17)
- # timbre (1)
- # tools-deps (24)
- # vim (20)
- # xtdb (9)
Hi, Im working on a http request that need to be have the following query parameters. ?where=customerId="aa238c96-189c-4740-a881-8d16dbc51001" and that needs to be url encoded into this ?where=customerId%3D%22aa238c96-189c-4740-a881-8d16dbc51001%22 but I´m not able to figure it out. Using Hato as http client library. Suggestions?
(hc/get "
did you tried this?
sent request contains
:query-string "where=customerId%3D%22aa238c96-189c-4740-a881-8d16dbc51001%22"
I know it's application specific, but what flags do y'all usually set when running a clojure app in production?
I would make sure you have the correct garbage collector for your needs: G1 (default), parallel (throughput), or ZGC
if you're in containers, prefer using MaxRamPercentage rather than absolute Xmx memory settings
We are using kube, so the ram available to the container is already limited, so not setting xmx. The garbage collection flag is one I hadn't thought of. I was thinking about stuff like whether the default heap settings are adequate, etc
> the ram available to the container is already limited, so not setting xmx xmx has a default value, it does not default to "all the memory on this machine", so you need to use the flag to ensure all the memory you provide is used
oh I guess ghadi already said that
Yeah, the typical default for max heap is 1/4 of available memory - both in containers and host OS.
Other than that I'd avoid tweaking other settings, at least iniitally.
You may find other flags useful to have debugging info available when a problem happens, e.g.
• -XX:+HeapDumpOnOutOfMemoryError
• -XX:-OmitStackTraceInFastThrow
Hello, can test.check be used with ClojureScript?
Thanks!
rewrite-clj uses test.check in cljc tests, ex: https://github.com/clj-commons/rewrite-clj/blob/main/test/rewrite_clj/custom_zipper/core_test.cljc
Quick question: if I want to read a file from the users home dir, a Config file like a .zshrc, how do I get clojure to find that dir?
@dev-hartmann (System/getProperty "user.home")
@seancorfield thanks, will try that!
There are several properties that are useful: you can get a hash map of all of them with (System/getProperties)
:
dev=> (-> (System/getProperties) (keys) (sort))
("clojure.basis" "clojure.core.async.go-checking" "clojure.tools.logging.factory" "file.encoding" "file.separator" "ftp.nonProxyHosts" "http.nonProxyHosts" "java.class.path" "java.class.version" "java.home" "java.io.tmpdir" "java.library.path" "java.protocol.handler.pkgs" "java.runtime.name" "java.runtime.version" "java.specification.name" "java.specification.vendor" "java.specification.version" "java.vendor" "java.vendor.url" "java.vendor.url.bug" "java.vendor.version" "java.version" "java.version.date" "java.vm.compressedOopsMode" "" "java.vm.name" "java.vm.specification.name" "java.vm.specification.vendor" "java.vm.specification.version" "java.vm.vendor" "java.vm.version" "javafx.runtime.version" "javafx.version" "jdk.debug" "line.separator" "log4j2.configurationFile" "logged-future" "os.arch" "os.name" "os.version" "path.separator" "socksNonProxyHosts" "sun.arch.data.model" "sun.boot.library.path" "sun.cpu.endian" "sun.font.fontmanager" "sun.io.unicode.encoding" "sun.java.command" "sun.java.launcher" "sun.jnu.encoding" "sun.management.compiler" "user.country" "user.dir" "user.home" "user.language" "user.name" "user.timezone" "vlaaad.reveal.prefs")
That's in our dev setup so some of those are set by us at startup of the REPL but most of them are standard/built-in for Java itself.Ah, that’s really useful, thx for the info! My Java knowledge is somewhat lacking
If this is the case, then you may want to know about the File type in Java, since this gives you portable access to a file you want to open.
The Clojure approach for talking to files on the JVM is in the
namespace. You can then create a File
object with
, which can be used as the argument for opening a file, or just sending to the slurp
function.
e.g.
(require '[ :as io])
(slurp (io/file (System/getProperty "user.home") ".zshrc"))
The file
function is useful here, because you give it the directory as the first arg, and the filename as the second without doing any string manipulation to build a path
@U051N6TTC thank you very much, that’s the approach I endend up taking 👌:skin-tone-4:
thanks again @U051N6TTC ended up using exactly this to read and create my applications config file!
Team, I was searching in a internet, I am trying to find good resource to learn and become expert in clojure macros, Can anyone help me with link please
1. Understanding the Clojure evaluation model. https://www.braveclojure.com/read-and-eval/ 2. Awing at their beauty and power. 3. Awing at the fact that Clojure (and other lisps for that matter) have only a handful of special forms and everything else is defined as a macro. 4. Macroexpanding a ton. 5. Understanding the use of quote, syntax-quote, unquote-splice, gensym. 6. Properly understanding that their use is when you want to transform the source code before evaluation. https://www.braveclojure.com/writing-macros/ 7. Finding the right use case for a macro, and checking if the macro emits the source that you expect by constantly macroexpanding your macro from the REPL. 8. Developing a sense for when to use them.
But I felt comfortable with creating macros after reading the relevant parts of “The Joy of Clojure”
> I own it but have never read it Symmetric with my macrology. I read them, but never write them :grinning_face_with_one_large_and_one_small_eye:
I’d written macros before, but The Joy of Clojure really solidified how to do it (and more importantly, how to avoid using them). The most important parts of macro authorship are: • Avoiding writing macros • When you DO write a macro, you should usually minimize the work in the macro and write helper functions • Understanding quoting and unquoting
I started out by reading “Programming Clojure” by Stu Halloway (back when the 1st edition was current). After that I believe that I was a competent Clojurista, but I often felt like I wasn’t necessarily being idiomatic in the language. Then I read The Joy of Clojure in 2011, and I finally felt like I got the language
I think some experience with formal systems / symbolic logic (so something like godel escher bach) and the notion of logics and metalogics helps a lot
> The most important parts of macro authorship are:
> ...
+ to all of quoll's points, with small additions:
clojure.walk/macroexpand-all
is a good friend. I like to run it ever time I use a new macro.
user=> (clojure.walk/macroexpand-all '(or (even? 3) (even? 42)))
(let* [or__5516__auto__ (even? 3)] (if or__5516__auto__ or__5516__auto__ (even? 42)))
Plus, definitely re-write macros you see in the wild, and play with syntax quoting rules to see the changing effect (e.g. the or
macro featured above).
Fiddle and macroexpand... it will help improve intuitions about how macros work in Clojure.I macroexpand all the time. It’s especially useful when you’re trying to explain things to people. Like, how do the threading macros work? How does destructuring work? etc
"Macros are also just functions of data -> to -> data, except they run at compile-time." ... It took me a good while to properly grok this concept.
I do wish macroexpansion would expand to bad forms rather than throw spec errors when you are diagnosing bad forms
Like if it expanded to (let [single] body) it would show that expansion rather than spec error out
@U11BV7MTK I guess let
is a special case since it tries to macroexpand let
to let*
. The following doesn't throw,
(macroexpand-1 '(let* [8]))
@UMPJRJU9E Yeah, several of Clojure's "Special Forms" are implemented as macros that expand to the actual, underlying special form. Like fn
is a macro implemented on top of fn*
, in addition to let
, etc.
(there was a thread some time back where a new Clojure developer was very surprised that some things listed as Special Forms were actually Clojure macros that expanded to mostly undocumented stuff)
1. Understanding the Clojure evaluation model. https://www.braveclojure.com/read-and-eval/ 2. Awing at their beauty and power. 3. Awing at the fact that Clojure (and other lisps for that matter) have only a handful of special forms and everything else is defined as a macro. 4. Macroexpanding a ton. 5. Understanding the use of quote, syntax-quote, unquote-splice, gensym. 6. Properly understanding that their use is when you want to transform the source code before evaluation. https://www.braveclojure.com/writing-macros/ 7. Finding the right use case for a macro, and checking if the macro emits the source that you expect by constantly macroexpanding your macro from the REPL. 8. Developing a sense for when to use them.
IMHO you only need to think about macros as an absolute beginner or a highly specialized expert, in between things go best if you don't make new macros
or maybe that's just resentment from having to deal with APIs that force the usage of macros
My first frustration to your point was go blocks and function composition.
Hi, I have got a coll of functions as:
(#function[clojure.core/inc]
#function[clojure.core/dec])
Now I would like to map it on coll containing colls of same length as:
[[5 5] [6 6] [7 7]]
result should be (lazily):
[[6 4] [7 5] [8 6]]
Is there some function for this?The first thing I thought of was juxt
but that applies everything to the same value. But since you’re going down seqs together, applying the first to the second, then that sounded like using map
with 2 seqs
(def fns [inc dec])
(for [[fst snd] [[5 5] [6 6] [7 7]]]
[((first fns) fst)
((second fns) snd)])
a suggestion from the peanut gallery:
user=> (for [[fst snd] [[5 5] [6 6] [7 7]]
:let [[f g] fns]]
[(f fst) (g snd)])
([6 4] [7 5] [8 6])
Yup yup, I’d definitely introduce a let to make it more readable. Was in a hurry here I guess
Since your values are the same number repeated (ie. [5 5]
) then juxt would work on a seq of just [5 6 7]
As in: (map (apply juxt fn-coll) [5 6 7])
But that’s just extrapolating from your example, rather than what you asked for
Yeah, it was just example - not really good one 🙂.
I will use this one
(map (fn [p] (map #(%1 %2) fn-coll p)) number-colls)
Just 2 comments then:
1. as I noted, you can use mapv
rather than map
if vectors matter to you. I don’t usually bother unless there’s need for it.
2. I called the argument p
to mean “pair”, but it may make sense to change it, particularly since it’s written to take a function seq (along with argument seq) of arbitrary length
Yeah, I get the code. I was just hoping that there is some “native” function for that.. something like zipmap but for functions and values 🙂
Hey all i'm using the luminus template and im working through Web Development with Clojure
and I'm trying to access the basic index route of the app at /
(by going to localhost:3000
) and i get a null pointer exception with not much to go off of. Any ideas on how I might begin to debug?
user=> Fri Aug 06 23:40:51 CEST 2021 [worker-1] ERROR - GET /
java.lang.NullPointerException
at org.httpkit.server.HttpHandler.run(RingHandler.java:117)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:830)
@varun.jayaraman How did you create your project and have you changed the code since creating the project? i.e., does a brand new generated project not work, or do you only get this error after making changes?
(a common "gotcha" is folks adding a println
to their handler, making it return nil
instead of a Ring response)
yeah i made a couple of changes, but ive followed the book until page 21 (not sure if you also have the book yourself). but basically ive modified routes/home.clj
to look like this:
(ns guestbook.routes.home
(:require
[guestbook.layout :as layout]
[guestbook.db.core :as db]
[ :as io]
[guestbook.middleware :as middleware]
[ring.util.response]
[ring.util.http-response :as response]))
(defn home-page [request]
(layout/render
request "home.html"))
(defn save-message! [{:keys [params]}]
(db/save-message! params)
(response/found "/"))
(defn about-page [request]
(layout/render request "about.html"))
(defn home-routes []
[""
{:middleware [middleware/wrap-csrf
middleware/wrap-formats]}
["/" {:get home-page}]
["/about" {:get about-page}]
["/message" {:post save-message!}]])
And home.html
looks like this:
{% extends "base.html" %}
{% block content %}
<div class="content">
<div class="columns is-centered">
<div class="column is-two-thirds">
<!-- Content -->
</div>
</div>
</div>
{% endblock %}
i thought it might have something to do with a typo in my selmer
template but i cant seem to see anything. and the route itself at /
is dead simple, I can't imagine what is throwing the null pointer exception there. Unfortunately i'm quite a noob and am not sure how to even begin debugging this
i was wondering if maybe i could replace home.html
with just rendering a simple string and seeing if it works, so something like:
(defn home-page [request]
(layout/render
request "<html><body><p>hi</p></body></html>"))
But layout/render
takes a filename that it automatically looks up in the resources folder i think, so that wont work 😕oh sorry @seancorfield i created the project with lein new luminus guestbook --template-version 3.91 -- +h2 +http-kit
Yeah, I just dug the book out to take a look. There's a lot of moving parts between running that and page 21 🙂
A suggestion: if you git init
after first creating the template and then add and commit each step along the way, you'll have an easier time reverting to a working version and also you'll have the specific diffs showing the changes you made between a working version and a non-working version.
(I generally recommend Clojure beginners start with just Ring and Compojure (or reitit) to learn the basics of web dev before attempting to use Luminus -- it's very complicated for a new-to-Clojure developer)
would you recommend it to someone who is an experienced web dev but just new to clojure?
No, if you're new-to-Clojure, regardless of background, start with something much simpler than the Luminus template.
I see so many beginners stumble with Luminus. It's fine until something goes wrong but then there are so many libraries involved and so much going on that debugging an error is really tricky 😕
I’m an experienced web dev who has been learning Clojure for about the last 1.5 years (on and off) and I’ve found Ring and Compojure by itself to be pretty straightforward.
So @seancorfield’s advice is sound. Not that there was any questioning that fact. 😉
To put it in perspective, that Luminus project (created via that command-line) has all these libraries in play:
:dependencies [[ch.qos.logback/logback-classic "1.2.3"]
[cheshire "5.10.0"]
[clojure.java-time "0.3.2"]
[com.h2database/h2 "1.4.200"]
[conman "0.9.1"]
[cprop "0.1.17"]
[expound "0.8.7"]
[funcool/struct "1.4.0"]
[luminus-http-kit "0.1.9"]
[luminus-migrations "0.7.1"]
[luminus-transit "0.1.2"]
[luminus/ring-ttl-session "0.3.3"]
[markdown-clj "1.10.5"]
[metosin/muuntaja "0.6.7"]
[metosin/reitit "0.5.10"]
[metosin/ring-http-response "0.9.1"]
[mount "0.1.16"]
[nrepl "0.8.3"]
[org.clojure/clojure "1.10.1"]
[org.clojure/tools.cli "1.0.194"]
[org.clojure/tools.logging "1.1.0"]
[org.webjars.npm/bulma "0.9.1"]
[org.webjars.npm/material-icons "0.3.1"]
[org.webjars/webjars-locator "0.40"]
[ring-webjars "0.2.0"]
[ring/ring-core "1.8.2"]
[ring/ring-defaults "0.3.2"]
[selmer "1.12.31"]]
compared to what you get with lein new compojure webapp
:
:dependencies [[org.clojure/clojure "1.10.0"]
[compojure "1.6.1"]
[ring/ring-defaults "0.3.2"]]
(and I also really dislike Mount and some of the other choices Luminus makes -- I especially don't think they are good choices for beginners)
I ought to take a look at Coast some time. I sort of deliberately stay away from "framework-y" things in Clojure...
i used to be against frameworky things after rails but after using phoenix in the elixir world, i think i'm less dogmatic
but i'll go back to basics and maybe only touch luminus once i understand how to debug a bit more in clojure
I think it's telling that Pragmatic rate this book as intermediate-expert:
@varun.jayaraman Don't feel disheartened about tripping up with Luminus -- that happens to pretty much everyone who is trying to learn Clojure from that book I think. A lot of people suggest Getting Clojure as a really good book to learn Clojure from.
It uses Pedestal -- how approachable is that for a beginner?
:deps {com.github.seancorfield/next.jdbc {:mvn/version "1.2.674"}
hiccup/hiccup {:mvn/version "2.0.0-alpha2"}
io.pedestal/pedestal.jetty {:mvn/version "0.5.9"}
io.pedestal/pedestal.service {:mvn/version "0.5.9"}
org.clojure/clojure {:mvn/version "1.10.3"}
org.postgresql/postgresql {:mvn/version "42.2.23"}}
(I looked at Pedestal when Cognitect first released it and thought it was pretty complex -- but there was next to no documentation for it back then either)i think i was getting impatient and just wanted to build something with clojure so i reached for the web dev book
If it helps, here's a simple web app example I put together: https://github.com/seancorfield/usermanager-example and the README links to a variant created by @admin055 that swaps out Compojure and Component for Reitit and Integrant so it's good to compare the two.
how does leiningen
know about these different templates by the way? are they hardcoded into lein
?
It searches http://clojars.org for <template-name>/lein-template
clj-new
for the Clojure CLI/`deps.edn` looks for <template-name>/clj-template but also supports Leiningen templates (but the latter mostly generate Leiningen-based projects).
(there also a few simple templates built into both tools: app
, lib
, and plugin
I think for Leiningen? It's template
for clj-new
-- and clj-new
also has polylith
built-in)