Fork me on GitHub
#beginners
<
2021-04-11
>
Oliver06:04:14

When I start a REPL with lein repl in my project directory I see the following error: #error { :cause Could not locate clj_json/core__init.class, clj_json/core.clj or clj_json/core.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name. :via [{:type clojure.lang.Compiler$CompilerException :message Syntax error compiling at (myproject/core.clj:1:1). :data #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source myproject/core.clj} :at [clojure.lang.Compiler load Compiler.java 7648]} {:type .FileNotFoundException :message Could not locate clj_json/core__init.class, clj_json/core.clj or clj_json/core.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name. Any hint what I am doing wrong here? deps.edn looks like this: { :deps { clj-json/clj-json {:mvn/version "0.5.3"} } }

pavlosmelissinos06:04:50

deps.edn is for clojure cli lein repl needs a project.clj file, does your project have one? Either that or add a :dev alias to your deps.edn and start a repl with clj -A:dev

Oliver07:04:00

I kind of understand. 😉 Sorry, I am just beginning with clojure. Would it be easier to start the repl with clj? I am trying to start a repl on the command line and then connect to it from inside spacemacs. If I use lein repl I get back a port and this works fine (besides the error). But I don't get any port information back if I start the repl with clj.

pavlosmelissinos07:04:03

Ah, I see. Since you feel comfortable with lein, you can create a project.clj file (deps.edn is clj specific). It's been years since I last used lein so I can't help you if you have more specific questions but here are the instructions: https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md#projectclj Personally I don't care about decoupling my REPLs from emacs so I'm using clj+cider

👍 3
Oliver08:04:39

Thanks, Pavlos. I also found some tutorial from @U05254DQM here https://practicalli.github.io/clojure/clojure-tools/install/community-tools.html that I will read through.

👍 3
practicalli-john08:04:51

The CLj doesn't start an nrepl server (network connection to the repl) by default, unlike Leiningen which does.

Oliver08:04:45

@U05254DQM I guess I need to run through your tutorial once more to understand all this. I used to program in CL years ago and am very interested in Clojure, but the tooling is quite confusing for me, too many things to understand at once.

practicalli-john08:04:25

If you do not need an interactive REPL command line, i.e. you are only evaluating Clojure code in Spacemacs buffers, then you can use

clojure -M:middleware/cider-clj
If you also want an interactive REPL in the terminal as well as evaluating in Spacemacs, then use
clojure -M:repl/rebel-nrepl
This uses the rebel readline repl which is a much richer REPL UI experience than the default clojure or clj, but the code evaluates just the same Both commands will show the nREPL port number once the nREPL server is listening. cider-connect should find the nrepl port automatically anyway

practicalli-john09:04:54

I also have Spacemacs specific content, which uses Clojure cli tools here: https://practicalli.github.io/spacemacs/clojure-repl/connect-to-repl.html

practicalli-john09:04:19

Feel free to ask me questions about this if its not clear, there is always room for improvement

practicalli-john09:04:38

To connect from the command line to a REPL aready running, either in Spacemacs or another terminal session, use the command

clj -Sdeps '{:deps {nrepl/nrepl {:mvn/version "0.8.3"}}}' -M -m nrepl.cmdline --connect --host localhost --port 46587
Replace values for --host and --port as requried. Use SPC b m to see the nrepl host and port in the *message* buffer. I'll add an alias for this too, as it may be useful for connecting to remote REPL's

👍 3
Oliver09:04:26

Cool, I think it works now. Thank you, John!

👍 3
practicalli-john13:04:48

I've simplified the REPL aliases section (hopefully), with a clearer distinction as to where to use each alias (terminal, editor, remote). I've also added two aliases for connecting to a remote REPL (eg. a repl run on a remote server or repl run from the editor jack-in process) https://github.com/practicalli/clojure-deps-edn#repl-terminal-ui

Zaymon06:04:32

Hi, I’m just starting with Clojure. I’m struggling to think about how I should compose handlers. Is there a more idiomatic way to run validation, if it’s okay perform some side-effect and then return an appropriate response?

(def Letter
  [:map
   {:title "Letter"}
   [:id uuid?]
   [:recipient uuid?]
   [:sender uuid?]
   [:content string?]])

(defn validate-letter [letter] (me/humanize (m/explain Letter letter)))

(defn send-letter
  "Handler to send a letter to another user"
  [{{{:keys [letter]} :body} :parameters}]

  (let [letter-with-id (assoc letter :id (java.util.UUID/randomUUID))]

    (if-let [errors (validate-letter letter-with-id)]
      {:status 400
       :body {:errors errors}}

      ;; Where do I persist this thing to the database?

      {:status 200
       :body {:letter letter-with-id}})))
Coming from F# where everything is mostly done with chaining monads I’m struggling to get my head around how to express workflows with an Ok path and an Error path.

dharrigan07:04:56

I see you're using reitit and malli?

Zaymon07:04:12

Sure am 🙂

dharrigan07:04:54

There is the #reitit and #malli channels, but for now, I can tell you what I do. Malli and Retiti also use Muunjata for validation (part of the suite). If you define a spec, and it fails the spec (i.e., validation), an appropriate error will be returned to the client. However, for more business-oriented validation, I have my own excpetion middleware

dharrigan07:04:02

that I place in the chain

dharrigan07:04:44

then deeper down in the layers if I encounter an error, like some business contraint violation, I throw and let the exception handler catch, log out to Sentry, but also format a lovely response back to the client.

Zaymon07:04:29

Aha. Okay that would be great to move the validation up a layer. I’ll look into it

dharrigan07:04:40

I should extend my simple github example to use some validation

dharrigan07:04:53

but for now, here's a quick example:

dharrigan07:04:37

today, you've given me reason to add in some simple validation 🙂

Zaymon07:04:26

Sweet I’ll look out for the changes too.

dharrigan07:04:32

in my business application, I have this:

dharrigan07:04:10

:middleware [swagger/swagger-feature
                        muuntaja/format-middleware
                        (exceptions/exception-middleware app-config)
                        parameters/parameters-middleware
                        coercion/coerce-exceptions-middleware
                        coercion/coerce-request-middleware
                        coercion/coerce-response-middleware]}}))

dharrigan07:04:23

notice the (exceptions/exceptions-middleware ....)

dharrigan07:04:28

that's my own middleware handler

Zaymon07:04:54

Did you just need more control over how exceptions were presented?

dharrigan07:04:23

yes, I wanted to give back to the client lots more details, and a nicely crafted response.

3
Hagenek10:04:17

Hi everyone, hope you are having a nice sunday. I am struggling to understand this destructuring. It reminds me of the syntax used when we require namespaces. Is it basically to get a variable flash which the value associated with the :flash key on the request map, and also keeping the request variable as the map provided as an argument to the home-page function?

(defn home-page [{:keys [flash] :as request}]
  (layout/render
   request
   "home.html"
   (merge {:messages (db/get-messages)}
          (select-keys flash [:name :message :errors]))))

pavlosmelissinos10:04:36

Yes, that's another way of saying:

(defn home-page [request]
  (layout/render
   request
   "home.html"
   (merge {:messages (db/get-messages)}
          (select-keys (:flash request) [:name :message :errors]))))

😃 3
raspasov11:04:47

@USMLDU9BR exhaustively useful guide to Clojure destructuring 🙂 https://clojure.org/guides/destructuring

☝️ 3
roelof11:04:51

just jumping in to say hello and I hope everyone is still healty

🙃 9
Dave Suico15:04:49

Hi guys, does anyone know how to fix an error message like this when u try to install polylith as a dependency? "Error building classpath. Manifest type not detected when finding deps for polyfy/polylith in coordinate"

David Reno16:04:32

I’m trying to figure out when to use the ! suffix on functions. Do I use it on any function that is impure? The docs aren’t super clear to me as they reference functions that “are not safe in STM transactions” which I’m not clear about. Also, does everything that calls this function also get the bang? Here’s my sequence of functions in question for background:

(defn get-register!
  "return the byte contents of a register (action)"
  [bus port register]
  (let [address (get-in registers [port register])] ;; lookup register address
    (smbus/read-byte bus address)))                 ;; return the value there

(defn get-registers!
  "return the byte contents of both :a and :b registers (action)"
  [bus register]
  {:a (get-register! bus :a register)
   :b (get-register! bus :b register)})

(defn get-int-registers!
  "Return all the registers needed to deduce what changed related to an
  interrupt occurence (action)"
  [bus]
  (do ;; order of reads is important
    (def intf (get-registers! bus :intf))
    (def intcap (get-registers! bus :intcap))
    (def gpio (get-registers! bus :gpio))
    {:intf intf :intcap intcap :gpio gpio}))

Zaymon01:04:38

I use ! to denote this function has a side-effect or mutates some state. It’s a nice little reminder when using any ! function that you should be aware it’s not a pure transformation.

👍 3
practicalli-john16:04:47

I use it on functions that actually do a mutation or major side-effect. I don't use the ! for functions that call functions that do the mutation.

👍 3
David Reno19:04:57

Thanks everyone. I’ve decided not to propagate the exclamation mark but I’ll have to think more about the first read because it’s not idempotent, it will actually cause the next read to be different. I may use it there.

dpsutton16:04:26

clojure uses the STM in loading libraries and possibly other functions, so it is true that every time i use clojure i use the STM. Outside of that though, I've never directly used it in code that i've consumed or code that i've written. I wouldn't use the STM's conventions in naming affect your naming choices. That being said, people often use ! as a suffix when writing mutating impure functions. Add it as you like

👍 7