Fork me on GitHub
#beginners
<
2021-05-19
>
popeye06:05:37

Does :use only to import java classes?

delaguardo07:05:29

https://clojuredocs.org/clojure.core/use No, it is only for clojure namespaces.

popeye07:05:44

@U04V4KLKC I referred few documents , and I did not get when to use :require and :use

delaguardo07:05:01

the only difference is that use also refer to each function from namespace.

(use 'clojure.string)
(join "," ["a" "b" "c"]) ;; => "a,b,c"
but with require you have to use fullyqualified function name
(require 'clojure.string)
(clojure.string/join "," ["a" "b" "c"])

popeye07:05:45

what about require ?

delaguardo07:05:45

usually, best practices of clojure suggesting to not use use but use require with :as alias modifier

🙌 4
☝️ 4
Aron09:05:13

i have 42 lines in my :require and i would really like to use circular dependencies

4
Elliot Stern13:05:40

What’s the most idiomatic way to thread a function if it exists - something like

(-> foo
  ...
  #(if sorter (sort sorter %) (identity %))
  ...)

raspasov01:05:44

I made some-as-> (like some-> and as-> combined). It will short-circuit on nil https://github.com/raspasov/alexandria-clj/blob/main/src/ss/core.cljc#L111

kennytilton14:05:50

the "else" above is (identity %). My imagination fails me: how will that be different than %? Otherwise, your approach seems fine.

Elliot Stern13:05:03

Is there something like when for this kind of usecase?

kennytilton15:05:21

In a world like Common Lisp where sort is destructive we could use when sorter sort X knowing X will either get sorted or sail on untroubled as is, but not so with clojure.

Alex Miller (Clojure team)13:05:57

well, kind of depends on the whole thing

Elliot Stern14:05:28

this is the only conditional item, but there’s only like 4 things in the macro

Alex Miller (Clojure team)14:05:57

could switch to as-> too

Alex Miller (Clojure team)14:05:30

or lift it out into let

Alex Miller (Clojure team)14:05:35

(let [f (if sorter (sort sorter %) (identity %))]
  (-> foo
    ...
    f
    ...)) 

☝️ 4
valerauko16:05:53

not sure if i asked this before, but is there a way to name a reify-produced class?

valerauko16:05:48

just asking because when i have multiple things reified in the same namespace, it is impossible to tell from the stacktrace which one of them caused trouble

valerauko16:05:29

would be nice if they could be named optionally like fns

noisesmith16:05:33

you can customize toString and you can put it inside a named parent

user=> ((fn foo [] (reify Object (toString [_] "a foo"))))
#object[user$eval142$foo$reify__144 0x7fab4be7 "a foo"]

noisesmith16:05:04

notice that foo shows up in the class name, and "a foo" shows up as the second half of the #object data

dpsutton16:05:44

i don't see anything in a stacktrace that helps though

ghadi16:05:53

reify is by definition unnamed

ghadi16:05:20

well... uncontrolled classnames

dpsutton16:05:35

(defprotocol Foo (foo [_]))
(foo (reify Object (toString [_] "custom") Foo (foo [_] (throw (ex-info "boom" {})))))
doesn't have that tostring message in the stacktrace, nor would i expect it

ghadi16:05:41

the stacktrace however should exactly pinpoint any fault

noisesmith16:05:41

@dpsutton the wrapping named function addresses the stack trace identifiability

noisesmith16:05:08

that's why I used both the fn and toString

noisesmith16:05:48

(ins)user=> (def frobber ((fn frobber [] (reify Object (toString [_] "a frobber") clojure.lang.IFn (invoke [_] (:frobbing-error))))))
#'user/frobber
(ins)user=> frobber
#object[user$frobber$reify__153 0x432034a "a frobber"]
(ins)user=> (frobber)
Execution error (IllegalArgumentException) at user/frobber$reify (REPL:1).
Wrong number of args passed to keyword: :frobbing-error
(ins)user=> *e
#error {
 :cause "Wrong number of args passed to keyword: :frobbing-error"
 :via
 [{:type java.lang.IllegalArgumentException
   :message "Wrong number of args passed to keyword: :frobbing-error"
   :at [clojure.lang.Keyword throwArity "Keyword.java" 98]}]
 :trace
 [[clojure.lang.Keyword throwArity "Keyword.java" 98]
  [clojure.lang.Keyword invoke "Keyword.java" 110]
  [user$frobber$reify__153 invoke "NO_SOURCE_FILE" 1]
...
@dpsutton @vale more complete example, showing how both the toString and the wrapping thunk help with readability

noisesmith16:05:34

and accidentally even showing how it is especially useful in the repl where there's no source file or line number

noisesmith16:05:05

it's taking advantage of the fact that reify inside an fn is an inner class of that fn (which we already established can be named)

ghadi16:05:30

gotta focus on the problem statement

ghadi16:05:46

"I can't tell which of the reifies is implicated in this stack trace"

ghadi16:05:19

that should be determinable from the line numbers appearing in the trace

noisesmith16:05:26

right, so if the desired thing is a readable name (like with fn), using a wrapping fn addresses that

noisesmith16:05:04

I agree that line numbers help too, but as a workflow issue, I find it easier to go by human readable names I can search rather than line numbers

valerauko16:05:38

yes you are of course right

valerauko16:05:41

it is not impossible

valerauko16:05:49

but it would be way more simple to parse and understand if they were nameable even for such cosmetic purposes

noisesmith18:05:56

a macro can make this approach easier to read

valerauko16:05:41

i don't want to refer to the generated class by its name or anything

Joni Hiltunen18:05:37

I wonder if it's possible to solve a circular dependency between 2 functions somehow? I'm trying to do this: https://gist.github.com/Sose/b07f44536f3e97fd1307b1edfa8b5840

Lukas19:05:43

Huhu, I wrote a macro, and I get an error when try to put a number in a form

Unexpected error (ClassCastException) macroexpanding q at (src/lhrb/engine.clj:327:3).
class java.lang.Long cannot be cast to class clojure.lang.Named (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.Named is in unnamed module of loader 'app')
for some reason it works when I substitute the number with a symbol
;; does not work
(macroexpand
   '(q {:find [?num ?loc]
       :where [(gr-th ?num 1000)
               [?b :battle/attacker_size ?num]]}
       db))

;; does work
(def n 100)
(macroexpand
   '(q {:find [?num ?loc]
        :where [(gr-th ?num n)
               [?b :battle/attacker_size ?num]]}
       db))
I figured that this is probably a silly beginner mistake, so could maybe a wise person please enlighten me? 😋 Any help is greatly appreciated background: I wrote a micro-kanren implementation and try to give it a datomic flavored query api with the macro

dpsutton19:05:36

wild stab, but i guess you look at each form to determine if it begins with a ??

Lukas19:05:00

(defn compile-where-clauses
  "transforms vector clauses into q-db queries
  e.g. [?a :age 10] => (q-db db ?a :age 10)"
  [clauses db]
  (->> clauses
       (map (fn [clause]
              (if (vector? clause)
                `(q-db ~db ~@clause)
                clause)))))

(defn find-lvars
  "finds and returns all unique logic-variables.
  A logic var begins with an '?'"
  [find where] ;; maybe decouple from concrete impl?
  (->> (into find where)
       (flatten)
       (filter
        (fn [s]
          (str/starts-with? (name s) "?")))
       (distinct)))

(defmacro q [{:keys [find where]} db]
  (prn &form)
  (prn &env)
  (let [;; test if vars in find clause are also in where clause
        lvars (find-lvars find where)
        where-compiled (compile-where-clauses where db)]
    `(let [~@(interleave
              lvars
              (map
               (fn [sym]
                 `(gensym ~(name sym)))
               lvars))]
       (run
         (reify-var ~@find)
         ~@where-compiled))))

ghadi19:05:09

always look at your full stack trace *e

Lukas19:05:01

Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (0 frames hidden)

2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling src/lhrb/engine.clj at (331:3)
   #:clojure.error{:phase :macroexpansion,
                   :line 331,
                   :column 3,
                   :source "/home/lukas/projects/datahog/src/lhrb/engine.clj",
                   :symbol q}
             Compiler.java: 7023  clojure.lang.Compiler/macroexpand1
                  core.clj: 4012  clojure.core/macroexpand-1
                  core.clj: 4014  clojure.core/macroexpand
                  core.clj: 4014  clojure.core/macroexpand
                      REPL:  331  lhrb.engine/eval10264
                      REPL:  331  lhrb.engine/eval10264
             Compiler.java: 7181  clojure.lang.Compiler/eval
             Compiler.java: 7136  clojure.lang.Compiler/eval
                  core.clj: 3202  clojure.core/eval
                  core.clj: 3198  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: 1977  clojure.core/with-bindings*
                  core.clj: 1977  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:  137  clojure.lang.RestFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj:  662  clojure.core/apply
                regrow.clj:   20  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.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:  202  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  201  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  832  java.lang.Thread/run

1. Caused by java.lang.ClassCastException
   (No message)

ghadi19:05:30

by default, the REPL presents a one or two line summary, but fuller information is available in the trace, including which line of your source file threw an exception

ghadi19:05:38

so, I am already going to qualify this ^ In the specific case of ClassCastExceptions and NullPointerExceptions, sometimes the JVM elides traces 🙂

ghadi19:05:55

(that is controllable)

Lukas19:05:32

this is the output from *e

#error {
 :cause nil
 :via
 [{:type clojure.lang.Compiler$CompilerException
   :message "Unexpected error macroexpanding q at (/home/lukas/projects/datahog/src/lhrb/engine.clj:331:3)."
   :data #:clojure.error{:phase :macroexpansion, :line 331, :column 3, :source "/home/lukas/projects/datahog/src/lhrb/engine.clj", :symbol q}
   :at [clojure.lang.Compiler macroexpand1 "Compiler.java" 7023]}
  {:type java.lang.ClassCastException
   :message nil}]
 :trace
 []}

ghadi19:05:39

right, elided -- normal traces are not empty vectors []

ghadi19:05:31

somewhere in your macro you're asking for the name of a symbol, but calling it on a number (which are Longs in clojure)

Lukas19:05:58

okay thank you

Lukas19:05:55

That helps I guess I can track it now down (there is one part where I call name)

ghadi19:05:56

you can pass -XX:-OmitStackTraceInFastThrow to your REPL

ghadi19:05:24

which would fill out the empty :trace []

ghadi19:05:57

it's a speed thing. Prod code never throws ClassCastExceptions, so the JVM optimizes for not filling out full stack traces.

Lukas19:05:11

🙏 ty soo much, very good input

Lukas19:05:26

could you help and tell my how I pass -XX:-OmitStackTraceInFastThrow` to my repl? 🙈

Joni Hiltunen19:05:48

good parsing library? I searched online and found https://github.com/Engelberg/instaparse but it hasn't been updated in a while. is it abandoned or is it just so finished that it hasn't needed any updates recently? I wanna parse a simple imperative language (for moving a turtle)

Lukas19:05:17

In clojurespace libraries do not get updates when they are done

Lukas19:05:35

your fine using this library

Joni Hiltunen19:05:41

okay, thank you

seancorfield19:05:58

@UQDFC6V4Z You’ll find a lot of Clojure libraries that haven’t been updated in ages — Clojure folks value stability and also tend to like libraries that solve one problem “completely” and therefore can be “done” and not need constant updates. It always looks strange to folks coming from language backgrounds where there is always a lot of “churn” on libraries.

seancorfield19:05:33

We use Instaparse heavily at work for our custom search rules (driving 40+ online dating sites).

❤️ 2
Joni Hiltunen19:05:24

@U04V70XH6 thank you! should be good for my toy program then 😄

caumond20:05:20

I am really happy now to see a library with an old Last commit. I only have a doubt with dependencies. I guess an ideal library have very few dependencies but in most cases there are. For instance version of clojure clojurescript and reader. So what is your practise concerning these dependencies ? Do you update them manually ?

seancorfield21:05:48

I usually don’t bother much about whatever version of tools.reader is pulled in these days. I have a top-level dependency for Clojure (or ClojureScript) so that takes precedence over whatever any libraries bring in (I’m using the CLI / deps.edn).

aengelberg19:05:58

We’re still here and generally respond to important bug reports 🙂 Also I answer questions in #instaparse sometimes

Lukas19:05:50

just typing in does not work

Lukas19:05:57

Anyways you guys rock, and thanks a lot for helping strangers on the internet you are great :hugging_face:

Alex Miller (Clojure team)19:05:32

in the repl, (clojure.repl/pst *e) is often more readable than *e (and does some other useful things like eliding top frames related to error throwing)

💡 6
dpsutton19:05:55

And -XX:-OmitStackTraceInFastThrow is a jvm option. You start a jvm with this option

💡 3
Lukas19:05:17

🙏 Thanks a lot

Rob Haisfield22:05:24

Any great libraries or repos for beginners to play around with to learn, particularly through projects?

Rob Haisfield22:05:15

I’d be very curious about any libraries that analyze quantitative data. Statistics is super interesting to me, but I’m interested in just about anything that’s fun to play with

lunik123:05:12

Is it preferable to use all and conde or *and and *or in core.logic ?

Jonas23:05:00

Hey all, I was wondering if there is a way in Clojure to convert a function back into it's source string, as in

user=> (defn foo [] (println "foo"))
user=> (clj->str foo)
"(defn foo [] (println \"foo\"))"

hiredman23:05:41

sometimes you can look up the source (if it is still around via a file somewhere) via metadata on foo

🙏 2
hiredman23:05:17

I tend to prefer conde and usually use fresh with no lvars, but likely should be using all

hiredman23:05:44

user=> (doc source)
-------------------------
clojure.repl/source
([n])
Macro
  Prints the source code for the given symbol, if it can find it.
  This requires that the symbol resolve to a Var defined in a
  namespace for which the .clj is in the classpath.

  Example: (source filter)
nil
user=>