Fork me on GitHub
#clojurescript
<
2017-03-24
>
john00:03:11

@darwin understood. I'll bring the with-meta up a level and try that.

john00:03:22

I could send...

darwin00:03:47

be careful with that if the web worker can potentially be compiled against different cljs version

john00:03:17

(with-meta ... worked, btw)

john00:03:02

what do you mean, against different versions?

darwin00:03:02

my point is even more subtle, assuming you compile both web worker code and main frame code under :advanced, but different sets of sources, the names of javascript functions will be renamed to different names

darwin00:03:14

so even cljs.core library function names won’t match

john00:03:32

understood

john00:03:58

Yeah, we'll see if sending sources is sufficient. I could be that most of the functions people would want to send over will often have dependencies on functions that don't have source, nor exist on the worker agent side...

darwin00:03:52

why do you need to send the sources? why don’t you let them compile their own js file for the web worker

darwin00:03:08

and then just define mechanism how to dispatch those functions from main frame

john00:03:17

(send-off compiled-fn param1 param2)

darwin00:03:33

simplest one would be to use ^:export and call them by string name

john00:03:46

(how do I apply compiled-fn to param1 and param2 on the worker side, if compiled fn is js and param1 and 2 are cljs datastructures?

darwin00:03:24

param1 and param2 must be serialized to JSON, there is no way around it

darwin00:03:56

so I guess you need some kind of pluggable marshalling in place

john00:03:09

yeah, I have the marshalling in place. Using butler

darwin00:03:13

so that user can specify how his data is serialized and de-serialized

john00:03:31

but the resulting js has no .call function, that I can tell

darwin00:03:59

what is "resulting js"?

john00:03:03

It's probably something simple, but I couldn't figure it out. js* is a single parameter function

darwin00:03:21

what would be wrong with: (dispatch-on-worker “some.fn.name” param1 param2 …)

john00:03:26

just sending the + function will send the js of that function, rather than the name or the var

darwin00:03:40

and your code would do the marshalling and call js/some.fn.name in the worker’s context

john00:03:07

I'd like it to be even smoother: (dispatch-on-worker + 1 2)

john00:03:22

what goes over the "wire" to the worker is the js of +

darwin00:03:48

this is not going to work. good luck 🙂

darwin00:03:06

in general case I mean 🙂

john00:03:39

right if I could apply the js that '+ returns to things, I could just send the js

john00:03:06

But regarding sending sources, I have that working now.

john00:03:26

Won't know how useful it is until I implement more of the nuts and bolts

darwin00:03:01

I understand that you can eval arbitrary js snippets in worker’s context. But that does not solve other code that the snippet might depend on.

john00:03:49

so your saying the munged names of core fns referenced in one js snippet may not match the names in another worker? or something different? What if both the main thread and the worker are leveraging the same munged code base?

john00:03:08

or is there something more sinister afoot?

darwin00:03:20

let’s assume :none for now, and both have the same cljs compiler version

darwin00:03:32

then names will match and API will match as well

darwin00:03:02

you can write a macro which will expand (dispatch-on-worker + 1 2) to something like (dispatch-on-worker 'cljs.core/+ 1 2)

darwin00:03:30

on worker side you would interpret that 'cljs.core/+ and invoke proper core function with de-serialized params

darwin00:03:42

serialize the result and send it back to main frame

john00:03:56

that's essentially what I'm doing

john00:03:08

by shuttling code

john00:03:16

cljs code

darwin00:03:32

this is not sending code, this is just sending an identifier what to call on other side

darwin00:03:00

kinda poor mans RPC

john00:03:27

Well, + would resolve to 'cljs.core/+ on the other side. But if there are any vars not defed on the other side, the sent code obviously won't run. So if you want the other side to have that function, you should send it the "(def code..." fn and then it's available there. right?

darwin00:03:55

and who would compile the cljs code for the worker?

darwin00:03:05

the worker speaks js not cljs

john00:03:14

the worker. It has a repl on that side, using replumb

darwin00:03:37

aha, ok, then that’s a different story 🙂

darwin00:03:47

so you have full self-hosted cljs compiler in the worker?

john00:03:52

yeah, it's a "self-hosted" hack sort of story 🙂

john00:03:13

so there's a large body of gotchas for the end result

john00:03:30

but you end up with something that feels like a clojure agent

john00:03:40

because agents are supposed to be asynchronous anyway

darwin00:03:57

I’m not familiar with clojure agents, I’m sorry

darwin00:03:20

this sounds a bit heavy-weight to me, but depends on your use case

john00:03:38

very heavy weight, as of right now

darwin00:03:28

also I assume you would want to have multiple agents (workers), so each would have its own self-hosted compiler with own compiler/REPL state

john00:03:11

yup. averaging around 4 in a pool. Spawning others as necessary.

darwin00:03:46

interesting project, but I’m concerned about its practicality 🙂

john00:03:58

mostly not practical in the browser 🙂

john00:03:44

The book Clojure Programming, by Chas Emerick, Brian Carper and Christophe Grand, has a pretty good example of using agents to implement a web crawler. So if you want a web crawler in your browser 🙂

john00:03:53

Thanks! I'm having too much fun on this, for real.

darwin00:03:33

anecdotally, last time when I needed to move some more serious clojurescript work off to a worker was reading/parsing jobs in my experimental ClojureScript editor[1], I ended up sending diffs of the data structures serialised as JSON, it was fun to write it, but ended up not very practical [1] https://github.com/darwin/plastic

john00:03:19

hah I'm a huge fan of structural editors. I def think clojure and some structural editor will be the perfect combo one day.

john00:03:43

I'll check out plastic

darwin00:03:53

not usable

darwin00:03:17

I’m still tempted to restart the project, but next day I realise I would kill few years of my life this way 😉

john00:03:48

What else are you going to kill a few years with 😉

darwin00:03:04

some other experiments 😜

darwin00:03:57

just recently I discovered this: http://skew-lang.org, the author did amazing job, I think skew could be great language for low-level engine and cljs for high-level business-logic part (e.g. this example https://github.com/evanw/sky)

darwin00:03:42

unfortunately he is busy working on his startup and nobody really took over

john00:03:48

So I have a lot of this agent stuff implemented =>(def a (agent 1)) #'re-pl.repl/a => a #object [Agent 1] => (add-watch a :logger (fn [key atom old-state new-state] (println "old:" old-state) (println "new:" new-state))) #object [Agent 1] => (send-off a inc) #object [Agent 1] old: 1 new: 2 => @a 2

darwin00:03:40

this is hard to read, why don’t you use gist or that “+” button in slack client? it syntax-highligts clojure

john01:03:23

Though that inc won't actually work without a version that has source. I could make send-off a macro and have it quote the fn parameter if it's not a MetaFn... which would assume the symbol is bound somewhere on the worker side. hmm

seantempesta01:03:45

Now that spec is around, how are people doing testing for clojurescript apps? I like the idea of generative testing, but I don’t know how to begin for testing a UI.

tagore02:03:12

Generative testing (whatever that means in practice) is very interesting, but I don't think it's a replacement for manually written tests.

tagore02:03:16

Testing UIs is inherently difficult, IMHO.

tagore02:03:34

I'm inclined to think that the best approach for relatively small projects is to look at them, when it comes to testing what they look like.

tagore02:03:06

But that's a very good reason to do as much as you can to decouple what things look like from what determines what they look like.

tagore02:03:08

You can test the latter.

bostonaholic02:03:35

^^ this is why libraries like React/Om are great. Pure functions of data -> dom. Test the data. If there is branching in your UI code, that’s where bugs will live. So minimize that as much as possible.

tagore03:03:55

I agree. I've been doing a lot of snapshot testing lately actually and...

tagore03:03:35

Honestly, it's the sort of thing you would have a had a hard time convincing me of a while back, but..

tagore03:03:23

I think I am a convert now.

tagore03:03:01

I've stopped thinking of manually written tests as some sort of proof of correctness. I've started thinking of them more as a tripwire to tell me when I've really broken things.

bostonaholic03:03:05

different types of tests can be used for different reasons

bostonaholic03:03:24

e.g. driving a design vs. a regression suite

tagore03:03:50

Well, I've never been much of a TDD person.

bostonaholic03:03:32

it’s a good practice to get good at, it comes in handy every once in a while

bostonaholic03:03:44

but I don’t TDD every line of code that I write

tagore03:03:46

I write tests first only in the absence of a repl, tbh.

bostonaholic03:03:57

but when I’m struggling for a design, maybe

tagore03:03:42

For instance I wrote a computational geometry library in C a couple of years ago, and...

tagore03:03:17

It was written test first because... how else was i going to exercise the code?

tagore03:03:29

But that didn't require a great deal of design.

tagore03:03:42

Just functions that had to be correct.

tagore03:03:53

The tests still missed some cases 😉.

john03:03:14

I imagine combining spec with something like https://github.com/tmarble/webica will have interesting results for UI testing

john03:03:10

though something like this would probably run the tests much faster: https://saucelabs.com/blog/headless-browser-testing-with-casperjs

john03:03:33

but I'm not sure if you'd get to see the results with a headless solution

john03:03:23

like, visually

tagore03:03:37

Yeah- the thing I guess I am getting at is that with a decent repl, and editor support for it...

tagore03:03:12

TDD becomes less a thing, IMHO.

tagore03:03:34

Tests remain important.

tagore03:03:47

I'm doing lots of node and JS for work these days and... the lack of an integrated Repl is making me nuts. It's enough to drive me to TDD 😉.

tagore03:03:39

I am inclined to think that TDD, if purely practiced, leads to a lot of hill-climbing.

tagore03:03:08

Then again, so do other ways f writing code, so...

tagore03:03:32

In fact, if I had to levy one real criticism against the code-bases I've written and had to work with, that were not obviously bad...

tagore03:03:41

It would be that they tend to get stuck at local maximums.

danbunea07:03:01

@tagore We do have quite a lot of automated tests, and these days I am trying to do generative testing (without success). We use reagent, and we have basically 3 types of tests: unit tests for usual functions, and for controllers which basically transform the state atom (a lot of async tests here). We also test the reagent components, in 2 ways: "in tests" which is when we pass some data, and it gets rendered on the screen so then we check whether what was supposed to be rendered is rendered, and "out tests" where we render the component and perform some action trying to see it it invokes the right functions with the right params.

danbunea07:03:19

I think we could use generative testing with the components, basically generating what goes in, just to see if it still renders on screen, even though speed might be an issue here.

danielstockton07:03:53

Can anyone share a good way to load a configuration file into a clojurescript project?

robert-stuttaford07:03:18

runtime or compile time, @danielstockton ? if the former, it’d have to be on the page (as a <script> tag) or via XHR, if the latter, via a macro?

danielstockton07:03:46

Compile time, macro could work yeah.

thheller08:03:29

@danielstockton I recommend sticking to goog-define and :closure-defines to configure CLJS. this way the closure compiler can make intelligent use of the configuration and maybe even remove some code it otherwise could not.

thheller08:03:05

also there is currently no way to tell CLJS to recompile a file if your config file changed

thheller08:03:21

so you have to manually clean everything if you change it

danielstockton08:03:03

I looked at :closure-defines but it looked like they already had to be @defined by closure. goog-define looks like a macro to define my own defines? Do you know of an example of this being used anywhere?

thheller08:03:24

yeah thats it

danielstockton08:03:35

The problem with closure-defines is that I have my project.clj checked into git. This config should be outside of git so that my fellow developers can change some settings for their local environment.

thheller08:03:04

you can execute code in the project.clj

thheller08:03:22

maybe that can read the defines from some other file?

danielstockton08:03:45

You're right, thanks.

dbackeus17:03:44

however lein cljsbuild auto just results in Compiling ClojureScript. - it never completes

dbackeus17:03:58

(after retreiving all the deps)

Rachel Westmacott17:03:10

It is waiting for changes?

dbackeus17:03:23

the only output I get is Compiling ClojureScript.

dbackeus17:03:34

according to tutorial I should get a green complete message

dbackeus17:03:06

nothing happens when changing the files

Rachel Westmacott17:03:30

Hmmm, strange...

dbackeus17:03:07

now I'm not familiar with clojure etc but these dependencies seem old?

:dependencies [[org.clojure/clojure "1.6.0"]
                 [org.clojure/clojurescript "0.0-3149"]
                 [cljs-ajax "0.3.10"]]

dbackeus17:03:38

isn't clojure up to 1.8? and clojurescript "0.0-3149", sounds like pre-alpha?

Rachel Westmacott18:03:12

That's a good question. You can certainly have more recent versions than those.

noisesmith18:03:52

dbackeus there’s a very nice plugin called lein ancient that answers these sorts of questions automatically

noisesmith18:03:17

if you let it, it can even upgrade all your deps automatically

bja18:03:29

Recent versions of lein-ancient probably won't even clobber your project.clj while automatically upgrading

noisesmith18:03:32

and yes, those are all very old deps

dbackeus18:03:48

well that's nice, but first off I'd like to get the build to work at all

dbackeus18:03:53

I updated to the latest versions in the project file

noisesmith18:03:59

bja if my project.clj wasn’t in git any consequences of that are my own fault anyway 😄

dbackeus18:03:42

ok first run was similar to old versions where the last message would be "Retrieving xxx from clojars" and it would be stuck forever

bja18:03:45

I was just commenting that it actually works now. The first few times I tried it, it got confused and made me git checkout project.clj again

dbackeus18:03:51

second run Watching for changes before compiling ClojureScript...

dbackeus18:03:59

let's see if I change a file now...

bja18:03:06

but I feel like I used that a couple jobs ago in like 2012 or 2014

bja18:03:11

err, 2013 maybe

dbackeus18:03:58

I just created the core.cljs in the root of the project but I guess that needs to live under src?

dbackeus18:03:34

I was only interested in Leiningen so I assumed I didn't have to go through the entire beginning of the tutorial

dbackeus18:03:28

yes working after putting it in src/hello_world/core.cljs

dbackeus18:03:29

a bit surprised at the ajax lib adding 110KB to the bundle, I'm assuming it has some more dependencies, is there a command I can see to understand the current deps tree?

noisesmith18:03:36

dbackeus a pedantic point - “core.cljs” is just a convention, and it was arrived at because there isn’t a generic way to know what to call a project’s primary namespace. The one who institutionalized the convention is on record as suggesting that people should use other names if possible.

dbackeus18:03:02

is cljs-ajax the idiomatic library for ajax communication or is there some newer thing around? (just thought it fair to ask since the tutorial seemed to use ancient versions of everything to begin with)

noisesmith18:03:23

cljs-ajax is fine, but using js/AJAX directly is also cromulent

dbackeus18:03:56

cromulent eh 🙂

dbackeus18:03:49

even my Scottish wife was not aware of that word (I'm Swedish myself)

noisesmith18:03:02

oh, sorry, it’s a joke

dbackeus18:03:13

yeah I looked it up

dbackeus18:03:15

no worries 😄

dbackeus18:03:40

thanks for the pointers, out for now

dbackeus18:03:48

thoroughly impressed with this one line though: (GET "")

dbackeus18:03:01

that's the kind of world I want to live in

rauh18:03:52

@dbackeus Many people will also use goog.net.XhrIo directly and avoid pulling in a library. My remote namespace is 2-3 functions that use XhrIo.

john21:03:50

@dbackeus I'd recommend using a more recent tutorial

john21:03:26

and cljs-ajax is popular. as is https://github.com/r0man/cljs-http

john21:03:45

both of those provide both server and client side implementations, making shoveling data back and forth super easy.

john21:03:28

never seen this:

john21:03:59

apparently has interceptors, perhaps ala pedestal