Fork me on GitHub
#clojure
<
2022-03-24
>
crinklywrappr04:03:32

this command clojure -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.stuartsierra/component {:git/url "" :git/tag "component-1.1.0" :git/sha "34f6ebb"}}}}}' produces the error Error building classpath. Manifest type not detected when finding deps for com.stuartsierra/component i see that message for integrant and mount as well, but not for clojure.tools.cli or honeysql. can anyone explain what it means and if I'm doing something wrong?

Alex Miller (Clojure team)05:03:31

Those are leinigen projects

Alex Miller (Clojure team)05:03:01

So they don't have a deps.edn

👍 1
crinklywrappr04:03:50

(i'm pretty new to the clj build tools)

opqdonut06:03:16

Trying out tools.build for the first time. I'd like to build an uberjar that contains my backend & a frontend built with shadow-cljs. However it looks like I need to add all the frontend dependencies and :paths ["src/cljs"] to my build alias to be able to use shadow.cljs.devtools.api/release directly from build.clj. That somehow feels wrong. Is this the right way to do it? Or should I do something like shell out from build.clj and invoke clj -M:shadow-cljs release?

opqdonut06:03:57

{:paths ["src/clj" "src/cljs" "resources" "built-resources"]
 :deps {;; backend deps
        org.clojure/clojure {:mvn/version "1.10.3"}
        metosin/reitit {:mvn/version "0.5.16"}
        ring/ring-jetty-adapter {:mvn/version "1.9.5"}}
 :aliases {:shadow-cljs
           {;; The cljs path needs to be added here so that -T:shadow-cljs:build sees the frontend source
            :paths ["src/cljs"]
            :replace-deps {;; frontend deps
                           thheller/shadow-cljs {:mvn/version "2.17.8"}
                           org.clojure/clojurescript {:mvn/version "1.11.4"}
                           reagent/reagent {:mvn/version "1.1.0"}
                           lambdaisland/fetch {:mvn/version "1.0.41"}}
            :main-opts ["-m" "shadow.cljs.devtools.cli"]}
           ;; Usage: clj -T:shadow-cljs:build uberjar
           :build
           {:deps {io.github.clojure/tools.build {:git/tag "v0.8.1" :git/sha "7d40500"}}
            :ns-default build}}}

thheller06:03:40

this is correct. clojurescript needs access to all your dependencies for compilation. so they need to be available in the build alias. or you shell out that also works.

opqdonut06:03:01

Yeah, both work! I was more thinking about what's the right philosophy for build.clj. Using -T:build kinda points one at not having project sources & deps on the classpath.

opqdonut06:03:27

PS. thanks for shadow-cljs, it's been a joy to work with. I'm never going back to cljsbuild 😁

👍 1
thheller06:03:19

I would recommend keeping the CLJS dependencies in a separate alias anyway since they are never required at runtime for the backend

thheller06:03:57

so if they are separate anyways you can activate it for builds

opqdonut06:03:33

Yeah that's what I'm doing. The separate alias is needed for running clj -M:shadow-cljs watch as well.

opqdonut06:03:34

and since the uberjar is built using the root deps (or whatever you call them, anyway, no aliases applied), the frontend deps won't get bundled into the uberjar as jars

opqdonut06:03:37

and that's exactly right

opqdonut06:03:25

Hmm, looks like org.corfield.build/run-task would be the principled alternative to shelling out. Will try to remember that for future use.

pez06:03:10

Is it true to say that all Clojure core conditional macros derive from if?

(clojure.walk/macroexpand-all '(case x
                                 y value-if-x-is-y
                                 z value-if-x-is-z
                                 value-if-x-is-neither-y-nor-z))
=> (let* [G__7578 x]
         (case* G__7578
                0
                0
                value-if-x-is-neither-y-nor-z
                {-1640523731 [z value-if-x-is-z],
                 -1640523666 [y value-if-x-is-y]}
                :compact
                :hash-equiv
                nil))
What's going on inside case*?

Ben Sless06:03:13

Case is a different construct on the JVM. It's a jump table

mpenet06:03:13

yup you get constant time lookup

mpenet06:03:11

it compiles down directly to jvm bytecode dedicated to that (`tableswitch` iirc)

pez06:03:33

Thanks! This is for the Get started with Clojure Guide. If I'd like to both avoid lying, and getting to detailed with the poor beginner. How do you advice I should rephrase:

;; In fact, all conditional constructs (macros) are
 ;; (eventually) built using `if`. 
😃

pez06:03:17

Hmmm, maybe I can just say, except case, which uses jump tables...

mpenet06:03:17

I am not sure I would mention if In theory you can do this stuff with and/or as well, maybe it's the case sometimes in core

pez07:03:58

> maybe it's the case 😃

phronmophobic07:03:27

Maybe something like: > There are only 13 builtin clojure constructs. All other constructs are defined in terms of the 13 builtin constructs. It turns out that even common constructs like and, or, when , cond, etc. aren't primitive, but are defined in terms of the more primitive builtin, if

phronmophobic07:03:37

and and or are built using if. You could technically use try and catch to make conditionals without if or case

pez07:03:48

Yes, that's what I am alluring to. I'm trying to explain that Clojure is built from these small building blocks. And this thing with if so beautifully illustrates it.

phronmophobic07:03:58

do not "try" this at home:

(defmacro try-if [test then else]
  `(try
     (throw
      (get {true (IllegalArgumentException.)
            false (NullPointerException.)}
           (boolean ~test)))
     (catch IllegalArgumentException e#
       ~then)
     (catch NullPointerException e#
       ~else)))

(try-if (even? 0) :true :false) ;; => :true
(try-if (even? 1) :true :false) ;; => :false

clojure-spin 2
😂 1
pez07:03:11

OMG. When new at the job at a Python house (long time ago) I found something ver similar to that in the code. When asking why (the hell), I got the answer that it was faster. Turned out it was true for some earlier version of Python, but utterly untrue for the version we were using...

😬 3
Ben Sless07:03:40

Clojure exposes all the control flow primitives on the JVM - if, case, and exceptions. Most (all?) control flow macros on Clojure are implemented using if

phronmophobic07:03:48

Another if -less conditional

(defmacro fn-if [test then else]
  `(([#(do ~then)
      #(do ~else)]
     (compare true (boolean ~test)))))

(fn-if (even? 0) :true :false) ;; :true
(fn-if (even? 1) :true :false) ;; :false

metal 1
pez08:03:50

@UK0810AQ2, was that a suggestion for how to phrase it? What's the distinction between conditionals and flow control here? I worded it like so.

;; Fun fact: Most conditional constructs (macros) are
  ;; (eventually) built using `if`.
Here's the updated section on special forms: https://github.com/BetterThanTomorrow/dram/blob/ca999a0bc45ef2b28c008ff9dd2206b82d6b4413/drams/calva_getting_started/welcome_to_clojure.clj#L431

Ben Sless08:03:58

@U0ETXRFEW not necessarily, more oriented towards elaborating and framing the discussion. In the end everything translates to jump instructions on the CPU, yet the JVM supports several constructs "natively" so Clojure supports all of them, too. I imagine if it targeted another platform to begin with it might not have had case. Regarding phrasing, you can take even a stronger stance: "besides case, all conditional and control flow constructs in Clojure are build using if"

Ben Sless08:03:23

not saying my phrasing is what you should end up with, but the idea is if builds everything besides case which is its own thing, and that's the entire world of control flow, unless you count exceptions and continuations in the mix

pez08:03:52

Thanks! I love your phrasing. Updated the dram now.

p4ulcristian08:03:40

Hello! I use clj-python/libpython-clj and everything worked fine, until I tried to uberjar my project. I use

(hf.depstar/jar 
{:aot true
 :jar "etheatre.jar"
 :jar-type :uberjar
 :main-class backend
 :jvm-opts ["--add-modules" "jdk.incubator.foreign"
            "--enable-native-access=ALL-UNNAMED"]}
and I get the error Caused by: java.lang.ClassNotFoundException: jdk.incubator.foreign.MemoryAddress This is my alias in deps.edn
:prod {:extra-deps {com.github.seancorfield/depstar {:mvn/version "2.1.303"}}
         :exec-fn    compiler/compile-app!
         :jvm-opts ["--add-modules" "jdk.incubator.foreign"
                    "--enable-native-access=ALL-UNNAMED"]
         :js-builds [:app-ready]}}}}

Carlo09:03:29

Say I have a definition of a function named foo. And I need to programmatically get the associated var (not using the #' reader). The correct invocation is (-> foo var), while (-> 'foo var) is an error. But if in my pipeline I can easily get foo, I'm not sure how foo would be obtained

Carlo09:03:31

Solved. I wanted to say (-> 'foo resolve)

magnars12:03:44

I just restarted my REPL, detecting very late that I had inadvertently introduced a circular dependency between namespaces. Is there are a reason why evaluating a namespace into the REPL does not warn about this? Any way to turn it on?

James Amberger13:03:04

I’d like to talk about Rich’s Are We There Yet from a (my) practical perspective. His discussions of time constructs are fascinating and instructive.\

James Amberger13:03:40

My daughter deecided thtat was a complete thought

😂 6
James Amberger13:03:09

I see people here always saying “Tell us what is the actual problem,” so here goes. My app at work is a web app with an Oracle relational database for persistence. So whatever I do in my code, its effect on the db is what matters. For some reason the people at my institution seem to think that business logic should be in the db layer in the form of pl/sql—and, they apparently don’t use the db driver in the Java code, but instead http via a bunch of Oracle facilities (even though the db and whatever Oracle thing serves the app are on the same network in the same building). I don’t know how to do simple atomicity short of programming constraints in pl/sql (some of which would go beyond basic primary key/foreign key stuff).

James Amberger13:03:52

Perhaps more generally I wonder how some of the stuff in the talk relates to use of a relational database as your only source of persistence

James Amberger13:03:49

like I can’t just do (swap! db table key fn) can I, but it would be cool if I could…

potetm14:03:08

@james.amberger If you have isolation level serializable, (swap! db fn) is pretty much what relational databases do.

James Amberger15:03:29

Right, of course, they offer atomicity. Will even enforce simple constraints

James Amberger15:03:38

forgive me for thinking out loud here

James Amberger15:03:23

I’m saying if I want to do (swap! db fn) and have fn throw an error if the state of things is not what the caller expected,

James Amberger15:03:31

the normal thing to do here would be use a sql library to make sure this happens atomically

potetm14:03:57

There are some subtleties (which change depending on the specific db), but it’s pretty close.

p-himik15:03:39

Please avoid crossposting. #beginners has more than 14000 members, it's more than enough for someone to give you an answer.

Pratik15:03:32

Thanks, will keep that in mind going forward

Maris16:03:35

clj-kondo complains about update-vals error: unresolved symbol update-vals

borkdude16:03:47

@maris.orbidans That will resolve itself when you delete your .clj-kondo/.cache folder and re-lint your dependencies, provided you have Clojure 1.11 on your classpath

👍 1
borkdude16:03:56

There's also a #clj-kondo channel

Maris16:03:20

thank you

emccue18:03:54

any thoughts about how I could make something like this in clip be automatic

{:comp-a (expr-1)
 :comp-b (expr-2)
 :comp-c (expr-3 {:comp-a (clip/ref :comp-a)
                  :comp-b (clip/ref :comp-b)})
 :comp-d (expr-4 {:comp-a (clip/ref :comp-a)
                  :comp-b (clip/ref :comp-b)
                  :comp-c (clip/ref :comp-c)})}
Something like a symbol I interpret specially like 'everything-so-far and then pre-process into the map?

p-himik18:03:13

Not sure what you mean by 'everything-so-far, but maybe this is something close?

(require '[clojure.walk :as walk])

(defn preprocess [m]
  (let [m (walk/postwalk #(if (and (symbol? %) (m (keyword %)))
                            (list 'clip/ref (keyword %))
                            %)
                         m)]
    (into (empty m)
          (map (fn [[k v]]
                 [(keyword k) v]))
          m)))

(println (preprocess '{:comp-a (expr-1)
                       :comp-b (expr-2)
                       :comp-c (expr-3 {:comp-a comp-a
                                        :comp-b comp-b})
                       :comp-d (expr-4 {:comp-a comp-a
                                        :comp-b comp-b
                                        :comp-c comp-c})}))

emccue14:03:41

I mean like - I want something like this

emccue14:03:24

{:comp-a (expr-1)
 :comp-b (expr-2)
 :comp-c (expr-3 (EVERYTHING))
 :comp-d (expr-4 (EVERYTHING :after [:comp-c]))}

emccue14:03:41

so in the first version this is the graph that is explicit with the refs

-------------
   /            |
A -             v
    | - > C - > D
B -             ^
   \            |
    ------------|
with what I wrote the graph that is explicit is
A 

B

(everything we can make) - > C

(everything we can make after C) -> D
And I want it to infer the top graph - i.e. expand to a map of {:thing (clip/ref :thing)}

p-himik14:03:12

It can't be just a function/macro because maps are unordered. You'd have to read the definition of that map, be it EDN or CLJ code, and then do something with it.

p-himik14:03:51

Or, if you're willing to get rid of the map and embrace e.g. kv-pairs, then it should be relatively trivial to implement with e.g. reduce.

emccue14:03:28

well there is the notion that by putting it there it is by default "ahead" of the graph that doesn't participate in the system. I am willing to have a preprocess function crawl the values before passing to clip

p-himik14:03:47

I don't understand what you mean by your first sentence.

p-himik14:03:51

Unless you yourself control the config reading process, what you want cannot be done, that's it. That's exactly why it's bad practice to destructure within map destructuring while relying on previous destructure results.

emccue14:03:13

like say A depends on B, C depends on A and B D depends on "everything before" so the question that is asked is "how many things can we make without involving D or anything that depends on D" and those become D's dependencies

p-himik14:03:37

So in

{:a 1
 :b (EVERYTHING)
 :c 2}
that EVERYTHING will also include :c?

p-himik15:03:48

I see. Sounds like this process should do it: 1. Set up a dependency graph with invariants based just on those :after keys 2. Do a second pass over the config and add all possible edges that won't create cycles 3. Topologically sort the graph

emccue15:03:56

I made an issue on the clip repo - might be easier to do all that inside clip than outside (and its not a hugely pressing concern for me right now, just something that would be nice)

👍 1
adi20:03:28

Well, it's 2:17AM and I just finished writing https://www.evalapply.org/posts/n-ways-to-fizzbuzz-in-clojure/#interlude-what-more-could-we-possibly-decomplect (in prep. for a talk due in 12 hours 🥲 ). If anyone here is able to provide a quick sniff test, I will be grateful. I'll drop it from the talk if it doesn't pass muster here.

Ben Sless20:03:32

Thanks for the mention 🙂 Another thing you can pull apart is all the F/B transformations, i.e.

(defn fuzzer
  [n s]
  (fn [x]
    (if (and (number? x) (divisible n x))
      s
      x)))

(comp
 (map (fuzzer 15 "FizzBuzz"))
 (map (fuzzer 5 "Fizz"))
 (map (fuzzer 3 "Buzz")))

lightsaber 1
Ben Sless20:03:57

Or if you carry around the numbers and the "displayed" value, you can do something like:

(defn fuzzer
  [n s]
  (fn [[k display]]
    (if (and (number? x) (divisible n x))
      [k (str display s)]
      [k display])))

(comp
 (map (fuzzer 5 "Fizz"))
 (map (fuzzer 3 "Buzz")))

Ben Sless21:03:28

looks good, touches a lot of terms, hope audience will be able to keep track. If you find they're grappling with it, throw "operators fusion" at them

😂 1
gratitude 1
adi21:03:39

Thanks for the review! I'm going to keep most of it in the back pocket. The deck will have very little jargon and very much le codeauxes.

adi21:03:51

> Another thing you can pull apart is all the F/B transformations I was considering doing so, https://www.evalapply.org/posts/n-ways-to-fizzbuzz-in-clojure/#domain-driven-design-fizzbuzz, but the nil-punning was better for the or and juxt / some. Makes sense to bring it back in a transducer context. > carry around the numbers and the "displayed" value The https://www.evalapply.org/posts/n-ways-to-fizzbuzz-in-clojure/#peano-fizzbuzz works like that! Right now I've just pulled the professor card. Transduction of all the other fizz-buzz variants up-text is left as an exercise for the reader :)

emilaasa21:03:58

The joy of public speaking adi! I like your writing :)

gratitude 1
💜 1
adi11:03:46

> The joy of public speaking adi! Mos def. Remote can be even more fun ... I had network outages in two different providers at two different locations. One in the AM at my place, which caused me to go to my friend's place. The second at my friend's place 5 minutes into my 20 minute demo. 🥲 But Plan B was already live (the blog post), and plan C was always on, viz. to do an in-depth recording anyway.

adi12:03:26

The prayer I said to the Demogods at the start didn't help :p

** Appease Demo Gods
   O Lambda the Ultimate,
   bless we who are in this demo.

   That our core be functional,
   and our functions be pure.

   That our data be immutable,
   so we may know the value of values.

   That our systems be composable,
   so they may grow and scale with grace.

   That their States only mutate
   in pleasantly surprising ways.

   That our networks & computers work,
   at least for 20 more minutes.

   For otherwise nothing lives.
   Nothing evolves.

   In the name of the alpha,
   the beta, and the eta...

   (λx.x x) (λx.x x)

Ben Sless18:03:42

Should have prayed to the omnissiah

not-sure-fry 1
JoshLemer23:03:33

I saw that here 10 years ago someone made an implementation of Clojure in Python, that's pretty cool https://github.com/drewr/clojure-py. I think Python would be a really interesting target for a lot of things, scripting, webdev, of course ML/AI/DS.

respatialized23:03:01

different sense of "target" than a separate implementation, but if you haven't seen it, you should take a look at libpython-clj, a great project that offers, on top of all the great libraries in the python ecosystem, zero-copy interop with NumPy arrays. https://github.com/clj-python/libpython-clj The #data-science channel hosts a lot of discussions about the data science ecosystem in Clojure, including python (and R!) interop

JoshLemer23:03:15

Was looking at this project, it looks like you end up running a python process and a jvm process at the same time?

abishek00:03:39

wow! thank you 🙂 I didn’t even think to search for something like this..

❤️ 1
Nundrum15:03:21

There's also the Clojure-inspired Hy: https://github.com/hylang/hy

❤️ 1
hlship23:03:57

I've found something that looks like a compiler bug, related to direct linking; I'm posting here first for discussion before creating a ticket.

(ns example.direct)

(defn ^bytes
  to-bytes [^String s]
  (.getBytes s "UTF-8"))

(defn output
  [^bytes bytes]
  (doseq [b bytes]
    (printf "%c - %2x%n" (char b) b)))

(defn show-me
  [_]
  (let [bytes (to-bytes "This may fail")]
    (output bytes)))
This works normally without direct linking. With direct linking, compilation fails:
:clojure.main/message
 "Syntax error (IllegalArgumentException) compiling fn* at (example/direct.clj:12:1).\nUnable to resolve classname: clojure.core$bytes@13f95696\n",
 :clojure.main/triage
 {:clojure.error/phase :compile-syntax-check,
  :clojure.error/line 12,
  :clojure.error/column 1,
  :clojure.error/source "direct.clj",
  :clojure.error/symbol fn*,
  :clojure.error/path "example/direct.clj",
  :clojure.error/class java.lang.IllegalArgumentException,
  :clojure.error/cause
  "Unable to resolve classname: clojure.core$bytes@13f95696"},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.Compiler$CompilerException,
    :message
    "Syntax error compiling fn* at (example/direct.clj:12:1).",
    :data
    {:clojure.error/phase :compile-syntax-check,
     :clojure.error/line 12,
     :clojure.error/column 1,
     :clojure.error/source "example/direct.clj",
     :clojure.error/symbol fn*},
    :at [clojure.lang.Compiler analyzeSeq "Compiler.java" 7132]}
   {:type java.lang.IllegalArgumentException,
    :message
    "Unable to resolve classname: clojure.core$bytes@13f95696",
    :at
    [clojure.lang.Compiler$HostExpr tagToClass "Compiler.java" 1129]}],
...
I've verified this with 1.10.3 and 1.11.0. It seems to be specifically related to the let. I haven't started digging into the compiler code yet.

hiredman23:03:43

It is a bad type hint

hiredman23:03:29

You have a type hint of "bytes" somewhere where the symbol is being resolved

hiredman23:03:46

Like maybe a buggy macro

hiredman23:03:50

It is your ^bytes annotation on the var for to-bytes, I believe if you move it to the arg vector it should be fine

hlship00:03:44

I'm 12 years into using Clojure and didn't know you could put type hints on the parameters vector. And there it is, right in the docs.

hiredman00:03:05

Yeah, I definitely have the strong muscle memory to put it on the var still

hiredman23:03:17

In general type hinting for non-classes doesn't work right on vars, but I'd the way it is, if you put the hint on the arg vector, of I recall correctly, the symbol won't be resolved