Fork me on GitHub
#beginners
<
2022-02-17
>
Eric00:02:25

Does anyone know the historical reason some returns true or nil instead of true or false? Is there a similar function that only returns a boolean value?

hiredman00:02:34

some is actually not going to return true all the time either

hiredman00:02:54

some returns the result of apply pred to the elements when it is truthy

hiredman00:02:14

user=> (some #{:fred} [:fred])
:fred
user=>

Eric00:02:26

Thank you for clearing that up. What is the canonical way to test if any of the values in a collection (mainly a vector or list) match my pred?

hiredman00:02:59

some is there exists and every is for all

Eric00:02:31

Ok, so I need to do my own conversion to bool if I want only a boolean?

hiredman00:02:46

if you really need booleans instead of truthy/falsey you can use (comp boolean some)

1
hiredman00:02:32

but most things in clojure (if, or, and, etc) are fine with nil and false as false and everything else as true

Eric00:02:10

Oh, that's great to know. Thanks.

dpsutton00:02:24

often the only times i care about true booleans is i’m grouping by a predicate and then i need true or false. (let [{missing false valid true} (group-by pred coll)] ...). Most other times truthy and nil punning are just fine

Eric01:02:54

Ok, great. Thank you. I was worried that Clojure was stricter about the nil/false distinction.

quan xing01:02:47

I use the slf4j in deps.end

:deps{org.slf4j/slf4j-api {:mvn/version "1.7.36"}}

user=> (require '[clojure.tools.logging])
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See  for further details.
how can I bind the logger implementation?

seancorfield01:02:13

@imxingquan You've specified the API (good) but you also need to specify an implementation: slf4j-simple is probably what you want. There's also slf4j-nop (for no logging) and several others (including bridges for other logging libraries)

seancorfield01:02:31

(Java logging is a giant mess!)

seancorfield01:02:08

This may help https://lambdaisland.com/blog/2020-06-12-logging-in-clojure-making-sense-of-the-mess (or it may confuse further). At work, we've decided to use log4j2 but that has its own peculiarities as far as getting it all set up with deps.edn etc.

quan xing01:02:02

user=> (log/info "hello")
[main] INFO user - hello
Yeah! I saw the result. thanks

quan xing01:02:40

I add logback.xml to resources dir, but there's no log file in ~/log/taskphone dir

apiology01:02:50

This is a guess, but many things that aren’t your shell don’t interpret ~ - could you try with a full path?

quan xing02:02:24

I forgot reference ch.qos.logback/logback-classic {:mvn/version "1.2.10"}in deps.edn

quan xing02:02:43

I use the project/jar, show error

$ clojure -X:project/jar :main-calss myapp.core
$ java -jar project.jar 
no main manifest attribute, in project.jar

seancorfield02:02:49

Because :main-calss should be :main-class on that first line?

seancorfield02:02:50

Also make sure your myapp.core's ns form contains (:gen-class) so that it will produce a Java-compatible class with your -main function in it.

quan xing02:02:23

main code:

(ns bzsczx.core
  (:gen-class))

(defn greet
  "Callable entry point to the application."
  [data]
  (println (str "Hello, " (or (:name data) "World") "!")))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (greet {:name (first args)}))
$ clojure -X:project/jar :main-class bzsczx.core
:aot is not recommended for a 'thin' JAR!
Compiling bzsczx.core ...
Building thin jar: project.jar
Processing pom.xml for {net.clojars.bzsczx/taskphonecall {:mvn/version "0.1.0-SNAPSHOT"}}
$ java -jar project.jar 
no main manifest attribute, in project.jar

seancorfield03:02:04

Oh, you're building a library JAR. You need to build an uberjar for an application.

seancorfield03:02:14

What is :project/jar?

seancorfield03:02:03

Ah, it's from https://github.com/practicalli/clojure-deps-edn yes? You want :project/uberjar

quan xing03:02:18

I build an uberjar use project/uberjar. and java -jar uber.jar after show me user > not run main function immediately and enter the Clojure runtime

seancorfield03:02:42

Sounds like you didn't specify :main-class?

quan xing03:02:30

I specify :main-class everyting is ok. thanks 🙂

practicalli-johnny06:02:38

As :project/uberjar is a user level alias, it's not going to know the specific main class name (which is different for each project). Template tools that generate a Clojure project (clj-new or deps-new) may include the main-class value when a new project is generated, simplifying the alias use.

quan xing08:02:16

Why use let to define variables in functions instead of def

seancorfield08:02:18

def always creates a top-level (global) definition.

seancorfield08:02:28

let creates local bindings.

seancorfield08:02:43

(and that Q seems to have nothing to do with the rest of this thread)

quan xing09:02:01

(def seq1 [1 2 3 4 3 4])
  (let [col1 #{}]
    (doseq [x seq1]
      (conj col1 x))
    (println col1))

  ;; I want this result col1{1 2 3 4}
  ;; pseudocode
  var col1 = #{}
  for (n in seq1){
      if(n is not in col1){
          add n to col1                 
      }
  }
The pseudocode up is the logic I want, but I don't know how to write it in Clojure

flowthing09:02:29

Other than than, in Clojure, collections are immutable. That means that this:

(doseq [x seq1]
      (conj col1 x))
does not do what it does in imperative languages. It does not modify col1 to add x into it. Instead, for each element in seq1, it creates a copy of col1 with x added, then throws the result away. doseq is only for doing things that have side-effects.

V09:02:46

There is no reason to check if X is in col1, because col1 is a set. You cannot have duplicate values anyways

☝️ 1
emilaasa09:02:44

(def coll [1 2 3 4 3 4])
(set coll) ; => #{1 4 3 2}

agile_geek10:02:27

If what you want is a set of the unique values in seq1 (and a set will by definition only contain unique values) you can simply create a set from the seq1 collection. i.e. (set seq1) However, this won't have retained the order that the elements appear in in seq1. For that you could use sorted-set which takes each element as a separate argument so you need to apply it. i.e. (apply sorted-set seq1)

agile_geek10:02:06

To turn that back to a vector use vec i.e. (vec (apply sorted-set seq1))

jaihindhreddy11:02:18

(set col1) best represents your pseudo-code. But, you say you want the result 1 2 3 4. If ordering matters, then you can use (distinct col1) to get a sequence of all distinct elements in the order of first-appearance in the input-seq. (sort (set col1)) and (into (sorted-set) col1) are both ways to get the distinct elements in their sorted order (sorted according to compare).

quan xing13:02:23

I don't want from seq1 to set. I want to do something.

;; pseudocode
  var col1 = set //memoize n 
  for (n in seq1){
      if(n is not in col1){
          add n to col1
          do something !!                 
      }else{
          nothing to do!!
      }
  }
like this how to write in Clojure

quan xing14:02:11

(def seq1 [1 2 2 3 4 1 3])
  (let [col1 (atom #{})]
    (doseq [x seq1]
      (if-not (contains? @col1 x)
        (do
          (println "do something " x)
          (reset! col1 (conj @col1 x)))
      ))
output:
do something  1
do something  2
do something  3
do something  4
all right? :)

V14:02:15

Is there a reason you use an atom for this? You could create this collection using reduce instead

(def seq1 [1 2 2 3 4 1 3])
(reduce (fn [coll elem] 
          (println "Do something" elem)
          (if (some #(= elem %) coll) ;; Checks if element is already in list
            coll
            (conj coll elem))) [] seq1)
should solve it without atoms

quan xing14:02:59

I wrote the code like imperative programming, no functional thinking?

V14:02:16

Ah i see. Above example i wrote would be the functional way to do it.

dorab17:02:59

In this case, it might be slightly better to use a set in the reduce rather than a vector. So

(def seq1 [1 2 2 3 4 1 3])
(reduce (fn [coll elem] 
          (println "Do something" elem)
          (if (coll elem) ;; Checks if element is already in set
            coll
            (conj coll elem))) #{} seq1)
Some loops (those that accumulate something) can often be replaced by reduce in a functional style.

kokonut12:02:26

hi, I wonder how you spec functions. With simplified examples, I sometimes like to put valid? at the input and output of the function like below. But this approach may cause some degree of performance issue, if I use extensibly, right?

(defn add-seven [x]
   {:pre [(s/valid? int? x)]
    :post [(s/valid? int? %)]}
  (+ x 7))
Otherwise, I could write like this using fdef.
(s/fdef add-eight
  :args (s/cat :x int?)
  :ret int?)
(defn add-eight [x]
  (+ x 8))
But this fdef block will only be checking when I turned the instrument on, as far as I know. Like below.
(require '[clojure.spec.test.alpha :as stest])
(stest/instrument `add-eight)
Honestly, this instrumentation is a bit cumbersome when I want my specs to check everything I write, as I have to write this (stest/instrument something) line for every function with fdef? I would like to learn what would be considered a good practice.

Alex Miller (Clojure team)12:02:48

You can call (stest/instrument) to instrument everything spec’ed

👍 1
Alex Miller (Clojure team)12:02:48

Or you can pass it a collection of symbols and use enumerate-namespace or other fns in stest to be more targeted if needed

clojure 1
kokonut12:02:10

Oh I didn't know I could (stest/instrument) and all fdefs are up. That's amazing. Thanks!!

Alex Miller (Clojure team)12:02:07

Same with unstrument too

lepistane12:02:36

Hello, I am trying to implement auto-reconnect websocket logic (with fail safes so that i am reasonable user). I am using atoms to hold the websocket client object which i later use for reconnects. Sometimes i WS client might not even be created so i made a loop to attempt to create new connection and store it in atom. My question is will i create memory leak or will WS client objects be properly garbage collected if i (swap! connection assoc :socket (connect client url)) I realize that swap might be executed multiple times and that it should be used for pure functions but reconnect logic shouldn't happen often and i my thinking is if connections get garbage collected without being closed - all is well. If my whole approach is backwards would you mind sharing resources how this could be done properly? Thank you for your time

ghadi12:02:48

swap! potentially executing multiple times doesn't matter above. the final argument to swap!, the result of calling (connect …) is calculated only once.

ghadi12:02:21

You are calling swap with a pure function: assoc

ghadi12:02:12

If you are calling swap! in a loop, connecting multiple times, YMMV

ghadi12:02:50

Your clients/connections will be GCed when nothing hangs on to them. To prevent the map inside the atom from hanging on, when a connection breaks or closes, swap! dissoc it from the atom

lepistane13:02:03

@U050ECB92 thanks a lot, it makes sense.

Hao Liang14:02:56

Hello! When should I use defonce instead of def? Below is a code snippet taken from shadow-electron-starter repo. defonce is used. I’d like to know is def ok here? What’s the difference between defonce and def?

(defonce state (atom 0))

(defn root-component []
  [:div
   [:button
    {:on-click #(swap! state inc)}
    (str "Clicked " @state " times")]])

Alex Miller (Clojure team)15:02:03

reloading the namespace will not recreate the state var

Hao Liang15:02:02

Ah! Then when will the namespace be reloaded? I knew that editing code will cause hot reload during development, but can this happen in a running application? Or is it only a convenience for dev?

Godwin Ko00:02:30

normally namespace won’t reload in production application, it is mainly used for repl-driven development, so that you don’t need to restart the entire app for code changes

Hao Liang01:02:24

Ah ok, thanks!

John Bradens21:02:45

Let's say I have a string like "the quick brown fox" and I want to replace all the spaces with hyphens... how can I do this in clojure?

hiredman21:02:09

there is a clojure.string namespace that wraps a lot of that stuff, but I tend to just call the methods on String

John Bradens21:02:33

Ok. Is this better than using the "replace" function from clojure.core?

hiredman21:02:36

I've never ever used clojure.core/replace

hiredman21:02:45

might as well just use clojure.core/map

hiredman21:02:44

(replace technically has some optimizations for vectors)

Alex Miller (Clojure team)21:02:57

do you mean clojure.string/replace? b/c that has some nice affordances

hiredman21:02:02

well, neither replace I guess

Alex Miller (Clojure team)21:02:11

(clojure.string/replace the-string " " "-") ?

hiredman21:02:32

but the replace in clojure.string is just because I habitually use the methods on String directly unless someone reminds me about clojure.string

quoll16:02:00

One advantage is cross platform portability. This is especially important if you’re writing code in a library

hiredman21:02:59

the replace in clojure.core, I just use map for that kind of thing

Alex Miller (Clojure team)21:02:10

in particular, the pattern/f-of-match arity can often be useful in clojure.string/replace

John Bradens21:02:00

What about if I want to delete certain characters from a string? Like if I want to turn "quick #brown !fox @ " into "quick-brown-fox" (I'm thinking about this in context of URLs)

hiredman21:02:27

replace with the empty string

John Bradens21:02:01

Can I replace the characters with "" ? Or is there a better way to delete things? Thanks for the help guys

hiredman21:02:00

"better way" is extremely dependent, replacing with the empty string is likely to be good

John Bradens21:02:09

How do I represent special characters? I'm thinking I will do (replace string "???" "")? What do I put for ???... do I put all the special characters I can think of, or is there a more succinct way?

hiredman21:02:55

depends what you mean by special characters

John Bradens21:02:22

Anything that should not show up in a URL

hiredman21:02:33

there are unicode escapes and escape seequences for certain control characters

hiredman21:02:06

you could just use java's built url encoding and decoding

Alex Miller (Clojure team)21:02:16

you can use a regex for grabbing multiple chars

Alex Miller (Clojure team)21:02:31

but actually using a url-encode/decode is probably better for sure

John Bradens21:02:45

Ok cool, I'll look more into that 🙂

John Bradens22:02:01

Ok a very beginner question... I found out that ring has a function ring.util.core/url-encode. I tried to use it in the repl with

(ring.util.codec/url-encode "this is my string")
But I get errors:
Execution error (ClassNotFoundException) at java.net.URLClassLoader/findClass (URLClassLoader.java:433).
ring.util.codec
I also tried to require ring but I think I'm doing that wrong too
(require '[ring/ring-codec "1.2.0"])
Here's the error I get:
Execution error (ClassCastException) at user/eval2107 (REPL:1).
class java.lang.Character cannot be cast to class clojure.lang.Named (java.lang.Character is in module java.base of loader 'bootstrap'; clojure.lang.Named is in unnamed module of loader 'bootstrap')
What am I doing wrong here? I'm thinking it must be a syntax thing or I forgot a command?

hiredman22:02:58

ring is a separate library

John Bradens22:02:07

What's the correct way for me to require everything? 🙂

hiredman22:02:30

a library is a collection of code (clojure namespace, java classes, etc), when you launch you jvm process it needs all the libraries available at startup, so typically however you are launching that jvm process will need to be told what libraries you are using

hiredman22:02:45

you cannot require "everything"

hiredman22:02:05

[ring/ring-codec "1.2.0"]
is one way of specifying a maven coordinate, ring is the group id, ring-codec is the artifact-id, and "1.2.0" is the version of that artifact

hiredman22:02:45

a maven coordinate is one of many ways you might specify to whatever tool you use that starts a jvm what libraries is should have available

John Bradens22:02:07

So why doesn't

(require '[ring/ring-codec "1.2.0"])
work?

hiredman22:02:24

because require takes clojure namespaces, not maven coordinates

John Bradens22:02:36

Ohhhh I see. How do I do the namespace?

hiredman22:02:36

and by the time you are calling require, you are in a running jvm

John Bradens22:02:58

So do I need to add it to a project.clj file before just doing a repl out of the blue?

hiredman22:02:07

and the set of libraries available to that jvm is already set (not entirely true, but close enough)

John Bradens22:02:29

Ohhhhhhhhh ok. Thanks! I'll try to add it to project.clj and then try again 🙂

hiredman22:02:07

once you have added it to you project.clj, then that library will be available to use, but you'll need to use require to load namespaces out of that library

hiredman22:02:26

(added it to your project.clj and started a new repl)

John Bradens22:02:17

Ok sweet thank you!