This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-08-10
Channels
- # announcements (9)
- # aws (11)
- # babashka (37)
- # beginners (97)
- # biff (2)
- # calva (73)
- # clj-kondo (17)
- # cljfx (3)
- # clojure (89)
- # clojure-europe (45)
- # clojure-norway (12)
- # clojurescript (17)
- # datahike (8)
- # datomic (13)
- # deps-new (4)
- # figwheel-main (1)
- # graalvm (2)
- # hyperfiddle (8)
- # introduce-yourself (6)
- # leiningen (38)
- # lsp (57)
- # malli (13)
- # nbb (46)
- # off-topic (40)
- # pathom (3)
- # polylith (8)
- # rum (4)
- # shadow-cljs (14)
- # spacemacs (1)
- # sql (11)
- # xtdb (10)
I seem to have forgotten how to use the repl from the terminal. My practice repo is setup like
src/practice/foo.clj
test/practice/foo_test.clj
I wanted to check something out in that test file so from the repl prompt (I use clj -M:repl
) I try
(in-ns 'practice.foo-test)
Now the repl is in that namespace, and in the foo_test.clj
source file I have a require statement referring all of clojure.test. But if I try something like (run-tests)
I get an Unable to resolve symbol: run-tests in this context
error. So then I try (require '[clojure.test :as test])
and get an Unable to resolve symbol: require in this context
error.Thank you! Man, it must have been even longer than I thought since I've used the repl much from the terminal.
What’s the simplest way to get a http server running for simple usage like echoing a hello world. I’d expect something in ~ 10 lines as in express.js or flask
seanc@Sean-win-11-laptop:~/clojure$ clj -Sdeps '{:deps {ring/ring {:mvn/version "RELEASE"}}}'
Downloading: ring/ring/maven-metadata.xml from clojars
user-loaded
Clojure 1.11.1
user=> (require '[ring.adapter.jetty :as jetty])
2022-08-09 21:13:46.377:INFO::main: Logging initialized @109700ms to org.eclipse.jetty.util.log.StdErrLog
nil
user=> (jetty/run-jetty (fn [_] {:status 200 :body "Hello, World!"}) {:port 8080 :join? false})
2022-08-09 21:14:43.187:INFO:oejs.Server:main: jetty-9.4.44.v20210927; built: 2021-09-27T23:02:44.612Z; git: 8da83308eeca865e495e53ef315a249d63ba9332; jvm 18.0.2+0
2022-08-09 21:14:43.236:INFO:oejs.AbstractConnector:main: Started ServerConnector@1292071f{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2022-08-09 21:14:43.237:INFO:oejs.Server:main: Started @166560ms
#object[org.eclipse.jetty.server.Server 0x22587507 "Server@22587507{STARTED}[9.4.44.v20210927]"]
;; browse to
user=> (.stop *1) ; stop the server
2022-08-09 21:16:00.688:INFO:oejs.AbstractConnector:main: Stopped ServerConnector@1292071f{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
nil
user=>
How about that?Great. One more demand, the returned content type is text/json. Possible to do that?
(jetty/run-jetty (fn [_] {:status 200 :body "{}" :headers {"content-type" "application/json"}}) {:port 8080 :join? false})
text/json
isn't valid, I don't believe?
Adding middleware can make the JSON stuff "easier" if you want to do something more sophisticated. But I generally start with the basics. run-jetty
returns the running server -- you'd normally def
it into a var (so it's easier to .stop
when you need to in the REPL).
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send("Hello world!");
});
app.listen(3000);
what about the path?What routes do you need the app to respond to? The requested route is under the :uri
key of the request hash map so you can handle it conditionally in the handler function if you want.
Or you could use compojure (or reitit) if you wanted it handled explicitly. Depends how complex you need things to get. And what you want to happen if a bad request comes in.
(jetty/run-jetty (fn [{:keys [uri]}] (if (= "/" uri) {:status 200 :body "{}" :headers {"content-type" "application/json"}} {:status 404})) {:port 8080 :join? false})
That responds with the {}
for /
and not found for anything else.Messy formatting since I'm just typing this into a REPL 🙂
You could define the handler function separately (as a named function) and pass it into run-jetty
to make it a bit cleaner.
(defn app [{:keys [uri]}] ...)
(jetty/run-jetty #'app {:port ..})
With that approach -- with #'app
-- you can even redefine the app
function in the REPL while the server is running and the changes will be picked up immediately, without restarting it 🙂It just goes to show that you really don't need more than just Ring itself for simple web apps 🙂
I find Compojure is pretty easy/compact for simple use cases.
clojure -Sdeps '{:deps {compojure/compojure {:mvn/version "RELEASE
"} http-kit/http-kit {:mvn/version "RELEASE"}}}'
(ns app
(:require [compojure.core :refer :all]
[org.httpkit.server :as kit]))
(defroutes app-routes
(GET "/" []
(fn [req] "Hello world!")))
(def server (kit/run-server #'app-routes {:port 3000}))
You can also use ring-jetty over http-kit if you prefer, but I like http-kit better also for simple use cases.And this ring namespace is kind of convenient as well for ease of use: https://ring-clojure.github.io/ring/ring.util.response.html like say:
clojure -Sdeps '{:deps {compojure/compojure {:mvn/version "RELEASE"} http-kit/http-kit {:mvn/version "RELEASE"}}}'
(ns app
(:require [compojure.core :refer :all]
[ring.util.response :refer :all]
[org.httpkit.server :as kit]))
(defroutes app-routes
(GET "/" []
(-> (response "Hello world!")
(content-type "text/json"))))
(def server (kit/run-server #'app-routes {:port 3000}))
Hello
How should i learn clojure?
I've learned #clojure using this tutorial
now what ? what should i do?
I gone through , luminus
creator book Web Development with Clojure,
which is pretty much bad. i dont even understand , and i left
should i learn frameworks of clojure for web dev (backend)?
what should i do now after basic clojure?
like i want to build full stack
project using clojure --- umm like some pet project or some large project nuban(fintech) like project
i've a small goal of making opensource project
I'm current spring boot
developer , but i want to use clojure for my project, so i'm learning clojure
i learned clojure but whats next?
like for example , when i finish learning python
, i would now go to django
and build rest api things easily
i want like that tutorial
i like reading book and dont like at the same time
like some udemy or some course video course about what should i do next would be fine?
Any tutorial recommend guys?
i was mind boggled by people saying enlightment, flexibility of lisp based langs
Hello, clojurians!
I am in despair… why lein repl
from root of project runs my application, but not REPL server?
this can often happen if you have top level definitions that “do work”. consider the difference between (def server (start-web-server))
and (defn start-server [] (start-web-server))
. One is a function that can be invoked to start a web server, the other has started a web server. Just loading the code will start the web server
I used -main
function and now all is fine. Thank you.
Look again. Most of the difference is closing braces and jsx end tags. Clojure bungs closing syntax all together, and Hiccup does not have closing tags. Try a Dart vs #clojuredart comparison. There we unleash the Unbearable Lightness of macros. Non-lisps do not stand a chance.
i still write JS, and i find that a lot of popular frameworks make you write a ton of JS in comparison with something like the micro-frameworks htmx/unpoly. one nice thing about your example is how cluttered the JS is (the names of functions are harder to read/don't stand out). the clojure isn't just less code, it's much easier to read
I was listening a podcast saying oh you write in a clojure you have 15000 lines of code, while you write in c or c++ you'll get 100,000 lines of code
but this is pros out of many pros of clojure
@sasha_bogdanov_dev so i want to learn clojure but where 😕
Books maybe
You not have much complete frameworks in clojure world. You composing them from libraries
book , ;( i'm just trying this clojure for brave
which is last updated in 2017 .
Don’t worry about age of book, the language doesn’t change much like others - Clojure for the Brave is awesome!
There are also a lot of courses out there as I saw you might be more visual https://lambdaisland.com/episodes https://www.learnreitit.com/ https://clojure.org/community/training#_online_courses
I would also suggest following Calva’s getting started guide, it’s also awesome and covers a wide range of topics so you can easily tell where you need to spend more of your time https://calva.io/getting-started/
Thank you @U015Y1A1N8Y though i'm a emacs guy i've already setup things.
I found lambdaisland
courses to be not well stacked, like everything are cluttered and there is no way to know where to start and where to end.
other i'll try, as international paying system is not yet developed in my place and it becomes too much costly purchasing course from those countries to less developed countries , though ericnormand had courses which were affordable i'll search way to pay and get courses 🙂 .
@index.h.ot.41.6.6 I’m currently learning with two books from the Pragmatic Programmers catalog: ‘https://pragprog.com/titles/shcloj3/programming-clojure-third-edition/’ (almost finished), ‘https://pragprog.com/titles/dswdcloj3/web-development-with-clojure-third-edition/’ (read the first two/three chapters). It’s working for me and I found no outdated information.
i learnt clojure but what should i do next , i want to build full complete backend system like these in spring boot
or django
@index.h.ot.41.6.6 please consider using a thread, then it's easier to also see other people's content here 🙂 Two good starting points: 1. HTTP-kit + Compojure: https://http-kit.github.io/server.html 2. Do it yourself with just Ring: https://ericnormand.me/guide/clojure-web-tutorial
I'll use compojure for building rest api . I was bit nervous like if i'm making a company for financial tech, i should choose a very good framework rather than just random framework or just start from scratch. so compojure, i've to make bit familier . Thank you.
How can I update set value in the hash-map? so that when I add a new element , it should go inside same set inside hash map
(update-in my-map [:a :b :c] conj #{} :T)
result would be {:a {:b {:c #{:T "D" "P"}}}
, every time when I add element
Then combine the two. And don’t combine the two until you have both parts working independently
“How do I add to a set”?
A: conj
. (conj #{} :a)
-> #{:a}
“What if the set doesn’t exist yet?
A: fnil
: (conj nil :a)
-> (:a)
. But ((fnil conj #{}) nil :a)
-> #{:a}
Now that I know how to combine items into a set (including if the set doesn’t exist, how do i update a set in a map at path [:foo :bar]?
(update-in {} [:foo :bar] (fnil conj #{}) :new-item)
-> {:foo {:bar #{:new-item}}}
And then
(update-in {:foo {:bar #{:new-item}}} [:foo :bar] (fnil conj #{}) :another-item)
becomes {:foo {:bar #{:new-item :another-item}}}
@U11BV7MTK nice illustration, much much better than just posting the answer 👍:skin-tone-2:
I use the java library of firebase and it returns me a structure of ArrayLists
and HashMaps
. clojure.walk/keywordize-keys
doesn't work with those structures so I need a way to transform the structure to maps and factors. I looked at a wrapper library for firebase (matchbox) and there they walk over the whole structure and transform the values based on the types. Is this really the way to go or is there a handy function I can use. Example (clojure.walk/keywordize-keys (HashMap. {"a" (ArrayList. [1 2 (HashMap. {"a" 1 "b" 2})])}))
=> {"a" [1 2 {"a" 1, "b" 2}]}
(clojure.walk/keywordize-keys {"a" [1 2 {"a" 1, "b" 2}]})
=> {:a [1 2 {:a 1, :b 2}]}
Here is the matchbox code https://github.com/crisptrutski/matchbox/blob/5bb9ba96f5df01bce302a8232f6cddd9d64a1d71/src/matchbox/serialization/sorted.cljc#L20
none that I know of. An other approach is via protocols (polymorphism vs pattern matching): https://github.com/oscaro/clj-gcloud-common/blob/master/src/clj_gcloud/coerce.clj#L27-L61
I think that clojure.walk/keywordize-keys
relies on the predicate map?
which seems to only return true for clojure's persistent maps. You could probably write your own that would return a clojureized set of maps. Maybe something like this?
(defn keywordize-keys [m]
(cond (instance? java.util.Map m)
(into {} (map (fn [[k v]] [(keyword k) (keywordize-keys v)])) m)
(instance? java.util.List m)
(into [] (map keywordize-keys) m)
:else
m))
(comment
(keywordize-keys (java.util.HashMap. {"a" (java.util.ArrayList. [1 2 (java.util.HashMap. {"a" 1 "b" 2})])}))
;; => {:a [1 2 {:a 1, :b 2}]}
)
(defprotocol ClojureCoercible
(->clj [object]))
(extend clojure.lang.PersistentArrayMap
ClojureCoercible
{:->clj identity})
(extend clojure.lang.MapEntry
ClojureCoercible
{:->clj identity})
(extend java.lang.String
ClojureCoercible
{:->clj identity})
(extend java.lang.Long
ClojureCoercible
{:->clj identity})
(extend Object
ClojureCoercible
{:->clj identity})
(extend HashMap
ClojureCoercible
{:->clj (fn [o] (into {} o))})
(extend ArrayList
ClojureCoercible
{:->clj (fn [o] (into [] o))})
(def res (HashMap. {"a" (ArrayList. [1 2 (HashMap. {"a" 1 "b" 2})])}))
(clojure.walk/keywordize-keys (clojure.walk/prewalk (fn [o] (->clj o)) (->clj res)))
@U0P0TMEFJ The custom function is also a good one.
Maybe I can make the walk function smarted so I don't have to cover all those types with identity
You only need to put identity
in the case of Object
since all the others inherit from Object
Thanks, this is already way better
(defprotocol ClojureCoercible
(->clj [object]))
(extend Object
ClojureCoercible
{:->clj identity})
(extend HashMap
ClojureCoercible
{:->clj (fn [o] (into {} o))})
(extend ArrayList
ClojureCoercible
{:->clj (fn [o] (into [] o))})