Fork me on GitHub
#beginners
<
2022-02-23
>
Noah Moss00:02:28

Let’s say I have a binding path that can either be a vector of primitive values, e.g. [:a "a" 0] or a vector of similar vectors, e.g. [[:a "a" 0] [:b "b" 1]]. I want to append a new item to the end of the vector if it is the former, or the end of every sub-vector if it is the latter. Currently my approach is (if (seqable? (first path) ...)) and just handling each case separately. It works but I’m wondering if there’s a more concise way to do this that I don’t know of.

dpsutton00:02:34

is it possible to treat the vector of primitives as a collection of vectors or primitives but it only has a single value? Just homogenize into the plural case all the time?

dpsutton00:02:09

just accept that you're dealing not with a path but paths even if it might often be only a single path there?

John Bradens02:02:16

What do you think is the best way to learn clojure? Tutorials or books? Videos or written tutorials? What's the best way of finding them?

quoll03:02:37

I think this depends heavily on an individual’s learning style, so there isn’t one perfect answer.

quoll03:02:31

Personally… I learned Clojure by first learning Scheme through the https://www.youtube.com/watch?v=-J_xL4IGhJA&amp;list=PLE18841CABEA24090. This taught me the fundamentals of programming without mutation. Then I learned about using immutable data structures through https://www.artima.com/shop/programming_in_scala_5ed. And finally, I learned Clojure from the original https://pragprog.com/titles/shcloj3/programming-clojure-third-edition/ by Stuart Halloway, Aaron Bedra, and Alex Miller. (Although, I read the first edition, which was only by Stuart). I liked the book, since it covers all of the elements of the language that might be glossed over or skipped in less comprehensive materials.

John Bradens02:02:33

I feel like I enjoy tutorials the best, but they are on all sorts of different blogs, and can be a bit hard to find

John Bradens02:02:55

Books are great but can be outdated quickly

John Bradens02:02:19

Or is it best to just read documentation? I think I'm almost getting to a point where I can start doing that, but definitely couldn't at the beginning

andy.fingerhut03:02:28

I would note that Clojure, the language itself, has grown primarily by additions over the years, not by taking things away or modifying them. Thus even several-year-old books tend to have a lot more relevance today than the same age book for a language that changes more rapdily (which is a lot of programming languages).

andy.fingerhut03:02:28

I have not read it myself, but I have heard glowing reviews of the book "Getting Clojure".

quoll03:02:46

I’m with Andy here: Clojure books tend to stay relevant over time. That said, very old books don’t have all of the newest features of the language. For instance, the first read the first edition of Programming Clojure did not have Protocols in it, and these had just come out as I was reading it

quoll03:02:42

Since then, a lot of things have been introduced. But none of the old stuff has gone away. So even if you read an ancient book, you’ll still learn a lot.

John Bradens03:02:50

Cool, thanks. That's really good to know

seancorfield03:02:11

I'll +1 Getting Clojure @U02387J8EKG

👍 1
seancorfield03:02:46

I would say that any book written for Clojure 1.7 and later would be good these days.

seancorfield03:02:33

(mid-2015 onward)

👍 1
Cora (she/her)03:02:18

I +7343 Getting Clojure

1
👍 1
practicalli-johnny09:02:36

I used a combination of resources, including some from https://clojure.org/community/resources I also created some online books and videos at https://practical.li/

cheewah04:02:38

Hi I am wondering if it is possible to get the full classname by macro? i.e. for the last line below to return "java.lang.String"

(import '[java.lang String])
(require '[clojure.reflect :as cr])
(defmacro clsname [x]
  (cr/typename x))

(cr/typename String) ; returns "java.lang.String"
(macroexpand-1 '(clsname String)) ; returns "String"
the reason i want this is because i am sending a lot of requests like a.b.c.ReqXXX and getting responses like a.b.c.RespXXX. The data format is binary, so I need to know what class to cast/deserialize when response arrives, and will like to 'guess' the response class during compile time.

hiredman04:02:25

Macros get data structures from the reader to operate on

hiredman04:02:13

The reader reads String as a symbol

hiredman04:02:58

The evaluating the symbol resolves it against *ns* which is going to return the java.lang.String class object

cheewah05:02:26

Does this mean what I'm trying to achieve is not feasible?

hiredman06:02:02

Hard to say, regardless I would step way back from this problem, and reexamine your original goal, and maybe ask about that

Hao Liang05:02:39

Hello. How can I extend a java (abstract) Class in clojure?

Hao Liang06:02:48

Seems I can use proxy.

Hao Liang06:02:50

Here’s an example I found from stackoverflow.

(def listener
  (proxy
    ;; first vector contains superclass and interfaces that the created class should extend/implement
    [KeyAdapter ActionListener]
    ;; second vector contains arguments to superclass constructor
    []
    ;; below are all overriden/implemented methods
    (keyPressed [event]
      (println "Key pressed" event))
    (actionPerformed [action]
      (println "Action performed" action))))

seancorfield06:02:09

There are some limitations with proxy so it'll depend on exactly what you need. See https://ask.clojure.org/index.php/3360/support-abstract-base-classes-with-java-only-variant-reify for example.

Hao Liang06:02:33

@U04V70XH6 Ok, Thanks. In fact I was learning netty from their official documentation. I was told to extend some classes like`ChannelInboundHandlerAdapter` to write a simple “discard server”. So I wondered how to do this kind of stuff in clojure. Or is there any idiomatic way to do things like that?

seancorfield06:02:48

@U0NCTKEV8 May be able to offer some advice as he's worked with Netty (and I suspect he may have more general insight into extending abstract classes too).

seancorfield06:02:58

(we have (proxy [io.netty.channel.ChannelInboundHandlerAdapter] [] ...) in the code at work)

Hao Liang07:02:52

Ok, thank you.

hiredman18:02:19

we have the proxy of ChannelInboundHandlerAdapter, but now (and I forget why exactly I decided we needed this instead of the proxy) we also have a defrecord that implements the interface (instead of the abstract class), and implements all the methods by either calling a passed in clojure function, or passing it on through the context

hiredman18:02:08

the defrecord in that case basically does the same thing for us a the abstract class does, but lets us override different parts by just passing in different clojure functions when constructing the record

Hao Liang02:02:58

@U0NCTKEV8 Thanks. I’ve tried using the proxy of ChannelInboundHandlerAdapter and tried to add it to the channel pipeline as an instance of ChannelHandler. But I got the following error: >

java.lang.ClassCastException: class clj_netty_demo.core.proxy$io.netty.channel.ChannelInboundHandlerAdapter$ff19274a cannot be cast to class [Lio.netty.channel.ChannelHandler;
I’m getting confused. I’ve thought the proxy is something like Anonymous class In java, but seems it’s not correct.

Hao Liang02:02:15

Here’s my code:

(defn- ->handler []
  (proxy [ChannelInboundHandlerAdapter]
         []
    (channelRead [this ctx msg]
      ;;(.release msg)
      (try
        (while (.isReadable msg)
          (print (.readByte msg))
          (flush))
        (finally
          (ReferenceCountUtil/release msg))))
    (exceptionCaught [this ctx cause]
      (.printStackTrace cause)
      (.close ctx))))

seancorfield02:02:45

[Lio.netty.channel.ChannelHandler; means "array of" so you're passing your proxied object to something that is expecting an array of channel handlers. You'll need to look at the stacktrace and see where exactly that error is coming from @U031NJ4ERUP

seancorfield02:02:04

Example:

dev=> (class (into-array Long [1 2 3]))
[Ljava.lang.Long;
So that's Long[] in Java terms: an array of Long objects.

Hao Liang02:02:20

Ahh, that’s right. I just forgot [L means “array of”. Thanks a lot!

Leon08:02:09

Hi guys I have this map here (def group-by-data {:data [{:_sub-by "leon", :items [{:bags 3, :weapons "Pistols"}]}]}) and I would to transform it to be something this {:data [{:bags 3, :weapons "Pistols", :_sub-by "leon"}]} any ideas on how to go about this?

Leon08:02:28

I was initially thinking the process of transforming it to be maybe inside a function

pyry09:02:48

Depends on what exactly you're trying to achieve and what your data looks like. Do you just want to extract the contents of :items to the underlying map? Is the sequence under :items always going to be exactly of length 1? Are the keys for entries under :items always going to be the same?

Leon09:02:58

yes I would want to extract the contents of :items to the underlying map, meaning the sequence under :items will be of the same length

Leon09:02:52

so that the new map would be something like this {:data [{:bags 3, :weapons "Pistols", :_sub-by "leon"}]} I hope that makes sense 😅

pyry09:02:45

Makes sense, sure. Even so, how about the two other questions? 1. Is the sequence under `:items` always going to be exactly of length 1? 2. Are the keys for entries under `:items` always going to be the same?

Leon10:02:03

Yes the sequence under :items will be of length 1 and the keys are always going to be the same

pyry10:02:51

(update group-by-data :data
          (fn [data]
            (->> 
              data
              (map
                (fn [{:keys [:items]
                      :as datum}]
                  (->> (first items)
                       (into (dissoc datum :items))))))))

pyry10:02:09

That should do the trick, even if it's not very pretty.

Leon11:02:03

Hehe no worries, thanks a lot

Lycheese09:02:18

How should I go about restricting the maximum amount of threads calling a specific function to n? The function I need to call does (potentially blocking) i/o and reports whether it's successful.

Lycheese09:02:42

That looks exactly like what I was looking for. I just realized I used it before too 😅 Thank you.

👍 1
Matej Šarlija09:02:26

What would "Invalid token : : " be in context of slurping?

Matej Šarlija09:02:54

I get a giant string from an API and can't seem to make progress in lieu of read-string

flowthing10:02:30

(clojure.edn/read-string "::foo")
Execution error at vault.code/eval5456 (REPL:382).
Invalid token: ::foo
https://clojure.org/guides/weird_characters#autoresolved_keys

Stuart13:02:13

How do I pass a java interop function to another function and call it in the function ?

(defn foo [x]
  (x 2 3))

(foo +)        ;; works
(foo *)        ;; works
(foo /)        ;; works
(foo -)        ;; works
(foo Math/pow) ;; doesn't work
This doesn't work either:
(foo #(Math/pow))

Stuart13:02:24

I'd like to be able to pass a function that can be either one of my functions, or a java interop function or a regular clojure function.

ghadi14:02:32

interop functions are not first class Clojure functions

Stuart14:02:09

So, If I want to use interop functions. I can wrap them ?

(foo (fn [x y]
       (Math/pow x y)))
This works?

ghadi14:02:11

need to do (fn [x y] (Math/pow x y))

Stuart14:02:15

cool, thanks

mbjarland14:02:58

Or (foo #(Math/pow %1 %2))

Alex Miller (Clojure team)14:02:05

but fyi, in Clojure 1.11, you can pass the new clojure.math/pow function!

Alex Miller (Clojure team)14:02:43

(and we are considering some additional interop that would help in cases like this for 1.12)

👍 3
mbjarland15:02:40

@U064X3EF3 what would it take (or is it even possible) to have java functions with a suitable arity “just work” as clojure functions? Tried staring at some decompiled clojure code and wrapping my head around the moving pieces but think I came up a tad short.

mbjarland15:02:24

mostly wondering whether it falls into “no it’s just not doable” or “yes, in theory but would have horrible performance” kind of bucket

ghadi15:02:20

it is doable, with high performance

Alex Miller (Clojure team)15:02:58

it is doable, but complicated :)

dumrat14:02:03

Invoking this fn results in an error at repl

(defn from-file
  "Read from file"
  [filename]
  (->> (with-open [r ( filename)]
         (cheshire/parse-stream r))
       (map (fn [x] (clojure.walk/keywordize-keys x)))
       (map (fn [x] (update x :open-time #(jt/instant %))))
       (map (fn [x] (update x :close-time #(jt/instant %))))))
; Error printing return value (IOException) at .BufferedReader/ensureOpen (BufferedReader.java:122). ; Stream closed I assume this means that the file is already closed when repl tries to print stuff. What should I do here?

dpsutton14:02:07

> If the top-level object is an array, it will be parsed lazily (use > `parse-strict’ if strict parsing is required for top-level arrays. From this and your use of map i’m assuming you have a top level array. Your intuition is correct, you are working on the structure after the file has been closed and the whole structure is not in memory but is lazy.

👍 1
sheluchin15:02:56

I'm using spec in my tests via s/valid?. When it fails, I edit it to s/explain to look at the error. A little tedious. Is there a better way?

dpsutton15:02:54

is can take an optional docstring. (is (s/valid? spec value) (s/explain spec value)) should print the explanation in the failure case. Caveat as you know is that it can get decently verbose

dpsutton15:02:30

(s/def ::x (s/keys :req-un [::k]))
(s/def ::k even?)
(s/valid? ::x {:k 3})
(is (s/valid? ::x {:k 3}) (s/explain ::x {:k 3}))

metadata=> (is (s/valid? ::x {:k 3}) (s/explain ::x {:k 3}))
3 - failed: even? in: [:k] at: [:k] spec: :nocommit.metadata/k
false

sheluchin15:02:57

Perfect, thanks!

fadrian16:02:24

Trying to base64 encode a string. I've required java.util.Base64 in my source file, but keep getting an error when compiling:

(ns mercator-server.core
    (:require 
              [java.util.Base64 :refer [.encodeToString getEncoder .getBytes]]))

clj꞉user꞉> 
; Evaluating file: core.clj
; Syntax error (FileNotFoundException) compiling at (c:\Users\fadrian\Projects\mercator-server\src\mercator-server\core.clj:1:1).
; Could not locate java/util/Base64__init.class, java/util/Base64.clj or java/util/Base64.cljc on classpath.
; Evaluation of file core.clj failed: class clojure.lang.Compiler$CompilerException
I'm running a CLI tools project under Calva. How can I get this to compile without the error message?}

dpsutton16:02:37

java classes must be :imported rather than required. Require is specific to clojure code

fadrian16:02:07

Thank you.

dpsutton16:02:21

(ns foo
  (:import java.util.Base64))
(let [encoder (Base64/getEncoder)
      decoder (Base64/getDecoder)]
  (String. (.decode decoder (.getBytes (.encodeToString encoder (.getBytes "bob"))))))

fadrian16:02:02

Thanks. I found the encoder code already. I was just getting the compilation error. I've checked it out and everything works now. Thanks again.

ghadi16:02:52

imports are only for classes, not methods

fadrian21:02:48

I have a couple of CLI tools projects structured as follows:

|
|
+-libname +
|         |
|         +--deps.edn
|         |
|         +--src/libname +
|                        +--A.clj (libname.A namespace)
|                        +--B.clj (libname.B namespace)
|                        +--C.clj (libname.C namespace)
|
|
+-- server +
|          +--deps.edn
|          +--src/server-- +
|                          + core.clj (server.core namespace)
The deps.edn for the two files are as follows:
;; libname/deps.edn
{:paths
 ["src/libname"]

 :deps
 {org.clojure/clojure {:mvn/version "1.10.3"}
  org.clojure/data.csv {:mvn/version "1.0.0"}
  }
 
 :jvm-opts ["-XX:-OmitStackTraceInFastThrow"]
;; server/deps.edn
{:paths
 ["src/server"]

 :deps
 {org.clojure/clojure {:mvn/version "1.10.3"}
  compojure/compojure {:mvn/version "1.6.2"}
  http-kit/http-kit {:mvn/version "2.5.3"}
  ring/ring-json {:mvn/version "0.5.1"}
  server/A {:local/root "../libname"}}

 :jvm-opts ["-XX:-OmitStackTraceInFastThrow"]}
 }
My ns declaration in server/core.clj is:
(ns mercator-server.core
    (:require [compojure.core :refer [defroutes GET POST]]
              [org.httpkit.server :refer [run-server]]
              [ring.middleware.json :refer [wrap-json-body]]
              [libname.A :refer [translate]])
    (:import  [java.util Base64]))
Here, I'm attempting to call the translate function from libname/A.clj (which depends on B.clj and C.clj). However, when I attempt to compile the code I get an error:
; Evaluating file: core.clj
; Syntax error (FileNotFoundException) compiling at (c:\Users\fadrian\Projects\server\src\server\core.clj:1:1).
; Could not locate libname/A__init.class, libname/A.clj or libanme/A.cljc on classpath.
; Evaluation of file core.clj failed: class clojure.lang.Compiler$CompilerException
What am I doing wrong in the deps.edn for the server library and why is it not finding libname/A, when I tell it the :local/root for it in the server's deps.edn?

hiredman21:02:19

a good first step would be to delete server/.cpcache and restart your repl

fadrian21:02:58

I tried that - I'm still getting the same error when I try to compile/load the file server/core.clj.

hiredman21:02:01

check if the src path for libname is in clj -Spath in server

hiredman21:02:45

what does the ns name in the A.clj file look like?

hiredman21:02:35

your :paths is bogus

hiredman21:02:40

it should just be "src"

hiredman21:02:46

in both projects

hiredman21:02:24

:paths ["src"]

fadrian22:02:54

Thank you. It wasn't just the src thing - it was also some -/_ confusion in a couple of the filenames.

Cora (she/her)21:02:39

might want to thread this sort of thing in the future