Fork me on GitHub
#beginners
<
2023-04-13
>
Charlot01:04:18

I'm trying to setup a library with tools.build for building (and maybe running tests) I'm planning on running the tests and builds through github actions. The actions runner ran the command clojure -T:build ci And got -T is no longer supported, use -A with repl, -M for main, or -X for exec as an error. The project was started using neil, which if I understand correctly used deps-new to generate the template.

Charlot01:04:30

I can't get my local clojure to throw that error

Charlot01:04:59

Okay, moving to latest CLI fixed it.

2
Alex Miller (Clojure team)03:04:18

yeah, there was a very old -T switch that was deprecated and then taken over by newer functionality and you had some version in between those

Sam07:04:23

Hi, how do I import this library? https://github.com/knuddelsgmbh/jtokkit I added [com.knuddels/jtokkit "0.2.0"] to project.clj, and tried (:import [com.knuddels.jtokkit Encodings]) but I get ClassNotFoundException.

delaguardo07:04:33

did you restart the repl after adding the library to project.clj? and where you put an import statement? into the namespace declaration?

Sam07:04:41

I did, I also ran lein deps Yes! I have a bunch of java library imports just beside it so it seems to be the correct place

(ns core
  (:import [com.google.auth.oauth2 GoogleCredentials]
           [com.google.api.client.http GenericUrl]
           [com.google.auth.http HttpCredentialsAdapter]
           [com.knuddels.jtokkit Encodings]
           [com.google.api.client.http.javanet NetHttpTransport]
           [com.google.api.client.http.json JsonHttpContent]
           [com.google.api.client.json.jackson2 JacksonFactory])
  (:require [clj-http.client :as http]
            [clojure.walk :as walk]
            [support :as s]
            [ring.util.response :as r]
            [org.httpkit.server :as app-server]
            [ring.middleware.json :as rjson]
            [ring.middleware.session :as session]
            [compojure.core :as comp]
            [clojure.string :as str]
            [clojure.data.json :as json])
  (:gen-class)) 

delaguardo07:04:21

looks good. maybe something is wrong with that specific version. do you know where to find downloaded artifact and how to check it's content?

Sam08:04:02

I can at least verify that it exists, if I extract the jar /home/sam/.m2/repository/com/knuddels/jtokkit/0.2.0/jtokkit-0.2.0.jar I can see the class inside it: com.knuddels.jtokkit.Encodings.class.

delaguardo08:04:38

ok. interesting) is it the same class Clojure complains about? it should be the exception description

Sam08:04:54

Is this the information you are referring to? 🙂

Caused by java.lang.ClassNotFoundException
   com.knuddels.jtokkit.Encodings
from
2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling src/core.clj at (1:1)
   #:clojure.error{:phase :execution,
                   :line 1,
                   :column 1,
                   :source "/home/sam/git/project/src/core.clj"}
             Compiler.java: 7665  clojure.lang.Compiler/load
                      REPL:    1  core/eval16331
                      REPL:    1  core/eval16331
             Compiler.java: 7194  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  Caused by java.lang.ClassNotFoundException
   com.knuddels.jtokkit.Encodingsnrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  833  java.lang.Thread/run

1. Caused by java.lang.ClassNotFoundException
   com.knuddels.jtokkit.Encodings

Sam08:04:45

I guess I should try the project with pure java, just to see if it works that way.

Sam08:04:09

Worked flawlessly in java!

delaguardo08:04:16

strange, there shouldn't be a difference. i can check when I'll have my laptop around

❤️ 2
delaguardo08:04:02

❯ clj -Sdeps '{:deps {com.knuddels/jtokkit {:mvn/version "0.2.0"}}}'
Downloading: com/knuddels/jtokkit/0.2.0/jtokkit-0.2.0.pom from central
Downloading: com/knuddels/jtokkit/0.2.0/jtokkit-0.2.0.jar from central
Clojure 1.11.1
user=> (import '[com.knuddels.jtokkit Encodings])
com.knuddels.jtokkit.Encodings
user=> (Encodings/newDefaultEncodingRegistry)
#object[com.knuddels.jtokkit.DefaultEncodingRegistry 0x32ae365a "com.knuddels.jtokkit.DefaultEncodingRegistry@32ae365a"]
I don't think the problem caused by that lib.

Sam08:04:19

You're right - (import '[com.knuddels.jtokkit Encodings]) works fine.

Sam08:04:09

Alright, that was annoying. cider-quit->cider-jack-in fixed it. I have run cider-restart multiple times..

delaguardo08:04:18

aha) cider-restart doesn't "restart" the repl. It is for reloading changed namespaces. After adding new library you have to do cider-quit and then cider-jack-in

Sam08:04:32

I see... Thank you!

delaguardo08:04:36

Classpath is calculated at the very beginning of starting a new process and will stay the same.

❤️ 2
Leon08:04:05

Hello how are you? How to use goog.functions.debounce for search input?

rolt09:04:19

(goog.functions/debounce (fn [] ...your search) 500) does that work for you ?

Leon10:04:56

[:input {:type "text"
                  :placeholder "Search..."
                  :onChange
                  (fn [e]
                    (let [search-term (.-value (.-target e))]
                      (goog.functions.debounce
                       (fn []
                         (swap! app-state assoc-in [:customers] [])
                         (http-get "/customers" {:search search-term}
                                   (fn [results]
                                     (swap! app-state assoc-in [:customers] results)))))))}]
@U02F0C62TC1, this doesn't work...

Leon10:04:02

I add required at the top

(ns client.core
  (:require [ajax.core :refer [GET POST PUT PATCH DELETE]]
            [goog.functions]
            [reagent.dom :as rdom]
            [reagent.core :as reagent]
            [clojure.string :as s]))

rolt10:04:15

goog.functions.debounce => goog.functions/debounce

rolt10:04:27

and you're missing the time interval after the function

Leon10:04:54

[:input {:type "text"
                  :placeholder "Search..."
                  :onChange
                  (fn [e]
                    (let [search-term (.-value (.-target e))]
                      (goog.functions/debounce
                       (fn []
                         (swap! app-state assoc-in [:customers] [])
                         (http-get "/customers" {:search search-term}
                                   (fn [results]
                                     (swap! app-state assoc-in [:customers] results)))) 1000)))}]

Leon10:04:02

@U02F0C62TC1, I updated like this, but same result

Leon10:04:10

My import for goog.functions is correct?

rolt10:04:31

add the time interval as a 2nd argument

Leon10:04:26

like this?

rolt10:04:41

oh sorry didn't see it, what's the error ?

Leon10:04:42

[:input {:type "text"
                  :placeholder "Search..."
                  :onChange
                  (fn [e]
                    (let [search-term (.-value (.-target e))]
                      (goog.functions/debounce
                       (fn []
                         (swap! app-state assoc-in [:customers] [])
                         (http-get "/customers" {:search search-term}
                                   (fn [results]
                                     (swap! app-state assoc-in [:customers] results))))
                       1000)))}]

rolt10:04:27

but the debounce looks fine to me, did it work without debounce ?

Leon10:04:48

yes, it works without debounce, but after debouce, I didn't get the customers api call

Leon10:04:46

is this correct?

rolt10:04:47

oh right you need to call it, debounce will return a function

Leon10:04:10

also, where should I call that? appreciate your advice, @U02F0C62TC1

rolt10:04:13

[:input {:type "text"
         :placeholder "Search..."
         :onChange (goog.functions/debounce
                    (fn [e]
                      (let [search-term (.-value (.-target e))]
                        (swap! app-state assoc-in [:customers] [])
                        (http-get "/customers" {:search search-term}
                                  (fn [results]
                                    (swap! app-state assoc-in [:customers] results)))))
                    1000)}]
something like that, parentheses are most likely wrong it's copy /paste + modification

Leon10:04:53

awesome, it works!

rolt15:04:28

btw it's probably better if you define the function outside the component render function. (reagent.core/with-let [onChange (goog.functions/debounce (fn [e] ...)))] [:input {... :onChange onChange}]) so that the function stays the same across render cycles. Otherwise the debounce will be reset at each new render. That way it'll still work even if you use the :value property for instance (have a look at the "form-1 form-2 form-3" reagent doc if you don't know those concepts)

Leon15:04:07

(defn search-customer [search-key]
  (swap! app-state assoc-in [:customers] [])
  (http-get "/customers" {:search search-key}
            (fn [results]
              (swap! app-state assoc-in [:customers] results))))

Leon15:04:25

@U02F0C62TC1, I made like this, am I correct?

Leon15:04:49

[:input.search {:type "text"
                         :placeholder "Search Customer by name..."
                         :onChange (gfunc/debounce
                                    (fn [e]
                                      (let [search-key (.-value (.-target e))]
                                        (search-customer search-key)))
                                    1000)}]

Leon15:04:52

and this is input

rolt15:04:01

nope the debounce will still be reset on each render here

Leon15:04:19

i see...

Leon15:04:45

can you write me the example if you don't mind?

rolt15:04:31

also if you use (def onChange (gfunc/debounce bla bla bla)) (defn my-component [] [:input {:onChange onChange}]) then every instance of the component will share the same debounce

rolt15:04:37

(maybe that's what you want ?)

Leon15:04:32

ah i see, so make the onChange component as global to access through other functions?

rolt15:04:45

that's why I'd use:

(defn my-component [] (reagent.core/with-let [onChange (goog.functions/debounce
                    (fn [e]
                      (let [search-term (.-value (.-target e))]
                        (swap! app-state assoc-in [:customers] [])
                        (http-get "/customers" {:search search-term}
                                  (fn [results]
                                    (swap! app-state assoc-in [:customers] results)))))
                    1000)]
  [:input {:onChange onChange}]

rolt15:04:30

that way each instance of my-component gets it's own debounce timer invidually

rolt15:04:22

it does not matter much in your case: you're using uncontrolled input so the component does not render at each change

Leon15:04:50

yeah.. i see.. i might need to look at docs more

Leon15:04:52

still newbie

rolt15:04:39

yeah it's not easy, but it's actually exactly the same in javascript so you can read how to do it in JS too

rolt15:04:03

(well except the form-1 form-2 shortcut, in JS you'd only have form-3)

Leon15:04:44

can you give me a link to that docs form-3?

Danny Matthews10:04:39

Hi all, I'm very new to Clojure and am attempting to and a series of values from a vector. From looking online I can see that as and is a macro it can't be given as a param to apply so have tried wrapping and in a function:

(apply #(and %&) [true true false true])
but get this response:
(true true false true)
I was hoping for a single boolean. This works but I was hoping to get it running for an arbitrary number of elements:
(apply #(and %1 %2) [true false])

lsenjov10:04:45

(reduce #(and %1 %2) ...) ?

lsenjov10:04:38

So reduce is doing (and first-item second-item), then doing (and first-result third-item) down the line

lsenjov10:04:00

In case you're looking to do that with different logic functions

lsenjov10:04:31

But if you're only looking to if all of them are true or false, then every? is probably your go-to

delaguardo11:04:11

note also that reduce with #(and %1 %2) will not short circuit on the first falsey value. you have to provide a slightly more sophisticated function to call reduced when the result is definitely will be false.

👍 2
Danny Matthews11:04:37

Ah, fantastic - nice to see both approaches - thank you - much appreciated 👍

lsenjov11:04:18

(reduce #(and ...) ...) isn't really a good approach, was more my top of head "this will do the job and explain wtf is going on"

lsenjov11:04:43

Uhhh sorry, I mean every? is the better approach

🙌 2
oddsor11:04:49

every? doesn’t return the truthy value in the same way that the reduce-approach does, so that’s worth keeping in mind:

user=> (every? identity [1 2 3 4 5])
true
user=> (and 1 2 3 4 5)
5
If you want to return the truthy value I am not aware of a better way besides rolling a new function inspired by the and-macro?
user=> (def and-xs (partial reduce (fn [x y] (if x (if y y (reduced y)) (reduced x)))))
#'user/and-xs
user=> (and-xs [1 2 3 4])
4
user=> (and-xs [1 2 false 3 4])
false
user=> (and-xs [1 nil 2 false 3 4])
nil

👍 2
delaguardo11:04:21

and one more solution in case you always have a literal collection:

(defmacro and-xs [xs]
  `(and ~@xs))
will not work in case your arguments bounded to a var:
(let [v [1 2 3 4]] (and-xs v)) ;; will throw :)
probably no one should use it, but as a part of some code-golf problems might be handy

Danny Matthews11:04:44

All very interesting - thank you. I hadn't considered the truthiness value - where I come from the only things that are truthy are true and false 🙂

delaguardo11:04:03

welcome) in Clojure it is simple - nil and false are falsey, everything else is truthy)

👍 2
dominikk14:04:02

Hi! I'm currently trying to solve some problems of the excercism clojure-track. Exercism validates solutions through pre-written tests. Without going into the details of the exercise (it's about writing a binary search tree) I'm interested in some input how deal with a certain situation. I have a solution for the exercise which basically works but fails some tests because the arguments of one of my functions are ordered differently (`tree new-value`, instead of new-value tree). I chose this order because i can conveniently feed my insert function into reduce and create a binary-tree from a list of values. If I change the order according to the test specifications i cannot make reduce work, because it seems to take it's output datastructure as it's first input. Is there a clever trick to deal with these kind of situations? Maybe there's a very simple way I am missing. I can probably find a solution without using reduce but it seems so simple and clojuresque compared to recur. Thanks!

delaguardo14:04:08

you can add another function with different arguments and call your function in the body. like: (defn foo' [x y] (foo y x))

tschady16:04:30

I looked up my old solution: (reduce #(insert %2 %1) nil xs))

dominikk08:04:10

Thank you both for your input! I'll try to implement it that way. I'm wondering if some kind of general guideline can be conveyed. Is it a good idea to have a datastructure as first function argument in case the function needs to be used in a reduce context? I'm not very experienced with test environments but it appears these tests quite heavily influence the implementation details. Is that good practice?

delaguardo08:04:40

i always prefer to write (fn name [,,,] ,,,) form as the first argument for reduce. in that case stack trace looks cleaner imho.

tschady13:04:02

you could copy the tests from the exercise repo into your own project, and change them to be more idiomatic.