Fork me on GitHub
#beginners
<
2018-11-26
>
thiru00:11:02

Thanks @andy.fingerhut! (flush) did the trick

andofryn11:11:39

Hey All. When I do (map (fn [nr] '(nr)) [1 2 3]), the result I get from my REPL is ((nr) (nr) (nr)), why isn’t this ((1) (2) (3)) ?

schmee12:11:02

quote is recursive, so it will quote everything inside the list

jaihindhreddy-duplicate13:11:59

A pedantic correction. quote isn't recursive. Recursion implies it looks inside the data structure. It's evaluation semantics are simply, return the thing.

schmee12:11:11

try (list nr) instead

👍 4
andofryn12:11:12

oh wow yeah that works, I don’t understand exactly why though. Is the quote character somehow telling the compiler to take everything inside of it literally?

Alex Miller (Clojure team)14:11:25

it would be more accurate to say that quote stops the compiler from doing half of its job. Normally, it “reads” (from characters into Clojure data structures), then “evaluates”. Quote says to not evaluate and just return literally the data structures that were read.

👍 4
schmee12:11:02

precisely! when you see quote, everything inside whatever you’ve quoted will be returned exactly as it looks

bronsa12:11:11

@tim.degroote yes, the quote special form (which ' maps to), tells the compiler to stop evaluation for the next form

bronsa12:11:55

there is also the syntax-quote form (the backtick), which accepts "unquoting" via ~ and "unquote splicing" via ~@

bronsa12:11:28

so you can do for example

`[a b ~c d]

bronsa12:11:53

this is rougly equivalent to ['a 'b c 'd] (except syntax-quote also namespace qualifies symbols)

andofryn12:11:49

oooh interesting, didn’t know this! This cleared it up for me, thanks a lot for the help @schmee @bronsa!

bronsa12:11:26

you can read a bit more about syntax-quote here https://clojure.org/reference/reader#syntax-quote

4
mseddon16:11:08

Is there something like resolve that, rather than returning the var, returns the fully qualified symbol name?

mseddon16:11:28

e.g. (symbolic-resolve '+) => clojure.core/+

bronsa16:11:07

in 1.10 that's just (comp symbol resolve)

bronsa16:11:13

but you can use syntax-quote for that

bronsa16:11:29

if you can do that at read-time

mseddon16:11:30

doh. perfect. thanks

mseddon16:11:44

yeah, i need a function since I am handling this at macroexpansion time

mseddon16:11:00

and quasiquote is ruining my symbol equality tests 😉

mseddon16:11:44

hmm. no not quite right. (comp resolve symbol) returns a var again. I need the fully qualified symbol

bronsa16:11:59

check what I wrote :)

bronsa16:11:07

you want to transpose those two functions

bronsa16:11:15

symbol resolve, not resolve symbol

mseddon16:11:24

ah, but .. hm. swapping that fails for me

mseddon16:11:32

((comp symbol resolve) '+) ClassCastException clojure.lang.Var cannot be cast to java.lang.String clojure.core/symbol (core.clj:579)

bronsa16:11:37

well yes, unless you're on 1.10

bronsa16:11:41

it's a new feature

mseddon16:11:59

ooh, doh. sorry i misread 1.10 as some incredibly old version of clojure...

bronsa16:11:31

if you're on 1.9 you'll have to do it manually by accessing the .sym and .ns fields of the var

mseddon16:11:38

ahh, secret hack.

dpsutton16:11:08

you read 1.1 == 1.10. simple and understandable mistake 🙂

mseddon16:11:22

😄 I suppose I did

bronsa16:11:46

one sec, I'll link you the impl

bronsa16:11:45

(defn var->sym
  [^clojure.lang.Var v]
  (symbol (str (.name (.ns v)))
          (str (.sym v)))
  )

bronsa16:11:52

this is the usual impl pre 1.10

mseddon16:11:28

that's perfect, thanks!

alexpashkov17:11:31

Hello guys! I’m writing a small program that solves n-puzzle problem with a* algorithm. The main function iterates over different states of the puzzle to find a path towards the goal state. I used atoms to hold some local state in the function and it worked just fine, but I feel maybe there is a better (more performant or idiomatic) way to do it. I understood that atoms are used for synchronous concurrent state. In my case I don’t have any concurrency involved. I’ve been thinking maybe :^dynamic vars with binding and set! can be a solution. Here is the code:

(defn solve [pzl heur]
  (let [target (generate (count pzl))
        a*-eval (partial puzzle/a*-eval heur)
        open-set (atom (priority-map pzl (a*-eval pzl)))
        closed-set (atom #{})]
    (loop [cur (first (peek @open-set))]
      (swap! open-set pop)
      (swap! closed-set conj cur)
      (if (not= cur target)
        (do
          (doseq [neighbor (get-neighbors cur)]
            (let [neighbor-cost (a*-eval neighbor)]
              (when-let [neighbor-cost-in-open-set (@open-set neighbor)]
                (when (< neighbor-cost neighbor-cost-in-open-set)
                  (swap! open-set dissoc neighbor)))
              (when-let [neighbor-in-closed-set (@closed-set neighbor)]
                (when (< (puzzle/count-parents neighbor)
                         (puzzle/count-parents neighbor-in-closed-set))
                  (swap! closed-set dissoc neighbor)))
              (when-not (or (contains? @open-set neighbor)
                            (contains? @closed-set neighbor))
                (swap! open-set assoc neighbor neighbor-cost))))
          (recur (first (peek @open-set))))
        cur))))
Maybe, somebody can give an advice?

noisesmith17:11:56

why are you using atoms?

alexpashkov17:11:12

to change value of the queue and closed set. I didn’t take much time to come up with a recursive algorithm

noisesmith17:11:14

you are paying a perf overhead for using atoms where there's no data races, and the idiomatic version would be to return new values for open-set and closed-set from each step that can alter them

noisesmith17:11:46

including making open-set and closed-set args to the loop/recur

noisesmith17:11:17

also, in code that actually used the atoms for concurrency, your usage of the atoms would have a race condition

alexpashkov17:11:19

ok got it, I would reformulate the code. can you briefly describe the last point?

noisesmith17:11:36

(let [foo (peek @a)] (swap! foo pop)) - this is a race condition because the two usages of foo to peek and pop are not coordinated

noisesmith17:11:49

two blocks could peek the same value, and then pop two different values

noisesmith17:11:05

thus using one value twice, and dropping another value on the floor

noisesmith17:11:40

basically any pair of operations on the same atom that mix a read and a write are probably a race

alexpashkov17:11:59

got it, thank you a lot!

noisesmith17:11:23

in your case the race never happens because the code is known to be single threaded

noisesmith17:11:34

but it's good to recognize patterns like that

alexpashkov17:11:05

absolutely, I’ve been thinking how to parallelize the algorithm to get better performance, but that maybe the next step after refactoring to recursion

noisesmith17:11:13

the pattern you'll likely end up with: (let [[open-set closed-set] (op open-set closed-set) [open-set closed-set] (op2 open-set closed-set) ...] [open-set closed-set])

noisesmith17:11:32

it becomes simpler if you group the data that gets changed together into one map with keys for each valaue

noisesmith17:11:59

so (-> {:open open-set :closed closed-set} (op) (op2) ...)

noisesmith17:11:28

then you rewrite each of the nested conditional ops into separate functions you can place into that chain

alexpashkov17:11:42

thanks a lot!

noisesmith17:11:58

the conceptual approach is changing each "thing" that changes some object into a function that takes the object and returns a replacement

noisesmith17:11:59

this is part of why vectors and maps are so ubiquitous in our code - we need to keep track of multiple updated values if a function needs to update more than one piece of data

patrick19:11:50

Hello there, i have a short question. Do I understand it correct that if i use cond all expressions are evaluated?

dpsutton19:11:21

(cond (do (prn "eval test 1") false) (prn "result 1")
      (do (prn "eval test 2") true)  (prn "result 2"))
should be able to show you what is going on

dpsutton19:11:59

note which tests are evaluated and which consequences. change up true and false as well

patrick19:11:32

Ok thank you very much! I was confused, because i thought it would always evaluate all expressions 😆

Lennart Buit20:11:37

Heya, my google fu is failing me, lets say I have a core async channel with collections of values, is there a simple function to put those values one-by-one on that channel?

Lennart Buit20:11:51

so, visually I have something like this: ... -> [a b c] -> [d e f] -> [g h i] -> ..., and I want to go to ... -> a -> b -> c ... -> g -> h -> i -> ...

Alex Miller (Clojure team)20:11:19

you could use a (mapcat identity) transducer on the channel

Alex Miller (Clojure team)20:11:39

or actually maybe just cat

Alex Miller (Clojure team)20:11:49

can’t say I’ve ever done that but might be just right

Lennart Buit20:11:35

yeah, I was googling for transducers but couldn’t wrap my head around how that would work ‘the other way around’ so to say. Will try, thanks!

Alex Miller (Clojure team)20:11:29

yeah, that should work fine

Lennart Buit20:11:54

Cool, thanks for verifying

Lennart Buit20:11:46

oh huh how does that work with buffers

dpsutton20:11:06

there's also onto-chan that you can read the docstring of to see if it helps here or in other similar situations

Lennart Buit20:11:15

core.async is a … bit to get my head around haha

Lennart Buit21:11:00

Feeling a bit like a plumber, “does this channel fit to this channel”

athomasoriginal22:11:46

I am writing a spec for a tuple. The only condition is that both positions in the tuple have to be the same data type. For example,

[1 45] ; good 
["hello" "blah"] ; good 
[:hello :blah ] ; good
[1 :hello] ; bad 
How would I go about doing this?

hiredman22:11:06

a spec is a predicate, so can you write a predicate that given some data returns true if it matches your criteria and false otherwise?

Alex Miller (Clojure team)22:11:23

to ensure same type, you’ll need some custom predicate

Alex Miller (Clojure team)22:11:41

but you can either write your own or combine with something like s/tuple or s/coll-of

👍 4
sova-soars-the-sora22:11:37

troubleshooting figwheel hanging on compile?

sova-soars-the-sora22:11:51

(print-config id ...)           ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
          (figwheel.client/set-autoload false)    ;; will turn autoloading off
          (figwheel.client/set-repl-pprint false) ;; will turn pretty printing off
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application
[Rebel readline] Type :repl/help for online help info

sova-soars-the-sora22:11:15

... never goes anywhere

sova-soars-the-sora22:11:43

i even started a fresh project via lein new macchiato app ;_;

hiredman22:11:10

what is in your ~/.lein/profiles.clj

ccann22:11:23

is it possible to use require with a wildcard? something like (require 'foo.bar.*)

sova-soars-the-sora23:11:28

vas$ cat profiles.clj
{:user {:plugins [
	 	  [lein-ancient "0.6.10"]
		  [http-kit/lein-template "1.0.0-SNAPSHOT"]
                  ]}}

sova-soars-the-sora23:11:17

do i need figwheel in my plugins? o.o

hiredman23:11:54

no, but I would try commenting out all the plugins in profiles.clj

sova-soars-the-sora23:11:23

for now I try with commenting out lein-template and see how it goes.

sova-soars-the-sora23:11:02

strangely, my app was compiling yesterday. so i don't know what i changed that caused it to no longer function. and there's no errors... so yeah, probably something weird in the dependency chain

hiredman23:11:01

the macchiato template looks insanely complicated

sova-soars-the-sora23:11:36

hey that may be so, but i can use cljs and make a node server so... it's nice [when working]

dpsutton23:11:57

did you use it with rebel readline yesterday?

sova-soars-the-sora23:11:11

no i don't even know what rebel readline is

dpsutton23:11:15

just making sure that's not the new part

sova-soars-the-sora23:11:52

i think that was there yesterday, that helps color code the repl iirc

sova-soars-the-sora23:11:57

and i had color coding so

dpsutton23:11:05

ok. just making sure that wasn't the variable

sova-soars-the-sora23:11:44

thanks. i may try disabling it... it's not actually getting me to a repl

hiredman23:11:20

the last time I did any cljs stuff I wrote a little script that called the cljs compiler and generated some js from some cljs code, which node was pretty happy with, without all that stuff

sova-soars-the-sora23:11:55

you're saying maybe do it from scratch without macchiato?

hiredman23:11:15

I dunno, maybe the cure is worse than the disease

sova-soars-the-sora23:11:06

yeah. i could just make an empty figwheel project using rum that also compiles a javascript file for a node server...

lilactown23:11:27

@sova did you start the node process?

sova-soars-the-sora23:11:07

i can't start the node process until the javascript is compiled, and that step never completes.

sova-soars-the-sora23:11:44

i really don't know what i did, but it just hangs on compilation

sova-soars-the-sora23:11:07

hangs on launching CLJS repl

sova-soars-the-sora23:11:27

i guess the JS is compiled... lemme try firing up that npm process

lilactown23:11:40

how are you launching the REPL?

Mario C.23:11:57

How to simplify (remove nil? (remove my-map atts))?

sova-soars-the-sora23:11:21

doh! @lilactown launching the npm process was the answer!

sova-soars-the-sora23:11:42

wishing i hadn't undone about ~2 hours of commits eaarlier but heyyy live and learn

lilactown23:11:59

been there 😛

jstaab23:11:32

Hey everyone, how can I get some useful information from errors thrown during tests run by shadow-cljs using cljs.test? For the following:

(deftest test-nonsense
  (doall (map identity identity)))
I get:
ERROR in (test-nonsense) (Error:NaN:NaN)
Uncaught exception, not in assertion.
expected: nil
  actual: #object[Error Error: function cljs$core$identity(x){
return x;
} is not ISeqable]
I've tried file-and-line but it gives me nil for both.

jstaab23:11:57

Also, I'm on nodejs, so I can't use the devtools extensions and such (or can I?)

jaawerth23:11:06

if you're using sourcemaps you can get node to leverage them in stacktraces via https://nodejs.org/en/docs/guides/debugging-getting-started/ - it should also wor kfrom chrome devtools if you're using it as a client when using node --inspect or --inspect-brk

jstaab23:11:26

Any idea how I might pass that to node using shadow-cljs?

lilactown23:11:24

you're probably having shadow-cljs auto-run the tests, but you can also run them from a terminal using node <output/location/of/tests>.js

jstaab23:11:53

Yep, I am autorunning. I'd like to keep that, although I am fiddling with the manual running for now

jstaab23:11:00

Is there no way to get better traces without using a debugger though? I've never been super happy with js errors, but I know there are stack traces to be had

jstaab00:11:47

I just tried running the bad code outside deftest and got a lot more information. Is there a way to get cljs.test to not suppress the stack?

jaawerth00:11:39

you'll need the sourcemaps - you should be able to just add that node module I linked as a dep or dev-dep and just require it at the entrypoint to the code. having not used shadow-cljs, I don't know how that would differ from the regular compilation wrt :npm-deps

lilactown00:11:28

running the offending code in a REPL is always a good idea. easier to hack on it, anyway

jstaab00:11:23

@U9QQRN612 which node module? You linked to a docs page about more general debugging.

jaawerth00:11:23

my bad, thought I'd replaced it in the clipboard

jstaab00:11:32

Ah, neat, thanks I'll give that a try

jaawerth00:11:16

as for customziing output from cljs.test... I know there are some general variables for customizing error behavior, but the only one I know offhand is *print-err-fn*

jaawerth00:11:45

that's not specific to cljs.test though

jstaab02:11:18

FWIW, I figured out where I was having trouble. In the repl or when running my code, errors were formatted as expected, using source maps; in tests, however, errors are caught by cljs.test and formatted using the :formatter property of the test environment, which you can pass to run-all-tests. I added a main function to my testing shadow-cljs build with a custom error formatter that invokes *print-err-fn* if it's an error. Here's my code:

(defn print-err [e]
  (if (.-stack e) (*print-err-fn* e) (pr-str e)))

(defn main []
  (run-all-tests #"my-project.*" (assoc (test/empty-env) :formatter print-err)))

jstaab23:11:53

I guess more specifically, I'd love to have a sourcemapped trace.

jaide23:11:32

Is there a simple way to create a new vec with an element inserted after an index?

jstaab23:11:18

Maybe try (flatten (interpose :x (split-at 2 [1 2 3 4 5 6])))

jstaab23:11:16

I guess for your example it would be (flatten (interpose :b (split-at 1 [:a :c])))

hiredman23:11:23

the best answer is no

hiredman23:11:59

vectors don't have a fast way to insert in to the middle, so you should avoid doing so

hiredman23:11:13

and never ever use flatten

jaide23:11:46

If it helps, I’m inserting a map if there is none into a hiccup vector which I believe has a maximum number of 3 elements.

jaawerth23:11:52

My approach would be (defn insert-v [v idx value] (into (conj (subvec v 0 idx) value) (subvec v idx))) but inserting in the middle is still going to be O(n)

jstaab23:11:43

@U0NCTKEV8 is the problem with flatten that there might be sub-collections?

jstaab00:11:53

:thumbsup:

jaawerth00:11:12

core.rrb-vector ( https://github.com/clojure/core.rrb-vector ) also has a catvec which will still work efficiently by converting regular vectors but I believe there is still some question as to whether it has (performance) edge cases

jaawerth00:11:15

though I could be wrong about the edge cases

jaide00:11:21

Thanks for the solutions. I got quite a lot out of this conversation 😎

jstaab02:11:18

FWIW, I figured out where I was having trouble. In the repl or when running my code, errors were formatted as expected, using source maps; in tests, however, errors are caught by cljs.test and formatted using the :formatter property of the test environment, which you can pass to run-all-tests. I added a main function to my testing shadow-cljs build with a custom error formatter that invokes *print-err-fn* if it's an error. Here's my code:

(defn print-err [e]
  (if (.-stack e) (*print-err-fn* e) (pr-str e)))

(defn main []
  (run-all-tests #"my-project.*" (assoc (test/empty-env) :formatter print-err)))