Fork me on GitHub
#beginners
<
2023-06-28
>
Evgeniy04:06:27

I'm trying to build a project using clj -T:build uber but getting this error:

Execution error (ExceptionInfo) at clojure.tools.build.api/assert-required (api.clj:49).
Missing required params for compile-clj: [:class-dir]
I have this in my deps.edn
:aliases
 {:build {:replace-deps {io.github.clojure/tools.build {:git/tag "v0.9.4" :git/sha "76b78fe"}}
          :ns-default build}}
Does anyone know how to fix this?

seancorfield04:06:29

What's in your build.clj file? Sounds like your call to compile-clj there is missing something?

Evgeniy04:06:40

(ns build
  (:require [clojure.tools.build.api :as b]))

;; the main namespace in your application:
(def main-ns 'my-webapp.handler)
;; where to compile your application:
(def class-dir "target/classes")
;; where to create the uberjar file:
(def uber-file "target/my-webapp.jar")

;; "basis" is a description of your project, as data, that includes
;; details about the paths and dependencies it uses:
(def basis (b/create-basis {:project "deps.edn"}))

(defn clean [_]
  (b/delete {:path "target"}))

(defn uber [_]
  (clean nil)
  (b/copy-dir {:src-dirs ["src" "resources"]
               :target-dir class-dir})
  (b/compile-clj {:basis basis
                  :src-dirs ["src"]
                  :clean-dir class-dir})
  (b/uber {:class-dir class-dir
           :uber-file uber-file
           :basis basis
           :main main-ns}))

seancorfield04:06:25

You have :clean-dir -- should be :class-dir

🎉 2
Evgeniy04:06:28

Ah, good catch! Thanks, that resolved the problem.

2
Rajeev Ranjan Jha17:06:43

Once again a very beginner level question.

predicates> (or 1 2 3)                                                                      
1                                                                                          
predicates> (apply or '(1 2 3))                                                             
Syntax error compiling at (predicates:localhost:41423(clj)*:240:13).                        
Can't take value of a macro: #'clojure.core/or 
Why does "or" not work with "apply"?

hiredman17:06:48

apply applies functions

hiredman17:06:58

as the error message indicates, or is a macro

👍 1
phronmophobic17:06:33

As a follow up, it's interesting to ponder why or and and are macros rather than regular functions. The answer is that or and and "shortcut" and only evaluate the arguments necessary to return an answer. However, the arguments to functions evaluate all of their arguments before passing them to the function.

2
phronmophobic17:06:20

> (and (prn "a") false (prn "b"))
"a"
Only (prn "a") and false get evaluated. Since false is false, (prn "b") does not get evaluated.

didibus19:06:01

You can use some instead in that case.

(some identity '(1 2 3))
Will return the first logically true element in the list. Which is the same thing that or does.

👍 1
Rajeev Ranjan Jha17:06:41

I would have expected "apply" to work the same way with "or" and "and" as with other functions.

phill23:06:25

Once aware of the distinction between macros and functions, you can notice it marked in the API docs, e.g., for "or", https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/or

Abhi K19:06:21

Hi Clojurians, I have two collections of maps, how I can I merge them to produce below output: {[1 388] {:a 388, :b 1, :c 8, :d 2014, :e 0.327285}, [1 460] {:a 460M, :b 1M, :c 12M, :d 2011M, :e 0.24885}, [1 636] {:a 636M, :b 1M, :c 10M, :d 2011M, :e 0.441123}} {[1 388] {:b 1, :a 388, :peer 0, :customer 1}, [1 460] {:b 1, :a 460, :peer 1, :customer 0}, [1 636] {:b 1, :a 636, :peer 1, :customer 0}, [1 739] {:b 1, :a 739, :peer 1, :customer 0}, [3 12] {:b 3, :a 12, :peer 0, :customer 1}, [3 27] {:b 3, :a 27, :peer 0, :customer 1}, [5 516] {:b 5, :a 516, :peer 1, :customer 0}} Desired Output {[1 388] {:a 388, :b 1, :c 8, :d 2014, :e 0.327285, :peer 0, :customer 1}, [1 460] {:a 460M, :b 1M, :c 12M, :d 2011M, :e 0.24885, :peer 1, :customer 0}, [1 636] {:a 636M, :b 1M, :c 10M, :d 2011M, :e 0.441123, :peer 1, :customer 0}}

delaguardo19:06:24

look at merge-with something like (select-keys (merge-with merge m1 m2) (keys m1))

thanks3 2
pppaul02:06:34

i can't tell what is going on here. but from the solution one thing you can do is select keys before merging, if you are merging lots of data.

pppaul02:06:42

also, merge-with merge will do 2 levels of merging, if you want something more general you may want to look into a recursive merge-with merge, which is a deep-merge

Abhi K14:06:58

Hi @U0LAJQLQ1 - thanks for your response, if I keeps the list simple, how can I achieve this without looping through the collection, I want to keep all the maps from first collection in the result: FIRST Collection: ({:a 388M, :b 1M, :c 12M, :d 2013M, :e 1.198327} {:a 460M, :b 1M, :c 5M, :d 2011M, :e 0.497701} {:a 636M, :b 1M, :c 4M, :d 2012M, :e 0.338195} {:a 460M, :b 1M, :c 5M, :d 2012M, :e 0.0} {:a 12M, :b 3M, :c 11M, :d 2011M, :e 0.507292} {:a 12M, :b 3M, :c 3M, :d 2012M, :e 1.0} {:a 12M, :b 3M, :c 8M, :d 2012M, :e 0.256046} {:a 27M, :b 3M, :c 2M, :d 2015M, :e 0.0} {:a 27M, :b 3M, :c 12M, :d 2016M, :e 0.181818}) Second Collection: ({:a 388M, :b 1M, :peer 0 :customer 1} {:a 460M, :b 1M, :peer 0 :customer 1} {:a 636M, :b 1M, :peer 1 :customer 0} {:a 12M, :b 3M, :peer 1 :customer 1} {:a 27M, :b 3M, :peer 0 :customer 1}) Desired Result - ({:a 388M, :b 1M, :c 12M, :d 2013M, :e 1.198327 :peer 0 :customer 1} {:a 460M, :b 1M, :c 5M, :d 2011M, :e 0.497701 :peer 0 :customer 1} {:a 636M, :b 1M, :c 4M, :d 2012M, :e 0.338195 :peer 1 :customer 0} {:a 460M, :b 1M, :c 5M, :d 2012M, :e 0.0 :peer 0 :customer 1} {:a 12M, :b 3M, :c 11M, :d 2011M, :e 0.507292 :peer 1 :customer 1} {:a 12M, :b 3M, :c 3M, :d 2012M, :e 1.0 :peer 1 :customer 1} {:a 12M, :b 3M, :c 8M, :d 2012M, :e 0.256046 :peer 1 :customer 1} {:a 27M, :b 3M, :c 2M, :d 2015M, :e 0.0 :peer 0 :customer 1} {:a 27M, :b 3M, :c 12M, :d 2016M, :e 0.181818 :peer 0 :customer 1})

Abhi K16:06:19

clojure/set join is giving me the correct output in REPL, however the actual dataset is not working since One collection is LazySeq and another is PersistentVector

(set/join collection1 collection2)

pppaul17:06:10

you can make your code look even easier to read if you use the code block wrapper (there is markdown syntax, but it's hard to show it, and there is a button in the chat that will change the text into mono-spaced and colored). https://www.markdownguide.org/basic-syntax#code

pppaul17:06:32

i know about set/join, but never really used it. set/join produces a set of items, if you are ok with the things in the set then you can turn it into a seq via seq

pppaul17:06:35

(let [a '({:a 388M, :b 1M, :c 12M, :d 2013M, :e 1.198327}
          {:a 460M, :b 1M, :c 5M, :d 2011M, :e 0.497701}
          {:a 636M, :b 1M, :c 4M, :d 2012M, :e 0.338195}
          {:a 460M, :b 1M, :c 5M, :d 2012M, :e 0.0}
          {:a 12M, :b 3M, :c 11M, :d 2011M, :e 0.507292}
          {:a 12M, :b 3M, :c 3M, :d 2012M, :e 1.0}
          {:a 12M, :b 3M, :c 8M, :d 2012M, :e 0.256046}
          {:a 27M, :b 3M, :c 2M, :d 2015M, :e 0.0}
          {:a 27M, :b 3M, :c 12M, :d 2016M, :e 0.181818})
      b '({:a 388M, :b 1M, :peer 0 :customer 1}
          {:a 460M, :b 1M, :peer 0 :customer 1}
          {:a 636M, :b 1M, :peer 1 :customer 0}
          {:a 12M, :b 3M, :peer 1 :customer 1}
          {:a 27M, :b 3M, :peer 0 :customer 1})
      ]
  (seq (clojure.set/join a b))
  )

pppaul17:06:59

i think that keeping it as a set is probably better, though.

Abhi K17:06:23

it works thanks much @U0LAJQLQ1

pppaul17:06:27

how did you discover set/join and know that it would solve your problem? i've never seen people use this before

delaguardo17:06:25

join is for sets. it would work for sequential data as well but this might change in the future versions of clojure

pppaul17:06:46

(defn join
  "When passed 2 rels, returns the rel corresponding to the natural
  join. When passed an additional keymap, joins on the corresponding
  keys."
  {:added "1.0"}
  ([xrel yrel] ;natural join
   (if (and (seq xrel) (seq yrel))
     (let [ks (intersection (set (keys (first xrel))) (set (keys (first yrel))))
           [r s] (if (<= (count xrel) (count yrel))
                   [xrel yrel]
                   [yrel xrel])
           idx (index r ks)]
       (reduce (fn [ret x]
                 (let [found (idx (select-keys x ks))]
                   (if found
                     (reduce #(conj %1 (merge %2 x)) ret found)
                     ret)))
               #{} s))
     #{}))
first line of join is seqing both inputs. i don't think that the set namespace is going to change much in the future, and we can always copy the old code if an update to clojure breaking things

pppaul17:06:07

also, in the set namespace, set params, map params, and relationship params are clearly different, and mean different things. i don't think a relationship means set, or they would use the "set" as the param name prefix like in other functions

delaguardo17:06:14

I don't think the name of the parameter implies some semantic anywhere in clojure source

delaguardo17:06:41

try to use it with duplicates in some of the arguments. it is not clear to me if the outcome would be correct to the topic starter

pppaul17:06:11

i'm just looking up what join does. reading the code doesn't make me think sets are important, but i have to read index as well. the first let makes me think that the sets need to be consistent, (all items have the same keys). but other than that nothing really stands out for constraints

pppaul17:06:21

the set ns hasn't really changed since i started using clojure (1.2), i think it's pretty much abandoned, so i would be very surprised if it changed much from now, also clojure policy is heavily against breaking changes.

pppaul17:06:41

however, the set namespace does have many functions where if you don't give sets as params you get a very bad experience

delaguardo17:06:59

that is why it is clojure.set and not clojure.core :)

pppaul17:06:43

so long as the lord and saviour of clojure is alive, we must believe there will be no breaking changes. 😛

pppaul17:06:50

but, after using clojure for almost forever, i just feel like these non-core old namespaces are just left to rot, for people to be inspired to make their own libs that do a better job

pppaul17:06:56

in other languages, i actually would share your worries more willingly, as i keep getting screwed over by exactly what you describe.

delaguardo17:06:30

this isn't a breaking change. nobody gave any guaranties that join should work for anything besides sets

pppaul17:06:09

if user code breaks, then it's a breaking change to them.

delaguardo17:06:32

https://www.clojure.org/reference/data_structures scroll to the bottom, there is Clojure provides basic set operations like union / difference / intersection, as well as some pseudo-relational algebra support for 'relations', which are simply sets of maps - select / index / rename / join.

pppaul17:06:40

ok, it would be nice if that were part of the docs for the actual function, and not on some website that isn't referenced in the code at all.

delaguardo17:06:57

this is an official clojure guide. it should be your first place to get knowledge about the language)

delaguardo17:06:20

I agree that this should be a part of docstrings, and there are many other places like that in clojure documentation. nothing is perfect

pppaul17:06:42

i started using clojure where i was making the docs on some community doc site http://www.clojuredocs.com (api docs were difficult to understand). these official pages are relatively new, and i do use them now, but more for deps.edn (new things). the code should really be the source of truth, and if there is some ideology missing from the code and on some site, it's difficult to know what to trust. though i tend to just read the code.

pppaul17:06:16

however, when i do use join or index in the future, i'll probably be careful to use sets

👍 2