Fork me on GitHub
#beginners
<
2022-08-10
>
Chase01:08:11

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

Chase01:08:37

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.

Chase01:08:18

In my deps.edn I have :paths ["src" "test"]

hiredman01:08:02

in-ns doesn't do any code loading

hiredman01:08:40

It just creates the namespace if it doesn't exist and sets *ns* to it

Chase01:08:29

but why is require not able to be resolved?

Chase01:08:45

What command should I use to properly get into an existing ns?

hiredman01:08:48

Because in-ns creates an empty nameslace

hiredman01:08:44

The namespace doesn't exist

hiredman01:08:58

You have to load the code that creates it

Chase01:08:40

Hmmm. How do I get the test/practice/foo_test.clj file/namespace loaded into the repl?

hiredman01:08:27

Anything that actually loads code: require, load-file, etc

Chase01:08:07

ahhh. So I just stay in the user ns and require in the test file's ns

hiredman01:08:11

You can do something like (doto 'the.ns require in-ns)

Chase01:08:32

Thank you! Man, it must have been even longer than I thought since I've used the repl much from the terminal.

pinkfrog04:08:46

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

seancorfield04:08:10

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?

pinkfrog04:08:37

Great. One more demand, the returned content type is text/json. Possible to do that?

seancorfield04:08:58

(jetty/run-jetty (fn [_] {:status 200 :body "{}" :headers {"content-type" "application/json"}}) {:port 8080 :join? false})

seancorfield04:08:22

text/json isn't valid, I don't believe?

seancorfield04:08:02

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).

pinkfrog04:08:53

var express = require('express');
var app = express();

app.get('/', function(req, res){
   res.send("Hello world!");
});

app.listen(3000);
what about the path?

pinkfrog04:08:08

Adding this sounds have to involve ring/reitit stuff

seancorfield04:08:22

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.

seancorfield04:08:05

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.

seancorfield04:08:44

(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.

seancorfield04:08:05

Messy formatting since I'm just typing this into a REPL 🙂

seancorfield04:08:48

You could define the handler function separately (as a named function) and pass it into run-jetty to make it a bit cleaner.

seancorfield04:08:15

(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 🙂

pinkfrog05:08:45

manually dispatching on URI might be the approach.

seancorfield05:08:09

It just goes to show that you really don't need more than just Ring itself for simple web apps 🙂

thanks3 2
didibus07:08:16

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.

didibus07:08:59

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}))

thanks3 1
wretched___12:08:51

How should i learn clojure?

wretched___12:08:17

I've learned #clojure using this tutorial now what ? what should i do?

wretched___12:08:25

I gone through , luminus creator book Web Development with Clojure, which is pretty much bad. i dont even understand , and i left

wretched___12:08:37

should i learn frameworks of clojure for web dev (backend)?

wretched___12:08:00

what should i do now after basic clojure?

wretched___12:08:45

like i want to build full stack project using clojure --- umm like some pet project or some large project nuban(fintech) like project

wretched___12:08:12

i've a small goal of making opensource project

wretched___12:08:48

I'm current spring boot developer , but i want to use clojure for my project, so i'm learning clojure

wretched___12:08:55

i learned clojure but whats next?

wretched___12:08:02

like for example , when i finish learning python , i would now go to django and build rest api things easily

wretched___12:08:06

i want like that tutorial

wretched___12:08:19

i like reading book and dont like at the same time

wretched___12:08:35

like some udemy or some course video course about what should i do next would be fine?

wretched___12:08:43

Any tutorial recommend guys?

wretched___13:08:28

i was mind boggled by people saying enlightment, flexibility of lisp based langs

Sasha V. Bogdanov13:08:37

Hello, clojurians!

Sasha V. Bogdanov13:08:49

I am in despair… why lein repl from root of project runs my application, but not REPL server?

dpsutton14:08:15

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

Sasha V. Bogdanov11:08:41

I used -main function and now all is fine. Thank you.

wretched___13:08:34

also shocked on how less code it takes

❤️ 1
kennytilton22:08:49

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.

🙂 1
pppaul19:08:43

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

wretched___13:08:21

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

wretched___13:08:49

but this is pros out of many pros of clojure

wretched___13:08:59

@sasha_bogdanov_dev so i want to learn clojure but where 😕

Sasha V. Bogdanov13:08:26

You not have much complete frameworks in clojure world. You composing them from libraries

Sasha V. Bogdanov13:08:36

Use Ring with Compojure for start )

🙂 1
wretched___13:08:51

book , ;( i'm just trying this clojure for brave which is last updated in 2017 .

Brandon Stubbs13:08:18

Don’t worry about age of book, the language doesn’t change much like others - Clojure for the Brave is awesome!

👍 3
😀 1
Brandon Stubbs13:08:47

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/

1
wretched___13:08:02

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 🙂 .

💙 1
ale16:08:39

@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.

💯 1
wretched___13:08:13

i learnt clojure but what should i do next , i want to build full complete backend system like these in spring boot or django

teodorlu13:08:45

@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

🙂 1
wretched___13:08:44

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.

👍 1
popeye15:08:08

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

dpsutton15:08:32

strip this into constituent parts. Do you know how to update a set?

dpsutton15:08:45

once you have that, do you know how to update-in a map?

dpsutton15:08:58

Then combine the two. And don’t combine the two until you have both parts working independently

dpsutton15:08:15

“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}}}

popeye15:08:14

yes working as expected 🙂 Thanks

Godwin Ko23:08:36

@U11BV7MTK nice illustration, much much better than just posting the answer 👍:skin-tone-2:

👍 1
rickheere15:08:50

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}]}

rickheere15:08:53

(clojure.walk/keywordize-keys {"a" [1 2 {"a" 1, "b" 2}]}) => {:a [1 2 {:a 1, :b 2}]}

rolt16:08:30

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

Ed16:08:06

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}]}

  )

rickheere16:08:54

Thanks both 🙂

rickheere16:08:03

I managed to get this working

rickheere16:08:04

(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)))

rickheere16:08:21

@U0P0TMEFJ The custom function is also a good one.

rickheere16:08:48

Maybe I can make the walk function smarted so I don't have to cover all those types with identity

Ed18:08:13

You only need to put identity in the case of Object since all the others inherit from Object

Ed18:08:33

And I would probably cover java.util.List/`Map` rather than the specific implementations

rickheere19:08:33

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))})

rickheere19:08:27

And with java.util.List/Map is even better

(defprotocol ClojureCoercible
    (->clj [object]))

  (extend Object
    ClojureCoercible
    {:->clj identity})

  (extend java.util.Map
    ClojureCoercible
    {:->clj (fn [o] (into {} o))})

  (extend java.util.List
    ClojureCoercible
    {:->clj (fn [o] (into [] o))})