Fork me on GitHub
#beginners
<
2021-10-17
>
jaihindhreddy10:10:02

It appears I understand evaluation less than I think I do. I'm unable to explain this:

user=> or
Syntax error compiling at (REPL:0:0).
Can't take value of a macro: #'clojure.core/or
user=> @#'or
#object[clojure.core$or 0x1e53135d "clojure.core$or@1e53135d"]
user=> 
Isn't @#'or taking the value of or? Edit: I thought when I typed-in or at the REPL, it's first qualified to clojure.core/or, which is then looked up in the namespace bindings to find #'clojure.core/or, which is then derefed. I expected that to fail, but @#'clojure.core/or seems to work. How is symbol evaluation different in this case?

👍 1
indy13:10:44

Seemed like an interesting question and went source hunting. Here is what I concluded, the isolated or and (deref (var or)) take different paths in the compiler. The isolated or throws at analyzeSymbol with that error since, I’m guessing, returning a value of macro definition in isolation at any other time than macro-expansion time doesn’t make sense. This is thrown before the namespace->var resolution can happen which is at run time (the var for the symbol is registered with the ns during analyzeSymbol, but only at runtime does it get derefed). Whereas (deref (var or)) will be routed to analyzeSeq and (var or) will not throw and is able to be successfully derefed. But the value of @#'clojure.core/or itself isn’t of much use at runtime (in contrast to macro-expansion time).

(@#'clojure.core/or 1 2)
=> nil
The throwing of symbols that are macros in isolation during compilation seems to be an explicit choice if not for which the result of typing in @#'clojure.core/or and or would’ve been the same. analyzeSymbol: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L6772 analyzeSeq: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L6793

👍 1
🙌 1
thanks 1
🤯 1
adi14:10:57

Thanks for this Q @U883WCP5Z and the A, @UMPJRJU9E. I'd also gone down the REPL a bit and I got to indy's point of (@#'or true false) => nil . That led me to suspect the behaviour jaihind observed had something to do with whatever happens prior to allowing the object to be invoked. And de-referencing the var would bypass whatever check was in-between. I didn't go as far as indy did, though --- thanks for the digging! One clue that I wanted to follow is that macros have :macro true in their metadata. Whereas defmacro is just a fn . Meaning, macro definition would create an object type indistinguishable from a "plain ol' fn", unless it also added some other distinguishing information that the compiler and/or runtime could use.

jaihindhreddy14:10:28

Thanks @UMPJRJU9E! I suspected it was being done in (symbol-) analysis but didn't take the time to read the code. Background of how I discovered this oddity (only in case someone's interested): I was mentoring someone on exercism (https://exercism.org/profiles/jaihindhreddy) and they asked if we could programmatically get a list of the test-cases from the tests. I took a crack at it using with-redefs and found it didn't work for macros. In addition, weirdly enough, using with-redefs with macros permanently removes :macro true from the var metadata. Turns out there's a ticket for it already (https://clojure.atlassian.net/browse/CLJ-1867) Here's the code I initially wrote:

(defn- get-test-inputs [ns test-ns]
  (let [cases (atom {})
        ;; returns the fn of a var, wrapped to put args in `cases` first
        wrap (fn [var]
               (let [sym (symbol var), f @var]
                 #(do (swap! cases update sym (fnil conj []) (vec %&))
                      (apply f %&))))]
    (with-redefs-fn
      (->> (vals (ns-publics ns))
           (remove (comp :macro meta))
           (map (juxt identity wrap))
           (into {}))
      #(binding [clojure.test/*test-out* (java.io.StringWriter.)]
        (clojure.test/run-tests ns test-ns)))
    @cases))
After observing weird behaviour with macros, I looked at (source defmacro) and found that macros functions have two implicit args i.e, the form, and an env variable, which has bindings. I updated wrap above, and then ran into https://clojure.atlassian.net/browse/CLJ-1867.

👍 2
Muhammad Hamza Chippa12:10:57

i have to list (1 2 3) (4 5 6) I want to arrange them in the form of (x y) cordinates like [[1 4] [2 5] [5 6]] how can I do that

walterl12:10:21

(map vector '(1 2 3) '(4 5 6))

🙌 1
seancorfield15:10:17

Please do not cross-post questions like this to #clojure as well -- just ask them here.

Stuart12:10:36

Hi, I'm doing some problems on codewars and I saw this solution from another player

(defn get-sum [a b]
  (->> 
    (if (> a b) [b a] [a b]) 
    (#(range (first %1) (+(second %1) 1)) ,,,)
    (reduce +)
    )
)
What is the ,,, doing here ?

walterl12:10:09

Nothing. It's interpreted as whitespace.

Stuart12:10:34

I've noticed some people put the trailing ) on their own lines too. Dont think I like this style.

Stuart12:10:26

But maybe it makes it easier when putting cursor to correct bracket for eval'ing stuff

walterl12:10:49

I most commonly see it used in Rich comments:

(comment
  #_(some-debug-stuff ...)
  ,,,)
I quite like this pattern, since it makes the "commented out" nature of the form stand out a bit more

Muhammad Hamza Chippa12:10:26

I have a list of number [1 2 3 4 5 6] I have to create a function which return closest smallest number to the argument lets say I passed it 6 and it returns me closes largest number 5

jaihindhreddy12:10:32

Is the list sorted? And what do you mean by closest? Do you mean closest in magnitude, or closest in position to the number 6 in the list?

pavlosmelissinos12:10:04

check out the min-key function @U02DQ45FQF9

Muhammad Hamza Chippa12:10:19

closest in mangitude

Stuart13:10:06

SOmething like this:

(defn smallest-closes [xs target]
  (->> xs
       (sort)
       (map (fn [n] {:v n
                     :d (Math/abs (- n target))}))
       (filter (fn [{:keys [v d]}]
                 (not= 0 d)))
       (sort-by :d)
       (first)
       :v))

(smallest-closes [10 12 30 31 35 36] 34) => 35
(smallest-closes [10 12 30 31 35 36] 33) => 31
(smallest-closes [1 2 3 4 5 6] 3) => 2
(smallest-closes [1 2 3 4 5 6] 6) => 5

🙌 1
indy13:10:57

(defn closest-smallest-unsorted
   [v num]
   (second (reduce (fn [[diff res] el]
                      (let [new-diff (Math/abs ^long (- num el))]
                         (if (and (< el num) (< new-diff diff))
                            [new-diff el]
                            [diff res])))
                   [Long/MAX_VALUE nil] v)))

(defn closest-smallest-sorted
   [v num]
   (reduce (fn [prev-el el]
              (if (= el num)
                 (reduced prev-el)
                 el))
           nil v))

pavlosmelissinos13:10:11

No need to sort the collection: (apply min-key #(Math/abs (- % target)) coll)

🙌 2
Muhammad Hamza Chippa15:10:37

is there any way to round or floor numbers ?

Rui Mendes15:10:02

would converting into integers work? You could use int. There's also Math/floor and Math/round.

🙌 1
kennytilton15:10:39

"core" thru me at first, but like any language computer or human there are idioms, and these quickly gain their correct semantic as we grow into the language. I am with you, to me core means utility. Now my practice is to use "base.clj" for super-low-level stuff that everything will need.

Niklas Koponen16:10:54

Hello! I just started experimenting with clojure, clojurescript and krell. This is my first time working with clj tools. I'm using neovim as my editor and have successfully connected nrepl to neovim using the conjure plugin. Now I would like to connect the krell repl to conjure. There is this documentation in the Krell wiki https://github.com/vouch-opensource/krell/wiki/Tooling-Integration---Emacs%2C-Cursive%2C-VSCode-etc... I just don't understand where I should put these lines of code... Here is the Conjure documentation https://github.com/Olical/conjure/wiki/Quick-start:-Clojure

Niklas Koponen16:10:02

I am able to start the normal Krell repl from the command line.

orpheus16:10:49

Is there a way to use something like &rest when destructing a map to get the remaining key/values in an object? Like:

(let [{myKey :myKey 
       restOftheObject &rest}]
     (println myKey restOftheObject))

pavlosmelissinos16:10:51

Just use :as and pass the whole map. If for some reason you really have to exclude certain keys, you can do it with dissoc

orpheus16:10:10

Ok thank you

orpheus17:10:13

I feel like I’m getting lost in tree structured let blocks by checking errors before I call subsequent functions. How can I, if possible, write this better? Here is a wrapper around a compojure/ring route that handles file uploads with parameter validation and file-storage. If those succeed, it will call the callback argument with some data from the previous step and request body. If any step fails I need to return a response map for Ring, but I’m not sure the best way to do error checking without doing a let block for each error.

(defn file-upload-wrapper
  [controller config callback]
  (wrap-multipart-params
   (POST controller request
         ;; Validate request body - returns null or error message
         (let [error (is-valid-req (:params request) config)]
           (if-not (nil? error)
             (bad-request error)
             ;; If no error, store file and check error
             (let [[filepaths error] (store-files (:params request) config)]
               (if-not (nil? error)
                 (bad-request error)
                 ;; If no error, call callback with metadata
                 ;; Check if error occurred in callback??
                 (callback (agg-metadata (:params request) filepaths) request)))))
         (response "OK"))))

seancorfield19:10:18

You can streamline that a bit by using if-let instead of let / if-not / nil? -- if error is either truthy or nil, you can rely on nil-punning.

seancorfield19:10:58

(if-let [error (is-valid-req (:params request) config)]
  (bad-request error)
  ...

👍 1
seancorfield19:10:11

You can't do much about the second let but saying (if error ..) rather than (if-not (nil? error) ..) would be more idiomatic (and a lot more readable for others).

👍 1
seancorfield19:10:51

I would probably extract the validation to a function and have it do both checks. Then you could at least have:

(let [[filepaths error] (validate-and-store-files (:params request) config)]
  (if error
    (bad-request error)
    (callback (agg-metadata (:params request) filepaths) request)))
(It's unclear to me what your (response "OK") is supposed to be attached to since I would have expected (bad-request error) to return an error response to the caller?)

seancorfield19:10:36

Hmm, I think you're always returning (response "OK") and so bad-request is just logging that an error has occurred? Seems odd to me but, OK.

orpheus20:10:12

No, you’re right. the response "OK" placement is a bug. It should be that if the callback returned without error, it would respond OK.

orpheus20:10:47

I thank you for the tips and pointers. I will implement them as fit. Instead of error checking each fn, would it be better to wrap it all in a try/catch and throw an error from the called functions and just handle check the error once in a catch?

seancorfield22:10:39

Well my feeling on exceptions is that they should only be used for unexpected conditions. If you're validating input, you expect it to be wrong sometimes and you explicitly handle that.

👍 1
Stuart17:10:20

is their a threading macro where it only keeps going as long as a predicate passes. e.g. an equivalent to something like this where M1, M2, M3 etc returns an Either.

M1().Then(M2)
    .Then(M3)
    .Then(M4)
    .Match(left: HandleFail
           right: HandleSuccess)
If say, M2 returns a Left in the Either, then it won't execute M3 and M4... So something like
(???-> (m1)
       (= 5) (m2)        ; if m1 returned 5, do m2
       (= "hotdog") (m3) ; if m2 returned "hotdog", do m3
) ; etc

R.A. Porter17:10:17

Take a look at some->

Stuart17:10:44

that looks like what i want! thanks

orpheus17:10:02

maybe cond-> too?

Stuart20:10:25

I'm trying to call clojure.java.shell and pass it the command rm (WIndows, powershell) and a file to delete. However, I keep getting that it can't find the file specified. Similar calls (sh "ls") , returns the same error. Can't find the file. Same for (sh "pwd") > java.io.IOException: CreateProcess error=2, The system cannot find the file specified user c:\Users\stuarts\Source___TESTREPOS__Clojure\create-dotnet-core-repo\create-dotnet-core-repo.clj:1:1 Yet, calls like (sh "dotnet") works just fine, or (sh "notepad.exe") fires up notepad. Is their something in particular I have to do when dealing with system commands ?

randomm char02:10:29

You have to open a shell first

randomm char02:10:39

(sh "cmd" "/C" "dir")

randomm char02:10:05

(sh "powershell" "/C" "dir")

Stuart08:10:26

that works!, thank you. Good to know if I need this again the future.

dpsutton20:10:19

can you open up a command prompt on windows and type ls? Isn't it dir?

Stuart21:10:08

tried dir as well, same error

Stuart21:10:15

ls works in powershell

Stuart21:10:49

I'm running the .clj file via babashka, should that matter?

borkdude21:10:00

@qmstuart clojure.java.shelldoesn't mean that you can execute unix shell commands on Windows: it simply shells out to the programs on your computer, so it's not a cross platform tool that tries to emulate bash or anything

Stuart21:10:45

ah, so really i can only run stuff that is on the PATH?

borkdude21:10:47

If you are using #babashka and you want to remove files or list files, I suggest taking a look at babashka.fs

Stuart21:10:20

ah ok! That's exactly what I want to do, just delete a file in my script as some tidy up, so I'll look at babashka.fs , thanks for your help!

borkdude21:10:10

The docs are over here: https://babashka.org/fs/babashka.fs.html Feel free to join #babashka if you have more questions

borkdude10:10:25

(I realized you were already in there, sorry, context switching made me forget this :))

Stuart11:10:29

thanks, I got this all working. At work we have 3 main project types, C# dotnetcore, dotnet framework and golang. I've written myself a babashka script that I can feed in a project name, repo name for each project type and it makes me a repo on our github, pulls it, modifies the build and release.yaml so that it builds and uses some of our in house github actions, sets up versioning, issue templates etc, all from one command. It's going to save me so much time, babashka is really great! And the workflow with it connected up to Calva via the bb repl is wonderful. Now, do I show people at work it or keep it my secret... If I show them they will want it, but they will want it rewritten in C# or go...

borkdude11:10:05

I'd say show them and since it's your personal helper script, you would want to share it but on the condition that you can maintain it in your language of choice ;)

borkdude11:10:35

Perhaps you could show them the REPL workflow in Calva too

Stuart11:10:55

to connect to the repl, im doing bb -nrepl-server 1667, then going into Calva and connecting to that repl via connect to running repl, Is that the correct way ?

borkdude11:10:24

yeah, I think so, although calva might also have a jack-in command for that, but this is fine

sova-soars-the-sora23:10:35

is this revenge of the hyphens / underscores?