Fork me on GitHub
#beginners
<
2019-02-11
>
Ramzi06:02:29

I have a text file I want to use as input. I can't open this file in Javascript without getting cross origin request errors. Is there an easy way I can get the contents of this text file into a variable, so I can continue rapid development, without having to wory about the cross origin request errors?

Lennart Buit06:02:43

So I’d assume we are talking clojurescript now. I think your best bet is either by serving it from your computer OR adding a file field to your page

Lennart Buit06:02:07

If you are serving it from your computer, you must correctly set the cross origin request headers

skykanin14:02:04

So I've setup a webserver with http-kit which serves my html file. This html file also includes my clojurescript code which I compile using lein-cljsbuild . How would I go about adding a javascript library which doesn't have a clojurescript wrapper to my project so that I can use it in my clojurescript file?

john15:02:30

@nicholas.jaunsen there's lots of ways to skin that cat. A main one is the "foreign libs" route.(https://clojurescript.org/reference/packaging-foreign-deps) There's various mechanisms to try to integrate with npm too - some ship with core and some tools, like shadow-cljs is well known for wrangling npm deps for cljs.

john15:02:25

But the old school "add a script tag to your html file" works too. You just have to make sure that you're taking care of externs: https://clojurescript.org/guides/externs

john15:02:18

You may want to hunt down a larger example project that uses http-kit, to see how things are put together. If you have most experience with a traditional LAMP stack, then yogthos's luminus framework (collection of libs) is a great jump off point: http://www.luminusweb.net/docs/clojurescript.md

john15:02:22

here's an "http-kit adapter" for luminus, for pointers on how http-kit would fit into the luminus framework https://github.com/luminus-framework/luminus-http-kit

john15:02:48

Well, I was hoping luminus docs had more examples of leveraging foreign js libs. Looks like the main docs are using mostly packaged things, per http://www.luminusweb.net/docs#the_project_file (see the webjars stuff)

john15:02:10

But in any case, if you're leveraging server side framework like luminus over something like http-kit, you'll often be defining the delivery of your 3rd party js assets in the server-side logic, using script tags in your hiccup forms or something like that. Whereas, if you're going the mostly SPA route (which you can still serve up from http-kit) then folks usually stick to wrapping js libs as cljs packages so that deps are available to code via native cljs :require dep resolution (rather than via interop calls).

ben15:02:53

I think I posted this to the wrong channel (it’s just vanilla compjure, not compojure-api), so I am cross-posting here (I hope that is okay)

mathpunk17:02:32

I'm trying to commit to Running with Scissors.... I am curious how people structure their projects to bring in repl tools they always use, repl tools that are project specific, and/or preserving repl explorations

mathpunk17:02:56

right now I have a src dir and a siderail dir with a user.clj in it, but that file is getting kind of wild and unruly

noisesmith17:02:08

for repl explorations, I like having multiple forms in a file like #_(fire-missiles) - that can be in the actual source, or a test, or a dedicated file for repl explorations

noisesmith17:02:52

bonus, having a multi line version

#_
(fire-missiles)
makes it easier to use with editor integrations or even copy/paste

dpsutton17:02:43

I think i've heard @hiredman keeps around a lot of history. I'd be interested to hear his approach if he has time

dpsutton17:02:00

something like 6kloc of just explorations, repl history, etc

noisesmith17:02:55

also if you use the nrepl terminal UI, all your history is in a file in the top level of the project .nrepl_history

noisesmith17:02:01

you can copy paste from that

noisesmith17:02:31

if CIDER / fireplace etc. don't have that, it would be a trivial feature to implement...

dpsutton17:02:50

CIDER has a repl history that is browsable, saveable, etc

dpsutton17:02:56

not sure about fireplace.

noisesmith17:02:01

yeah, not surprised at all :D

hiredman18:02:20

I keep a couple of files called scratch.clj stored in different places

hiredman18:02:19

my personal ~/src/scratch.clj is 9kloc right now, and I have another for work related odds and ends

hiredman18:02:41

if I write clojure that doesn't immediately belong in another file it starts there

dpsutton18:02:30

you copy and paste? can you easily associate the file with a repl?

hiredman18:02:48

yeah, I copy and paste

hiredman18:02:18

I also have a scratch.lua, .go, .sh, and .sql

felbit18:02:26

I have the same for every project. It‘s in the project root and is something like „my repl history - the good parts“

hiredman18:02:44

yeah, and a good scratch file is very stream of consciousness, not much going back and editing, lots of duplicaction

bronsa18:02:48

a single scratch file seems much more of a better idea than my hundreds of randomly named, randomly placed snippet files I can never find

bronsa18:02:14

this is the sort of ideas we pay you for @hiredman

😂 5
Raymond Ko19:02:31

For users of reagent, a question, assuming @name becomes a primitive JS string, how does Reagent know that a component used an atom? Does it measure/snapshot check which atom has been deref on first render?

noisesmith19:02:28

@raymond.w.ko the r/atom code checks for surrounding context when you deref, and registers itself with the template if it finds one

Raymond Ko19:02:09

Wow, thanks 🙂

dpsutton19:02:30

lol. although i'm starting to think that's not the most relevant line of code ... maybe the add watchers part. either way it just tracks who is watching it

Raymond Ko19:02:11

I was wondering that since [f] (a vector consisting of 1 function) doesn't change between renders, how does it "know" it has to re-render...

dpsutton19:02:02

if you look at the IReset protocol underneath that, when the value is changed it notifies watches

dpsutton19:02:24

so its a bit of bookkeeping of writing down who is watching and when the value changes let those watchers know

dpsutton19:02:38

and if you chase that around a bit you end up in

(defn- rea-enqueue [r]
  (when (nil? rea-queue)
    (set! rea-queue (array))
    (batch/schedule))
  (.push rea-queue r))
which is what ultimately runs on every watching component. it gets scheduled to rerender

lilactown19:02:54

the other thing to remember: every reagent component instance gets converted to a React component instance with it's own state and lifecycle

dpsutton19:02:16

^ very knowledge about React

john19:02:16

It's like an atom with a permanent add-watch that triggers a refresh

lilactown19:02:30

s [f] looks like a vector with a function that doesn't change, but when it gets rendered, it gets converted to a stateful thing. so when the ratom inside it changes, it triggers a re-render inside the React component instance

noisesmith19:02:20

the vector is a template that the driver function uses to create the component right?

Raymond Ko19:02:28

Like Reagent sort of maps [f] to a React component and the deref marks this React component dirty?

noisesmith19:02:38

and that component then gets registered for watching the ratom

noisesmith19:02:00

the deref marks the component as something to update if the ratom is dirty

john19:02:58

In my mental model, a swap! triggers it, rather than a deref... Then the diff happens on the React side and any dependent components get updated. Right @lilactown?

dpsutton19:02:09

i think you are correct

lilactown19:02:54

swap! causes the ratom to look up any components that have registered them selves, and then the ratom tells them to eventually forceUpdate

Raymond Ko19:02:00

@john that seems to make more sense.

lilactown19:02:22

yeah sorry, I misread your message wrong @raymond.w.ko

lilactown19:02:06

the deref ties the component and ratom together

lilactown19:02:07

reset! / swap! tells the ratom to look up any associated components and start triggering updates.

Raymond Ko19:02:04

Okay, thanks all, I think I understand enough to use it confidently. I was worried that it wouldn't get updates.

Raymond Ko19:02:44

This still seems like magic to me 😣

lilactown19:02:58

it is all a bit magical

lilactown19:02:33

no more magical than React is 😛 but perhaps with some idioms that are not as common

john19:02:44

The lifecycle stuff is where the magic turns into debt, imo. But perhaps the new hooks stuff will smooth that stuff over.

dpsutton19:02:44

any sufficiently complicated bookkeeping will appear as magic

lilactown19:02:44

if there was a way to do componentDidCatch, I'd never have to use React classes again I think

Nick Stares19:02:50

how do you print a large data structure? seems like at some point (around 100 elements or so) I just get [el el el ...]

lilactown19:02:53

but that might be doable with an HOC or render prop

noisesmith19:02:55

@nickstares0 clojure.pprint/pprint and clojure.pprint/print-table both work great, depending on context

john19:02:18

also good with with-out-str

(defn pp-str [x]
  (with-out-str (clojure.pprint/pprint x))

vlad_poh19:02:06

Howdy! I get errors when i run "lein cljsbuild auto" and not sure how to troubleshoot

vlad_poh19:02:13

can't find a log file

noisesmith19:02:18

user=> (clojure.pprint/print-table (map #(zipmap [:a :b :c :d] (repeat %)) (range 10)))

| :a | :b | :c | :d |
|----+----+----+----|
|  0 |  0 |  0 |  0 |
|  1 |  1 |  1 |  1 |
|  2 |  2 |  2 |  2 |
|  3 |  3 |  3 |  3 |
|  4 |  4 |  4 |  4 |
|  5 |  5 |  5 |  5 |
|  6 |  6 |  6 |  6 |
|  7 |  7 |  7 |  7 |
|  8 |  8 |  8 |  8 |
|  9 |  9 |  9 |  9 |
nil

Lennart Buit19:02:54

wow thats pretty neat!

Nick Stares19:02:32

doesn't work for the vector i'm using! I still get ... at the end

Nick Stares19:02:41

slack won't let me post the whole thing but the end is

(with-out-str (clojure.pprint/pprint (converge 200 2)))
[
{:state-counts {:a 80, :b 120}, :period 85}\n {:state-counts {:a 69, :b 131}, :period 86}\n {:state-counts {:a 73, :b 127}, :period 87}\n {:state-counts {:a 77, :b 123}, :period 88}\n {:state-counts {:a 89, :b 111}, :period 89}\n {:state-counts {:a 78, :b 122}, :period 90}\n {:state-counts {:a 72, :b 128}, :period 91}\n {:state-counts {:a 73, :b 127}, :period 92}\n {:state-counts {:a 73, :b 127}, :period 93}\n {:state-counts {:a 73, :b 127}, :period 94}\n {:state-counts {:a 74, :b 126}, :period 95}\n {:state-counts {:a 64, :b 136}, :period 96}\n {:state-counts {:a 63, :b 137}, :period 97}\n {:state-counts {:a 78, :b 122}, :period 98}\n ...]\n"

noisesmith19:02:01

oh! the ... is the problem? that's caused by *print-length* iirc (there's some other related vars you can rebind). I misinterpreted your question

Nick Stares19:02:28

Yes! I'm trying to write a large vector to a file and all of the tutorials say to use pprint or pt-str

Nick Stares19:02:01

(with-open [w ( "values.edn")]
    (binding [*out* w]
      (pr-str (converge 100 2))))

noisesmith19:02:33

user=> (apropos #"^\*print.*")
(clojure.core/*print-dup* clojure.core/*print-length* clojure.core/*print-level* clojure.core/*print-meta* clojure.core/*print-namespace-maps* clojure.core/*print-readably* clojure.pprint/*print-base* clojure.pprint/*print-miser-width* clojure.pprint/*print-pprint-dispatch* clojure.pprint/*print-pretty* clojure.pprint/*print-radix* clojure.pprint/*print-right-margin* clojure.pprint/*print-suppress-namespaces*)
- *print-length* is the main one, but there's some others that have effect too

noisesmith19:02:58

you can set it to nil to allow arbitrary depth

Nick Stares19:02:35

awesome, so like this?

(def ^:dynamic *print-length* nil)

Nick Stares19:02:47

Or should I write over the one in clojure.core?

noisesmith19:02:21

with dynamic vars, you can use set! or binding

(ins)user=> (set! *print-length* 5)
5
(ins)user=> (range)
(0 1 2 3 4 ...)
(ins)user=> (binding [*print-length* nil] (pr-str (range 20)))
"(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)"

noisesmith19:02:44

binding is better in general, as it avoids the spooky action of global settings

Nick Stares19:02:36

great, i'll use the binding

noisesmith19:02:38

yeah, for your usage (binding [...] (with-out-str ...)) is likely ideal

noisesmith19:02:58

just be sure anything lazy is forced before leaving the with-out-str block

Nick Stares19:02:49

Yeah, I think the laziness is my problem. I read that doall will force strict evaluation but that's not doing the trick

noisesmith19:02:00

doall works on a single level only

noisesmith19:02:22

but println / prn / pprint will all force the whole input

noisesmith19:02:04

but they also obey *print-length*, which isn't about laziness, it's about how printing is done (which interacts with laziness but is distinct)

Nick Stares19:02:30

I think I understand the distinction

Nick Stares19:02:40

(with-out-str (binding [*print-length* nil] (clojure.pprint/pprint (doall (converge 200 2)))))

Nick Stares19:02:45

but I would expect that to work

Nick Stares19:02:22

(defn converge [n s]
  "Creates a population of n agents with s possible states and runs a period of interaction
  on that population until all agents have the same state. Returns a
  vector of maps, where each map contains the count of agents after
  each period, e.g. [{:state-counts {:a 1 :b 19} :period 0}
  {:state-counts {:a 0 :b 20} :period 1}]"

  (loop [period     0 
         population (population n s)
         results    (vector (-> (run-period population s) 
                                (assoc :period period)))]

    (if (some #(= % (count population)) (vals (:state-counts (peek results)))) 
      results
      (recur (inc period) population (conj results (-> (run-period population s)
                                                       (assoc :period period)))))))

noisesmith19:02:43

you don't need doall inside pprint - pprint will eagerly realize the inside as long as *print-length* is nil; if *print-length* isn't nil, it will only force the top level, and no children

noisesmith19:02:20

and converge itself is not lazy, so there's no point in doall over that - it's the children of converge that might be lazy

Nick Stares20:02:40

Ok, 3 steps of indirection down the children of converge use a fn that returns a lazy sequence via repeatedly

noisesmith20:02:04

you could do a tree operation to recursively force the input, but the printing code will already force the input up to the limits of *print-length* - hope I'm not repeating myself too much here, want to make sure my point is communicated properly

Nick Stares20:02:00

Yeah, I think I just need to force the input at the bottom leaf of the tree since there is only one lazy seq

Nick Stares20:02:12

Or rather only one place where lazy seqs are introduced

Nick Stares20:02:40

(defn population [n s]
  "Creates a population of n agents that can each have s possible states"
  (repeatedly n (partial new-agent s)))

Nick Stares20:02:11

Thank you so much for helping with all of this as I chase down a rabbit hole...

Nick Stares20:02:55

Is there a better way to put n elements into a collection besides repeatedly? seems like I am not using that fn as intended

noisesmith20:02:07

you can use (vec (repeatedly ...)) if laziness isn't desirable, also you might consider replacing (partial f arg) with #(f arg) for conciseness / readability

Nick Stares20:02:06

Awesome, that did the trick. Thank you so much!!

Nick Stares20:02:19

and thanks for the readability tip, seems obvious in retrospect 😛

noisesmith20:02:47

I wish we had one or two character versions of identity, comp, partial apply etc. - so useful, but if they are more than two characters they add some noise...

noisesmith20:02:02

(of course any of us can define our own aliases for all those functions...)

noisesmith20:02:17

oh and complement too

Nick Stares20:02:27

ooh, complement is a good one that I forget to use

Lennart Buit20:02:58

partial and compliment are on the long side

Lennart Buit20:02:23

especially when you can ‘fake’ partial by #(fun arg1 arg2 %1 %2 ...)

noisesmith22:02:58

and complement is (comp not f)

Lennart Buit07:02:57

few characters saving!

noisesmith19:02:30

@kbosompem I've found it useful to stop the auto run, run lein clean then lein cljsbuild once if I discover a nontrivial error

vlad_poh19:02:24

@noisesmith my lein clean reports no errors and only empties the resource folder but not the target

vlad_poh19:02:03

C:\Sources\moose>lein cljsbuild once Compiling ClojureScript... Compiling ["resources/public/js/app.js"] from ["src/cljs"]... Compiling ["resources/public/js/app.js"] failed. clojure.lang.ExceptionInfo: failed compiling file:resources\public\js\out\cljs\core.cljs {:file #object[java.io.File 0x2bfb583b "resources\\public\\js\\out\\cljs\\core.cljs"]} at cljs.compiler$compile_file$fn__3702.invoke(compiler.cljc:1562)

noisesmith19:02:05

then you might want to update your clean-targets - if you customize where the targets go, clean needs to be updated to stay in sync

noisesmith19:02:10

in the mean time you can manually remove your target file(s), the point of running clean is that continuous auto-compiles can easily lead to inconsistent state reflecting a transient situation or a bug you already fixed

vlad_poh19:02:25

@noisesmith what i really need is a simple example of a re-frame app with a clojure backend

vlad_poh19:02:51

been wrestling with cljsbuild and figwheel for 2days

noisesmith19:02:33

I'm not sure what's most up to date / recommended today, but iirc luminus has a lot of good examples and docs http://www.luminusweb.net/

john19:02:58

chestnut is also a pretty solid template https://github.com/plexus/chestnut

john19:02:47

has re-frame and http-kit options

vlad_poh19:02:03

@noisesmith luminus was overwhelming. base template's project.clj had 70 lines

vlad_poh19:02:14

@john checking it out now. Thanks

Ian Fernandez19:02:30

how can I compare keys from a list of maps if the entire list of maps, the maps has the same keys?

Ian Fernandez19:02:01

reduce-kv maybe?

dpsutton19:02:42

you want to assert that the set of keys from each map in a list are the same right?

noisesmith19:02:53

what kind of comparison? clojure.data/diff is great for comparing any two maps with the same keys

Ian Fernandez19:02:21

I'm testing if every map in a list of maps has the same keys of a comparison map

noisesmith19:02:24

(apply = (map #(select-keys % [:my :keys]) inputs))

noisesmith19:02:32

same keys or same contents?

noisesmith19:02:42

`(apply = (map keys inputs))`

dpsutton19:02:51

(apply = (map (comp set keys) [{:a 1 :b 2} {:a 4 :b 5}]))

noisesmith19:02:08

oh - good call about using set before comparing

dpsutton19:02:30

often saying what you want (as a property) makes it easy to see what to do

dpsutton19:02:06

> you want to assert that the set of keys from each map in a list are the same right? (map (comp set keys)) falls out right there. and then you assert that everything in a list is the same

Ian Fernandez19:02:47

yeah it worked

Ian Fernandez19:02:11

I'm new to tests, trying to make some tests