Fork me on GitHub
#beginners
<
2020-07-14
>
Eric Ihli02:07:47

(defn foo []
  (s/def ::baz string?))
(foo)
(s/valid? ::baz "baz")

(defn fii [k]
  (s/def k string?))
(fii ::faa)
(s/valid? ::faa "buzz")
The second example doesn't work. It can't find spec ::faa. I understand this is because s/def is a macro and it's creating a spec for the symbol k rather than the keyword that the symbol evaluates to. How can I change the code so that s/def gets the value passed in rather than the symbol k?

yuhan02:07:24

Macros are "contagious" - anything that constructs parameters to a macro must itself be a macro. So in this case try defining (defmacro fii ... to construct the call to s/def

Eric Ihli02:07:55

So I can't clear out all the specs in my namespace with something like

(let [k (keys (s/registry))
        my-k (filter #(= (namespace %) "com.my.namespace") k)]
    (map (fn [x] (s/def x nil)) my-k))

yuhan02:07:55

Alternatively, macroexpand or look up the definition of s/def - you'll find that it forwards to a function def-impl which you might be able to call directly

Eric Ihli02:07:14

Aye that's where I was about to head, to calling def-impl.

yuhan03:07:28

yup, the docstring warns "do not call directly" but it can't hurt in your case (which I assume is a dev-time utility)

seancorfield04:07:37

Seeing s/def inside another form just freaks me out -- You wouldn't put a regular def or defn inside another function!

3
jkida11:07:59

Hi everyone! Very new to clojure and im am trying to figure out how I can implement a java interface and some new additional fields to the class. For example - https://kafka.apache.org/0110/documentation/streams/developer-guide#streams_processor_process How would i declare kvStore so its accessible on this.

(reify Processor
           (init [this ctx]
             ((fn [ctx]
                (println "Processor Initialized: " ctx)
                (set! (.kvStore this) (.getStateStore ctx "user-store") ))
              ctx))

           (process [this k v]
             ((fn [key value]
                (println "Processing Message: " key value)
                (println "The Store: " (.kvStore this))
                (KeyValue. key value))
              k v))

           (close [_]
             ((fn []
                (println "Closing"))
              )))

jkida11:07:22

I basically need to add a new instance field member to the class.. not sure how to do this.

Tamizhvendan S11:07:54

You can wrap it inside a function and pass the kvStore as an argument like below

Tamizhvendan S11:07:59

(defn instance [kvStore]
  (reify Processor
   (init [this ctx]
     ((fn [ctx]
        (println "Processor Initialized: " ctx)
        (reset! kvStore (.getStateStore ctx "user-store")))
      ctx))
   (process [this k v]
     ((fn [key value]
        (println "Processing Message: " key value)
        (println "The Store: " @kvStore)
        (KeyValue. key value))
      k v))
   (close [_]
     ((fn []
        (println "Closing"))))))

(let [state (atom {:foo "bar"})
      x (instance state)]
  ;do something with the x or state 
  )

jkida12:07:06

Thanks, this is basically what i ended up with. I was searching for a while though looking for a way to actually create a class with new field members... i can now see how this is a nice clojure alternative to how java does it.

Alex Miller (Clojure team)12:07:29

You could do the same with a defrecord which has fields (and a consistent type)

jkida11:07:56

I think i found a hint that exposes it as an atom..

adamflax12:07:30

Does anyone have any experience plugging a database pooling library like Hikari into hugsql? I'm calling my hugsql fucntion like so

(db/insert-account db/datasource {:password "password" :email ""})
where datasource is a HikariDataSource object internally hugsql seems to be calling get-connection in clojure.java.jbdc and failing due to db-spec HikariDataSource (HikariPool-1) is missing a required parameter

José Javier Blanco Rivero20:01:00

Hello Adam! Did you find a solution? I am running into the same issue.

José Javier Blanco Rivero14:01:19

I solved this! I didn't paid enough attention to this in the jdbc documentation about connection pooling: "Authentication credentials must use :username (if you are using c3p0 or regular, non-pooled, connections, then the db-spec hash map must contain :user)." source: https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.3.847/doc/getting-started#connection-pooling

Stuart22:07:33

Can someone help me build a project with lein? I have a new project made with

lein new app test-foo
Then I do:
cd test-foo
lein uberjar
Compiling test-foo.core
Created /home/stuart/Source/clojure/test-foo/target/uberjar/test-foo-0.1.0-SNAPSHOT.jar
Created /home/stuart/Source/clojure/test-foo/target/uberjar/test-foo-0.1.0-SNAPSHOT-standalone.jar

(base) stuart@stuart-Z87M-D3H:~/Source/clojure/test-foo$ java target.uberjar.test-foo-0.1.0-SNAPSHOT.jar
Error: Could not find or load main class target.uberjar.test-foo-0.1.0-SNAPSHOT.jar
I can't resolve this could not find or load main class error ??? My core.clj looks like
(ns test-foo.core
   (:gen-class))
(defn -main
    "I don't do a whole lot ... yet."
    [& args]
    (println "Hello, World!"))
I haven't changed it from lein creating it. My project file:
(defproject test-foo "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
           :url ""}
  :dependencies [[org.clojure/clojure "1.10.1"]]
  :main ^:skip-aot test-foo.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Stuart22:07:30

OK, I realise my mistake, I need to do

java -jar target/uberjar/test-foo-0.1.0-SNAPSHOT.standalone.jar
ugh

noisesmith22:07:38

this is an extremely common mistake, making me think the app behavior is broken

Stuart22:07:38

i need to learn how java actually works 😄

noisesmith22:07:02

it makes sense to create the jar as a subtask of making the uberjar, but leaving it there to be used instead of the uberjar is a design flaw

Stuart22:07:34

do you know any good tutorials for using graalvm to make a native binary from my uberjar? So i get a quicker start up time?

noisesmith22:07:48

I don't recommend this as a beginner task

noisesmith22:07:06

there are a lot of things within clojure you can't use, or have to use differently, in order to make that work

noisesmith22:07:26

if you want an easy way to have graalvm and fast startup from the beginning, you could use babashka though

noisesmith22:07:07

it doesn't support all of clojure, but neither does graalvm, and it did a bunch of the hard stuff for you

Stuart22:07:16

Thanks, I'll take a look

stopa22:07:32

Hey team, I'd love to turn this -> into a lazy sequence

(defn get-all-users []
  (loop [ret []
         page (-> (FirebaseAuth/getInstance)
                  (.listUsers nil))]
    (if page
      (recur (concat
               ret
               (map user-record->map (.getValues page)))
             (.getNextPage page))
      ret)))
^ firebase gives me a paginating api for users. I'd love to give an all-users lazy sequence, which only fetches when required. This way, I can do something like:
(->> (get-all-users)
        (pmap do-some-work))
--- I haven't quite wrapped my head around lazy-seq. Am not sure how I can express the statement: keep giving values i have. if i don't, try to paginate to the next page How could I go about writing this?

noisesmith22:07:12

using lazy-seqs with side effects / external resources is messy

👍 3
noisesmith22:07:09

that said, the get-all-users could turn into something like (mapcat :values (iterate (fn [{:keys [page]} (let [nxt (.getNextPage page)] {:page nxt :values (.getValues nxt)}) ...)

seancorfield22:07:20

There's a new function under consideration for Clojure 1.11 which makes it easier to iterate over side-effecting things that produce data in pages -- you could use the code from the Jira issue until that lands.

👍 3
stopa22:07:02

will search, thanks Sean!

noisesmith22:07:34

the big problem with the code you have now is a stack of concats that could blow up

noisesmith22:07:55

concat and imperative looping don't mix nicely

stopa22:07:50

oo (mapcat :values (iterate (fn [{:keys [page]} (let [nxt (.getNextPage page)] {:page nxt :values (.getValues nxt)}) ...) looks great! Thanks team. Noob q: mind expounding on stack of concats that could blow up ? What do you mean? (if blow up stack frame, i thought loop avoided that)

seancorfield22:07:04

(I think that blog post covers it)

seancorfield22:07:40

Yup, it even gives the loop/`concat` example to show the problem...

noisesmith22:07:28

@stopachka I recommend the blog post but the tl;dr version is that a lazy-seq (the thing you are trying to understand here), works by turning "next" into a thunk (cached function of zero arguments)

👍 3
noisesmith22:07:50

calling concat on concat without yet forching the result, leads to a massive call stack before any work happens

😮 3
stopa22:07:48

this is great. thanks team!

seancorfield23:07:17

(Stuart's whole Do's/Don't series is good reading, BTW @stopachka)

❤️ 3
stopa23:07:55

> So we have a stack overflow. But why? We used recur. Our code has no stack-consuming recursion. Or does it? (cue ominous music) xD love the writing. Will look deeper