beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Mario C. 2020-10-17T02:11:04.104200Z

We do a check on the file by creating a hash and if it matches the hash that is stored in an atom, then we skip the load and just use the model that is stored in a registry. We dont need have any state in the models so we always use the same model. But this does help me understand whats going on a little better

2020-10-17T03:30:42.106900Z

Hey team, say I have a function like this:

(def b (fn [a] a))

(def x (fn [a] (b a))) 
I want to write a function called “expand”:
(expand '(x 1))
; => 
'((fn [a] ((fn [a] a) a))
This would in essence “replace” all definitions with the source code To do this, I thought about first writing a new kind of fn macro, which remembers the source code:
(defmacro fn# [args & body]
  (with-meta
    `(fn ~args ~@body)
    {:args '~args
     :body '~body}))
This way, I expand can walk my form and replace from the metadata However, very quickly I came across an issue:
(meta (fn# [a] (+ 1 a)))
Syntax error compiling at (/private/var/folders/sz/rnrd6cv509x2t8nbm6k4mzsh0000gn/T/form-init16854188530357342528.clj:1:7).
Unable to resolve symbol: args in this context
I think I am making a noob mistake here:
{:args '~args
 :body '~body}
Tried a bunch of variations: Just args, ~args, '~args, but no luck. If someone has thoughts would appreciate it!

2020-10-17T03:56:36.107100Z

I think ~'args might be what you want

2020-10-17T03:58:00.107300Z

~ inside of a syntax-quoted expression means sort of "don't syntax-quote the next subexpression". If you use ~args, the thing that gets inserted into the syntax-quoted expression is args, but evaluated at compile time. If you use ~'args, then 'args is evaluated at compile time, which is just the symbol args

2020-10-17T04:09:34.107500Z

Did some experimenting, because I always get this kind of stuff wrong on first guess without trying it out:

(defmacro fn1 [args & body]
  (with-meta
    `(fn ~args ~@body)
    `{:args '~args
      :body '~body}))

❤️ 1
2020-10-17T04:21:05.107800Z

Ah, amazing. Thanks @andy.fingerhut!

2020-10-17T04:24:08.108Z

Ah, I see, the syntax-quote was necessary around our meta map, because otherwise there would be no way to use ~

2020-10-17T04:41:58.109700Z

Okay, final question for the day team. Say I have a namespace: I want to “monkey-patch” def with this:

(ns church-factorial
  (:refer-clojure :exclude [def]))

(defmacro def [name v]
  `(do
    (clojure.core/def ~name ~v)
    (alter-meta! (var ~name) assoc :source {:name '~name :v '~v})
    (var ~name)))
The exclude works, but I’m not sure how to access the original def. I don’t think it’s just defined in clojure.core/def

2020-10-17T17:45:39.134100Z

Learned a lot, thank you team!

teodorlu 2020-10-21T10:33:49.248400Z

(late to the party) Clojure Spec provides its own def, and uses clojure.core/def in the namespace that provides the new def: https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/alpha.clj#L349

2020-10-17T04:52:32.109900Z

I don't think that is possible except via modifying Java code in the Clojure compiler.

2020-10-17T04:52:51.110100Z

def is one of the handful of special forms that are not implemented as functions or macros.

2020-10-17T04:54:45.110300Z

The :exclude [def] does not give an error, but it does not actually mean that def is undefined within the namespace, either:

$ clj
Clojure 1.10.1
user=> (ns foo (:refer-clojure :exclude [def]))
nil
foo=> (def bar 5)
#'foo/bar
foo=> bar
5

2020-10-17T04:56:18.110500Z

As opposed to +, say, which you can actually exclude, and it will not be defined in that namespace:

$ clj
Clojure 1.10.1
user=> (ns foo (:refer-clojure :exclude [+]))
nil
foo=> (+ 1 2)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: + in this context

2020-10-17T04:57:59.110700Z

You can find a list of special forms here, and I would guess that for any of them, you won't get an error if you exclude them, but they will be like def in their behavior -- still defined if excluded, and cannot be monkey-patched from within Clojure itself: https://clojure.org/reference/special_forms

2020-10-17T04:59:44.110900Z

fn and let might be exceptions in that list, since they actually are implemented as macros, with fn* and let* being the underlying special forms they are based upon.

dpsutton 2020-10-17T05:07:43.111100Z

also, I think if you just use def there the compiler doesn't attempt to look special forms up in the environment

dpsutton 2020-10-17T05:10:14.111400Z

❯❯❯ clj
Clojure 1.10.1
(defmacro def [name v]
  `(do
     (def ~name ~v)
     (alter-meta! (var ~name) assoc :source {:name '~name :v '~v})
     (println "i defined " ~name)
     (var ~name)))
#'user/def
user=> (def x 3) ;; refers to clojure def
#'user/x
user=> (user/def x 5) ;; must always have a namespace or alias to refer to def so it doesn't resolve to clojure's
i defined  5
#'user/x
user=>

jumar 2020-10-17T05:17:15.116400Z

Async profiler is great and it basically monitors the whole vm, not particular group of functions; although you can, I think, limit it only to certain threads? Anyway, it’s very useful for cpu profiling and great first tool to reach for. Beaware that if your program spends lot of time “off cpu” you won’t see such bottlenecks in the profiling output

jumar 2020-10-17T05:31:16.120300Z

For OOM: async profiler has “allocation profiling” but I think it does not work on Mac os; yourkit can do that too but it tends to have huge overhead. You can also dump heap on OOM (there’s jvm arg for that) and load the dump in yourkit or VisualVM

jumar 2020-10-17T05:35:04.122700Z

Why you don’t include the models as standard dependency? I don’t see any benefit in having them in the bucket and extracting then into the resources folder

ampersanda 2020-10-17T08:17:58.125300Z

Hello, I try to run -main function in a fresh Clojure CLI project but I got this error.

➜  contsscraper.app git:(main) ✗ clj -A:run        
Execution error (FileNotFoundException) at clojure.main/main (main.java:40).
Could not locate constsscraper/core__init.class, constsscraper/core.clj or constsscraper/core.cljc on classpath.

Full report at:
/var/folders/gv/ry9mmtjj0vx87_ppthlrc4nr0000gn/T/clojure-4702191647831092797.edn
To be honest, I don’t know what’s wrong with my setup, anyone could help? I attach screenshot that might useful Thank you

2020-10-17T08:40:36.127100Z

Hello, is there any good plotting lib which can generate vector graphics, for scientific papers. Thanks.

👍 1
dharrigan 2020-10-17T10:34:24.127800Z

@ampersanda

dharrigan 2020-10-17T10:34:26.127900Z

dharrigan 2020-10-17T10:34:33.128300Z

You're missing a s

practicalli-john 2020-10-17T13:18:33.128600Z

https://hypirion.github.io/clj-xchart/ will generate SVG (vector graphics) I have use Oz for data visualisation, I didnt check to see if it creates vector graphics. The hiccup syntax can be used to create SVG graphics, which is just a data structure. https://clojurebridgelondon.github.io/workshop/introducing-clojure/clojure-svg-graphics.html I havent tried these libraries yet, but they may be of use https://keminglabs.com/c2/ http://liebke.github.io/analemma/

alexmiller 2020-10-17T13:52:13.129900Z

That looks right to me, unless there’s some spelling error in the namespaces somewhere that I didn’t catch

alexmiller 2020-10-17T13:52:58.131300Z

Might be worth trying clj -Sforce -A:run to make sure your classpath isn’t cached wrong somehow

alexmiller 2020-10-17T13:53:48.132400Z

Then I’d back out of the alias and just do clj -m constsscraper.core

alexmiller 2020-10-17T13:54:37.133300Z

Something doesn’t line up here, just need to jiggle it enough to see what

ampersanda 2020-10-17T14:21:29.133500Z

ah thanks, I didn’t see that

ampersanda 2020-10-17T14:22:40.133700Z

Thank you for the replies, I missed the spelling of the namespace https://clojurians.slack.com/archives/C053AK3F9/p1602930866127900

2020-10-17T18:05:59.136300Z

Hey team, one more wacky question:

(defn make-code [x] 
  ...)
(make-code '(foo 1 2)) 
I want this to return:
(
  ; bar
  foo 
  1 2)
I want to “insert” in the “; bar” comment In effect, is there a way I can make clojure “create” a form with comments inside? : O

seancorfield 2020-10-17T18:09:03.137100Z

@stopachka Not if you're producing symbolic code (you'd have to produce a string instead).

👍 1
2020-10-17T18:10:30.137500Z

I see, this makes sense. Thanks Sean!

seancorfield 2020-10-17T18:11:10.138200Z

(because comments are handled by the reader, and the reader turns strings into data structures/code and removes the comments)

❤️ 1
seancorfield 2020-10-17T18:12:37.139300Z

You could always produce (do (comment "bar") (foo 1 2)) -- the comment form is executable and returns nil 🙂

❤️ 1
2020-10-17T18:37:28.141500Z

There are some libraries written with the goal of enabling you to read text representing Clojure code/data, and return not what clojure.core/read or clojure.edn/read would return, but instead a custom data structure that represents all white space, comments, etc. and let you manipulate that. e.g. https://github.com/xsc/rewrite-clj I have not used them, so can't comment on all of the possible gotchas and limitations they might have.

❤️ 1
schmee 2020-10-17T18:51:04.142100Z

I saw this thing from borkdude just the other day, seems to fit the bill perfectly! https://github.com/borkdude/rewrite-edn

❤️ 1
borkdude 2020-10-17T18:53:07.143300Z

For creating / manipulating code while preserving whitespace / comments rewrite-clj is my goto library. I recommend using https://github.com/lread/rewrite-cljc-playground since that's going to be the maintained future of both rewrite-clj(s)(c)

❤️ 1
borkdude 2020-10-17T18:53:29.143900Z

It will be transferred to clj-commons later on

borkdude 2020-10-17T18:54:24.144600Z

rewrite-edn is a lib on top of rewrite-cljc that can help with editing config files while preserving comments

Joe 2020-10-17T21:26:15.147Z

Is there a way to watch activity on an async chan without taking from it? I tried to do something like (tap (mult channel-to-watch) put-on-this-channel) and then but it seemed to mess with the activity on the channel-to-watch

Joe 2020-10-17T21:29:17.147100Z

This works (though is not good because of the sleep)

(let [program [3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26
                 27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5]
        c1-in (async/chan)
        c1-out (run-async program c1-in)
        c2-out (run-async program c1-out)
        c3-out (run-async program c2-out)
        c4-out (run-async program c3-out)
        c5-out (run-async program c4-out)]
    (async/pipe c5-out c1-in)
    (>!! c1-in 9)
    (>!! c1-out 8)
    (>!! c2-out 7)
    (>!! c3-out 6)
    (>!! c4-out 5)
    (>!! c1-in 0)
    (Thread/sleep 5000)
    (<!! c1-in))

phronmophobic 2020-10-17T21:32:04.147300Z

what about poll!?

Joe 2020-10-17T21:32:10.147500Z

What I'm trying to do is set up a tap on ch1-in (or I suppose ch5-out) so I can see what's getting put on there - the final value before the channel closes is what I want. This doesn't work - it throw a null pointer somewhere much further up in the code, which makes me think that the tap is impeding the pipe flow between c1-in and c5-out

(let [program [3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26
                 27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5]
        c1-in (async/chan)
        c1-out (run-async program c1-in)
        c2-out (run-async program c1-out)
        c3-out (run-async program c2-out)
        c4-out (run-async program c3-out)
        c5-out (run-async program c4-out)
        read (async/chan)]
    (async/pipe c5-out c1-in)
    (async/tap (mult c1-in) read)
    (>!! c1-in 9)
    (>!! c1-out 8)
    (>!! c2-out 7)
    (>!! c3-out 6)
    (>!! c4-out 5)
    (>!! c1-in 0)
    (async/<!! (async/go-loop [outputs []]
                 (let [val (async/<! read)]
                   (println "read got val" val)
                   (if val (recur (conj outputs val))
                       outputs)))))

Joe 2020-10-17T21:33:15.147700Z

> what about poll!? Won't that take the value off the chan? I'm trying to not impact the machine that's running in the background, just look at the traffic

phronmophobic 2020-10-17T21:33:26.147900Z

https://clojuredocs.org/clojure.core.async/poll!

Joe 2020-10-17T21:34:16.148100Z

> Takes a val from port if it's possible to do so immediately.

Joe 2020-10-17T21:35:03.148400Z

My worry would be that if I take the val while polling, then it won't get taken by the actual thing that's meant to be consuming it

phronmophobic 2020-10-17T21:35:37.148700Z

yea, I don't think poll! is what you want here

phronmophobic 2020-10-17T21:38:03.148900Z

in general, it's easier to test each individual process and then put everything together than to try to test everything at once

phronmophobic 2020-10-17T21:39:54.149100Z

you could "spy" on values flowing through by wrapping each link in the chain with a process that just prints the value passing through before piping it to the next channel

phronmophobic 2020-10-17T21:42:08.149400Z

something like:

(defn spy-process [prefix ch1 ch2]
  (go-loop [v (<! ch1)]
    (when v
      (prn prefix v)
      (>! ch2 v)
      (recur))))

;; eg.
(spy-process "ch1->ch2" ch1 ch2)

Joe 2020-10-17T21:55:17.149600Z

> in general, it's easier to test each individual process and then put everything together than to try to test everything at once Thanks, this is good advice. I'm getting closer I think, by simplifying the machines a bit:

(defn simple-run [inchan name]
    (let [outchan (async/chan)]
      (async/go-loop [counter 0]
        (if (> counter 10) (do (async/close! outchan))
            (do (println name counter)
                (async/>! outchan (inc (async/<! inchan)))
                (recur (inc counter)))))
      outchan))
  
  (let [in (async/chan)
        out1 (simple-run in :machine1)
        out2 (simple-run out1 :machine2)]
    (async/pipe out2 in)
    (>!! in 100)
    (Thread/sleep 1000)
    (<!! in))
  
  (let [in (async/chan)
        out1 (simple-run in :machine1)
        out2 (simple-run out1 :machine2)
        read (async/chan 1000)
        m (async/mult out2)]
    (async/tap m in)
    (async/tap m read)
    (>!! in 100)
    (<!! (async/go-loop [outputs []]
           (let [val (async/<! read)]
             (if val (recur (conj outputs val))
                 outputs)))))

Joe 2020-10-17T21:57:27.149800Z

I think trying to mix and match pipes and mults in the original was a bad idea. The last one still isn't working - It breaks my repl, I think it's not terminating or something, but I can't figure out why

Joe 2020-10-17T22:11:47.150Z

OK, I guess the read channel isn't closing on it's own properly, since this works OK

(def read (async/chan 1000))

  (let [in (async/chan)
        out1 (simple-run in :machine1)
        out2 (simple-run out1 :machine2)
        m (async/mult out2)]
    (async/tap m in)
    (async/tap m read)
    (>!! in 100))

  (async/close! read)

  (<!! read)

phronmophobic 2020-10-17T22:11:48.150200Z

if there's an exception, I don't think simple-run will close the channel which might be causing your last block to hang

phronmophobic 2020-10-17T22:12:24.150400Z

you'll probably want to wrap the go-loop in a try/catch and close the channel in the catch clause

Joe 2020-10-17T22:19:22.150700Z

I don't think there is any exception being thrown, because I can run it corectly 'by hand', just pulling values of the read chan until it nils. I don't see anything in the docs for mult about it automatically closing when its out2 closes. Maybe that's it?

Joe 2020-10-17T22:20:15.150900Z

I would think it would though...

Joe 2020-10-17T22:29:00.151200Z

OK, works fine when I increase the buffer

(defn simple-run [inchan name]
    (let [outchan (async/chan 100)]
      (async/go-loop [counter 0]
        (if (> counter 10) (do (async/close! outchan))
            (do (println name counter)
                (async/>! outchan (inc (async/<! inchan)))
                (recur (inc counter)))))
      outchan))

  (let [in (async/chan 100)
        out1 (simple-run in :machine1)
        out2 (simple-run out1 :machine2)
        m (async/mult out2)
        read (async/chan 100)]
    (async/tap m in)
    (async/tap m read)
    (>!! in 100)
    (async/<!! (async/go-loop [outputs []]
                 (let [val (async/<! read)]
                   (println "read got val" val)
                   (if val (recur (conj outputs val))
                       outputs)))))
So I guess what's happening with no buffer is that 1. machine 1 sends it's final output to machine 2, then stops 2. machine 2 sends it's final output the mult (to machine 1 and read), then stops 3. machine 1 is stopped, so will never take the val of it's input. This blocks the (synchronous) mult, which never closes, and so read also never closes

🎉 1
jsn 2020-10-17T22:43:18.151600Z

can you use a chan transducer for spying?