Fork me on GitHub
#clojurescript
<
2017-04-05
>
john01:04:12

so it looks like some kind of binding conveyance could be hackable with something like https://github.com/binaryage/cljs-zones

john01:04:38

but that would only work for (zones/bindings [...

john01:04:41

I get the feeling that agents without binding conveyance could still be useful to users. You'd just have to be more careful with what you pass to the agent.

john01:04:45

Another interesting thing about building on shared atoms and agents, rather than static calls and core async, is that we have the makings of a ref datatype. We could then implement STM coordination on top of a pool of web workers operating on the "shared" data structures. This would abstract webworks and message passing away under the atom/agent/stm concurrency abstraction. You set up your agents to bang on your refs. Order the coordination of the results in a (dosync ... and you've just eliminated a whole class of concurrency problems: both threading and message passing.

john01:04:59

Clojure kinda "solved" concurrent programming on the jvm. Java has good primitives, but it's still easy to shoot yourself in the foot. Clojure made that safe. Web workers are unfortunately not ideal primitives for concurrency and it's still easy to shoot yourself in the foot. So I think an agent/stm stack for cljs would help webworker concurrency in both safety and usability.

john01:04:58

Well, message passing makes certain foot shooting harder...

john01:04:57

And since cljs already uses MVCC data structures, getting stm to work on top should be workable.

thheller01:04:50

@john you are ignoring one fact when comparing with the JVM and STM. you do not have shared memory, so there is no point in making it transactional.

john01:04:59

right, there's a class of coordination that is not necessary when memory is only accessed in a single thread

john01:04:34

But once you start coordinating state between multiple agents, some problems come back

john01:04:06

the state is still protected by a single thread that can read and write to it, but from the perspective of other workers, they still need coordination

thheller01:04:37

not sure what you mean

john01:04:28

well, from the perspective of a thread, it's own state is safe, because only it can update its state while it is looking at it.

john01:04:45

So, from that perspective, you don't need a coordination mechanism like STM

john01:04:09

But from the perspective of other workers, the state you hold looks volatile

noisesmith01:04:48

@thheller I don't know if transactions apply to this, but you might eg. want to guarantee that a worker and a main process see the same changes in the same order

noisesmith01:04:04

some coordination is needed there, and a transaction seems a sane abstraction for it

noisesmith01:04:05

that is, you might want a coordinated and uniform view of a shared set of facts, where no two processes see different orderings of state

noisesmith01:04:17

eg. among a group of peers, exactly one should send a message to server X, and you might want them to decide amongst themselves which one does it

noisesmith01:04:35

(contrived example, but I often have tasks like that between vms)

john01:04:45

the canonical STM example is moving money from one account to another. You never want there to be a state where either account has no money or double. Similarly, you may have a situation where you never want two separate pieces of information, owned by different web workers, to ever have a state inconsistent with the state of other pieces of information owned by potentially other web workers

thheller02:04:49

no shared memory: everything must be serialized back and forth (might not be possible)

john02:04:10

right, so the worker itself provides a degree of protection

thheller02:04:13

no blocking: you cannot WAIT for something, you can only react to it

john02:04:28

another process won't interrupt its access to its storage if it is accessing it

john02:04:07

I don't think we need blocking

thheller02:04:39

no but you want coordination

noisesmith02:04:51

a good comparison for this is zookeeper I think - it's a distributed store with guaranteed ordering of events

thheller02:04:54

you mentioned a pool of web workers

thheller02:04:06

they can never act in parallel

thheller02:04:26

as each needs to wait for the other to finish if working on the same data

john02:04:48

if the "same data" exists in another web worker?

noisesmith02:04:54

if every step touches the same data, yes, but that's almost never strictly needed

thheller02:04:16

but it doesn't exist there

noisesmith02:04:28

(maybe I'm just imagining how I would use this at this point - the coordinated data is a very small subset of the data I use, though it is vital)

noisesmith02:04:43

similarly with zookeeper - it's not meant to be a db

thheller02:04:09

@noisesmith zookeeper doesn't ever execute any code for you, so its a totally different thing

john02:04:42

well, some kind of distributed computing thing could be implemented on web workers

noisesmith02:04:48

@thheller but I use zookeeper to decide which code to execute

john02:04:00

storm / onyx etc

noisesmith02:04:26

@thheller my idea here is that a group of workers could have ordered and coordinated state (and therefore differentiate confirmed state and state mid transaction)

thheller02:04:49

@john yes "distributed computing" ... exactly that ... not agents though

john02:04:20

@thheller my point is that once we start introducing the need to coordinate the states of other webworkers, isn't STM a possible solution?

john02:04:42

agents in clojure execute send on a 'thread-pool'

john02:04:14

The webworker pool I implemented was able to run for jobs in parallel just fine

thheller02:04:20

> In computer science, software transactional memory (STM) is a concurrency control mechanism analogous to database transactions for controlling access to shared memory in concurrent computing.

thheller02:04:56

you do not have shared memory. stop talking about STM 😛

john02:04:03

From the perspective of Worker A, the memory in B and C is "shared"

thheller02:04:12

no it is not

noisesmith02:04:16

@john no that can't work

noisesmith02:04:26

you have neither the power, nor the penalties, of that situation

john02:04:32

How does A ensure that B and C do not end up in an inconsistent state, relative to one another?

noisesmith02:04:55

inter process coordination, which can also use a transaction concept, but is the exact opposite of shared memory

noisesmith02:04:00

it's distributed state

john02:04:53

If A wants to take 10 dollars from B and put it into C, how can it do that without either B or C not having either 0 or 20 dollars?

john02:04:06

You can lock both B and C at the same time.

john02:04:17

from the perspective of C, it is a lock on B and C

john02:04:23

STM automates that lock

john02:04:27

so we don't have to do it

noisesmith02:04:28

via a transactional system

noisesmith02:04:46

but STM is a specific kind of mechanic for a transactional system, that assumes shared memory access

john02:04:08

I know. But in a distributed system, memory is shared

noisesmith02:04:19

no, it emphatically is not

thheller02:04:19

I'm too tired for this, 4am over here. I going to bed. gn8

john02:04:53

in a distributed system of web workers, their single threadedness is only guaranteeing local consistency, similar to an atom in clj

noisesmith02:04:35

I don't see the atom comparison, at all

noisesmith02:04:40

for example, where are the retries?

john02:04:54

retries?

noisesmith02:04:15

the point of atoms is that you can change them without locking, and you retry if concurrent changes occur

noisesmith02:04:21

that's literally why we use them

john02:04:28

in clojure

noisesmith02:04:51

what I'm saying is that I don't see what having a single thread has in common with using an atom

john02:04:22

Well, in clojure, like you said, you get that concurrent change protection

noisesmith02:04:40

no, no protection - just the ability to retry

noisesmith02:04:52

it's a thing that doesn't exist when you only have one thread

noisesmith02:04:07

you can still have read-write skew if you use deref

john02:04:11

in a web worker system, worker A and worker B can both access an atom in worker C and, because C is single threaded, the atom is also safe from concurrent access. In that way they are similar.

john02:04:20

That's all I was saying

john02:04:46

there's no retry by default

john02:04:56

one could build a retry system... which is what I did

noisesmith02:04:58

but they can't access that atom - they can get a message from c, but that memory they can't touch

john02:04:04

all requests get queued up

john02:04:09

right, but again, it's a question of: How can you get A to take 10 dollars from B and give it to C, without B or C having 0 or 20 dollars?

noisesmith02:04:15

the distinction I am looking to preserve here is the one between the semantics you want, and the implementation that makes it work; STM is an implementation that relies on shared memory access, but the more general concept of transactions doesn't require this

john02:04:22

how does single threadedness help that?

noisesmith02:04:39

you want a transaction, STM is an implementation of transactions that is impossible on this platform, so you need another implementation

noisesmith02:04:01

one that involves coordination and ordering, rather than shared state

john02:04:16

I'm pretty sure you'd still call that an STM

noisesmith02:04:41

if the memory isn't shared, it's not STM

noisesmith02:04:54

if that's our only disagreement, and you can't accept that, we can stop now

noisesmith02:04:10

but if you go read about STM - it will agree with what I just said

john02:04:18

STM is software transactional memory. the alternative is hardware transactional memory, AFAIAA

john02:04:01

Okay, well yes then, I'm talking about the DTM

john02:04:07

the Distributed Transactional Memory

john02:04:53

and I'm talking about using an implementation similar to CLJ's STM, which monitors locks on state and retries occasionally

john02:04:08

But the locks are on distributed objects

noisesmith02:04:24

STM is an algorithm that is only possible when memory is shared http://groups.csail.mit.edu/tds/papers/Shavit/ShavitTouitou-podc95.pdf

One can use STM to provide a general highly concurrent
method for translating sequential object implementations
into non-blocking ones based on the caching approach of
[6, 26]. The approach is straightforward: use transactional
memory to to implement any collection of changes to a
shared object, performing them as an atomic k-word Compare&Swap
transaction (see Figure 2) on the desired locations.

noisesmith02:04:35

OK - you mention locking, STM doesn't use locking!

noisesmith02:04:11

that's the entire point of it - and a distributed version of the algo would hopefully also not require locks...

john02:04:26

it's a compare and swap

noisesmith02:04:38

right, which is not a lock, and doesn't use a lock

noisesmith02:04:45

anyway, it's probably not helpful to get caught up on the semantics - neither of us think we are talking about something that happens in shared memory

noisesmith02:04:34

but my point is that you need a coordination algorithm, and the stm algorithm assumes shared memory in order to operate - what you need is a coordination algorithm

john02:04:41

right, the point is the bank account example and the mechanism to coordinate the consistency of states between two remote states.

noisesmith02:04:27

so with STM the approach would be to attempt the operation and then commit if there was no concurrent modification

noisesmith02:04:43

the problem in the distributed case is that there is no check to see if there was concurrent modification

noisesmith02:04:57

(well, you can get that, but you need to build a lot of other stuff first before you get that)

john02:04:02

you can check that

noisesmith02:04:11

it's no longer simple

john02:04:33

A can check to see if B or C changed before and after the transaction

john02:04:59

the transaction only exists relative to A

john02:04:20

Because, of course, relative to B and C, their memories aren't "shared"

noisesmith02:04:47

what happens if a and b make changes simultaneously that overlap?

noisesmith02:04:56

you no longer have ordering

john02:04:07

But to ensure B and C are consistent, A can just keep retrying the alter until it has consistency

noisesmith02:04:11

with STM you have ordering and first one wins

noisesmith02:04:21

consistency with what?

noisesmith02:04:45

the point is that STM can assume simple things like "this changed or it didn't" that are no longer simple in a distributed case

john02:04:45

with the validation of the refs and ordering in the (dosync

john02:04:11

we can monitor what changed and what didn't in the distributed case

noisesmith02:04:38

you need a proper consensus agorithm, and trying to translate somthing that can use immediate and undeniable ordering and access will lead to weird failures and excess complexity

john02:04:03

I'm not sure what you mean

noisesmith02:04:31

with shared memory, whether a at point 0 = a at point 1 is a simple question

noisesmith02:04:14

with a single computer, whether a happened before or after b is simple

noisesmith02:04:49

once you have distribution, these become trickier problems. They have solutions, but the nature of the problems mean that STM can't just be translated directly

john02:04:12

I'm handling all the distribution in the library

noisesmith02:04:35

I do have a caveat here - this theory comes from the domain of networked systems where there are a lot less guarantees, it's slightly less complex in a typical web worker scenario

john02:04:55

right, I'm not building a mesh network

john02:04:17

each worker has either a parent worker or the UI is its parent

john02:04:31

each "shared atom" is managed by a parent

john02:04:53

sub workers access the parents shared atom

noisesmith02:04:01

OK, right - but I still think you'll save some trouble by implementing transactions, and not trying to implement the precise STM model

john02:04:03

the parent manages locks on the atom

noisesmith02:04:27

you keep mentioning locks - neither STM nor atoms use locks fyi

john02:04:54

Isn't everything, at a low enough level, using locks of some sort?

noisesmith02:04:09

no, this is the entire point

noisesmith02:04:19

locking uses completely different algorithms

john02:04:29

My understanding is that STM allows you to never have to use locks

noisesmith02:04:34

but STM and atoms can do things faster, because of the lack of locks

noisesmith02:04:56

it's not just that - STM can be implemented without locks

john02:04:16

I think CLJ uses an atomic that leverages a locking compare and swap. I could be wrong though.

john02:04:28

and CLJS doesn't really do anything.

john02:04:39

swap is pretty much just a set!

noisesmith02:04:44

compare and swap isn't locking iirc... I'll have to look this up

noisesmith02:04:22

@john control-f on this page doesn't find any locks for me, it's all using compare-and-set ops https://github.com/clojure/clojure/blob/clojure-1.9.0-alpha14/src/jvm/clojure/lang/Atom.java

john02:04:14

Yeah, I think the AtomicReference underneath is doing all the locking

noisesmith02:04:30

there's no locking in the AtomicReference API though... but OK

noisesmith02:04:01

anyway, if you can change a value and know another thread did not touch it in between, you don't need a lock

john02:04:19

well, you can implement an atomic compare and swap with locks, right?

john02:04:44

right, so B knows nobody else changed B's swap, before and after

john02:04:54

and C knows nobody else changed C's atom, before and after

john02:04:23

But A doesn't have those guarantees about B and C, from it's single thread

noisesmith02:04:48

oh wait, right - to do a compare and set on state shared by processes that don't share memory, you need a distributed lock - which makes me think there's some option better than compare and set

john02:04:38

BTW, I'm synchronizing state automatically between parent owner and sub-workers with access to the shared atom, if that helps your mental picture

noisesmith02:04:00

OK - that's already coordinated without STM

noisesmith02:04:06

it's coordinating via an actor

john02:04:23

that one atom is, yes

noisesmith02:04:25

the actor owns the data, and it negotiates changes based on messages

noisesmith02:04:44

the atom is a store owned by the process that is essentially an actor brokering the atom's state

john02:04:10

What if I'm a sub worker with two shared atoms from two different parents?

john02:04:39

now I need to coordinate access. I need to put locks on both

noisesmith02:04:44

then you ask one of the parents to transact with the other on your behalf

john02:04:29

but the heavy CPU bound problem belongs in this worker. It just needs access to the state.

noisesmith02:04:00

you could imitate STM by creating a compare-and-set based on inter-process locks, but if your work is CPU bound I don't think you'd like the result

noisesmith02:04:05

because STM implies retries

john02:04:13

yeah, STM wouldn't be great for a large number of long running tasks. Especially built over a message passing infrastructure.

noisesmith17:04:00

So I had a lot to say about this topic because I've been skunkworking a replacement for my current inter process coordination. What I'm coming to now is keeping a record built by event-sourcing modifications over a kafka channel (which guarantees ordering) - that way every client can subscribe to the same steam and therefore see the same data structure. A structure that needs to be kept relatively minimal, only for things that need coordination. I'm realizing that to get the semantics I want all I actually need is guaranteed ordering of events, each event describing a compare+swap on the coordination data.

noisesmith17:04:39

if you need transactional coordination, and different parts of the data belong to different entities, you either need multi-process data locking, or some multi-hop commit process, where if any untouched server owns any of the committed data, you only commit if that server gives the OK... but even then you would need to build some sort of proof that you don't end up in byzantine feedback behaviors I think

noisesmith17:04:51

or maybe there's something much simpler I'm missing here

john00:04:57

I'm going for the multi-process data locking route atm

john00:04:18

And yeah, it's super hairy trying to manage those locks in a distributed fashion

john00:04:55

my first implementation is going to be extremely chatty.

john00:04:03

Submit lock request to lock owner. Owner sends lock request confirmation. Owner sends all subscribers lock request. Once all subscribers confirm lock request to owner, owner sends lock request success confirmation to original requester. Now original requestor can send mutation request. Owner broadcasts new state to all subscribers. All subscribers report back confirmation. Owner sends mutation complete confirmation to original requester. Original requester sends unlock request. Owner broadcasts unlock request to all subscribers. Owner sends original sender unlock request success confirmation.

john00:04:32

Then there's all the cases where confirmations come back negative, which all need to be handled gracefully.

john00:04:07

Once those semantics are working, I'll look into optimizing by cutting back on requests. Batching lock and mutate requests together. Leaving "soft locks" on the state, so that the last requester to have a lock doesn't have to re-lock to send a mutation request. etc. As long as the correct semantics are preserved

noisesmith00:04:31

given that none of the threads are interruptable, couldn't you just send a compare and swap with the old data and the new data in the request, and then the recipient could accept it if the current data is still the old?

noisesmith00:04:45

still equal that is

john00:04:54

If the owner receives a request, where the request has the old data and the new data, if the requesters old data matches the owners current data, then we know it's atomic?

noisesmith00:04:31

then we know it can be confirmed

noisesmith00:04:56

right, and we can't get bad states since we can't get our thread interrupted mid action

john00:04:51

and if the old data doesn't match?

noisesmith00:04:14

then you tell the caller the transaction failed

noisesmith00:04:29

the tricky part is if there was data in the transaction belonging to multiple owners

noisesmith00:04:38

then you need the locks and such again

noisesmith00:04:53

I'm off for dinner - I'll probably be around later

john00:04:28

cool, thanks for chatting. This is very helpful, bouncing these ideas around.

armed04:04:43

Does anybody know how to find and delete unused imports in large clojurescript codebase?

danielgrosse10:04:47

Is there a repo where the new :npm-deps argument is used? @spinningtopsofdoom

danielgrosse10:04:09

I just don't get it, how it could be used

mitchelkuijpers10:04:05

@danielgrosse You add it to your compiler options

mitchelkuijpers10:04:16

But you have to run clojurescript from master

mitchelkuijpers10:04:41

It wil download your npm depenciens for you automatically when you run the cljs build

torbjornvatn11:04:33

will this be compatible with lein-cljsbuild btw?

danielgrosse11:04:57

Until now I don't had it get running with lein

tjtolton11:04:07

Anyone know of a good "html for clojurescript programmers" guide?

tjtolton11:04:31

or even "html for programmers"

tjtolton11:04:16

as opposed to the normal html modules, which is are a mix of "html for html veterans" and "html for bored students with a graphic arts minor"

tjtolton11:04:28

I basically want a guide on what is cheap to render and what is expensive, what are the "gotchas", and what is the general strategy for laying out a document or component to be programmatically manipulated.

mitchelkuijpers11:04:35

@torbjornvatn Yes if you have the correct clojurescript version

mitchelkuijpers11:04:05

@danielgrosse You need to run from the master branch of the clojurescript repository to test it out.

danielgrosse11:04:05

I want to import the net module, but got the error

events.js:141
      throw er; // Unhandled 'error' event
      ^

Error: ENOENT: no such file or directory, open 'net'
    at Error (native)

danielgrosse12:04:35

How do I use the master branch of clojurescript? With leiningen?

danielgrosse13:04:30

Okay. The master branch solved the missing net dependency. But now I have another error. The library I import depends on some other libs. One of them wshas devdependencies which are required in catch() {} try() blocks. Now the compiler throws an error, that this dependency isn't available.

spinningtopsofdoom14:04:09

NP always glad to help 🙂

mitchelkuijpers13:04:29

@danielgrosse are you compiling for the browser or for nodejs?

danielgrosse13:04:04

For the browser, but it seems to be a conflict with the library or my npm install.

mitchelkuijpers13:04:26

Which library are your trying to use?

thheller14:04:45

@spinningtopsofdoom in your clojurewest talk you talked about Closure Modules and how they are not supported in :none, this might be interesting to you then https://github.com/thheller/shadow-devtools/wiki/ClojureScript-for-the-browser

spinningtopsofdoom17:04:51

@thheller For the shadow-build modules for :none do they do something similar to what :main does for standard ClojureScript builds?

thheller17:04:15

sort of yes

thheller18:04:19

but I also have a "compact" mode, which just concatenates all files together into the module file

thheller18:04:40

I don't recommend that mode but it makes the ModuleManager stuff work

spinningtopsofdoom18:04:45

I thought the ModuleManager only needed a uri to load and a namespace with setLoaded called with that modules name to work.

spinningtopsofdoom18:04:40

If the modules under :none load in all namespaces they contain it should 🍀 just work.

thheller18:04:20

no the module file just contains a bunch of goog.require which want to emit script tags

thheller18:04:30

but they can't do that after the document has finished loading

thheller18:04:23

FWIW I have the entire ModuleManager working .. fully automated but it sucks

danielgrosse14:04:52

I created an issue for the problem i discovered while compiling an node module. http://dev.clojure.org/jira/browse/CLJS-1999

thheller14:04:19

@danielgrosse looks like a Closure problem, looking at autobahn I can see why it would have issues

danielgrosse20:04:26

what could be the cause of this?

joelkuiper14:04:44

There’s a jquery library that I’d like to use in ClojureScript (http://jvectormap.com/) for which I did not find a compelling alternative

joelkuiper14:04:04

is there an up to date guide on how to include such a dependency in a cljs + boot project?

martinklepsch14:04:17

@joelkuiper This guide is pretty solid and if you read & follow it carefully you should have a cljsjs-style jar in ~20-30min https://github.com/cljsjs/packages/wiki/Creating-Packages

anmonteiro15:04:50

@danielgrosse that issue is fixed in Closure Compiler master

anmonteiro15:04:56

Nothing to do with CLJS

danielgrosse20:04:42

Thanks for the information.

timgilbert15:04:45

I'm going a little nuts trying to integrate an ES6 library with a react dependency to play nicely with clojurescript. The basic javascript build doesn't produce a single .js file that I can run an externs generator on, and my attempts to produce one with webpack winds up giving me a big file that throws errors when I import it

timgilbert15:04:05

I see closure compiler claims to be able to just import ES6 natively now, should I try just throwing the ES6 source at closure rather than trying to transpile it to ES5 first? Anyone got general advice for this sort of thing?

timgilbert15:04:49

FWIW, the library I'm trying to import is here: https://github.com/toxicFork/react-three-renderer

joelkuiper15:04:16

hmm so the minified js of this looks like

!function($){var apiParams={set:{colors:1,values:1,backgroundColor:1,scaleColors:1,normalizeFunction:1,focus:1},get:{selectedRegions:1,selectedMarkers:1,mapObject:1,regionName:1}};$.fn.vectorMap=function(options){var …

joelkuiper15:04:41

would it be possible to generate externs for this?

joelkuiper15:04:15

basically, it’s a classical jquery plugin that I’d like to include

deadghost15:04:28

anyone know if it's possible to run multiple handlers on success/failure with https://github.com/Day8/re-frame-http-fx?

timgilbert15:04:30

@deadghost: what I've usually done for things like that is to have the :on-success handler itself fire one or more new events via :dispatch or :dispatch-n. You might want to check in the #re-frame channel too.

joelkuiper15:04:05

@timgilbert hmm, doesn’t seem to do anything

deadghost15:04:13

@timgilbert thanks I'll check those out

joelkuiper15:04:55

would it be really bad if I just add them as scripts to my page 😛 ?

timgilbert15:04:18

@joelkuiper, yeah, not sure, haven't tried it with jQuery stuff before. There's also this one as an alternative: http://www.dotnetwise.com/code/externs/

timgilbert15:04:46

Just adding it to your page is IMHO a reasonable approach versus wrestling with google compiler, although you will lose the dead code elimination and other js optimizations

martinklepsch15:04:25

foreign-libs are never dead code eliminated so you can’t lose that. You can use the script tag. What you lose is the dependency handling and bundling.

joelkuiper15:04:19

@martinklepsch it’s the first time I try to do this

joelkuiper15:04:28

so any help would be welcome!

martinklepsch16:04:39

@joelkuiper hm. generating externs is not so easy.

joelkuiper16:04:59

I figured, it seems so simple, I want an heat map between country codes 😛

joelkuiper16:04:07

but down the rabbit hole we go!

martinklepsch16:04:29

@joelkuiper guess your best bet might be trying the new extern inference stuff or writing the extern file manually along the way

martinklepsch16:04:51

remember that you only need externs for the stuff you actually use so they don’t need to be exhaustive

joelkuiper16:04:52

or just add those files to my script header 😛

martinklepsch16:04:22

@joelkuiper I forgot to mention earlier that you need externs either way if you want to advanced compile stuff

joelkuiper16:04:01

I guess I can get away with just the minified js for now

joelkuiper16:04:05

adds to page load, but meh

martinklepsch16:04:34

@joelkuiper of your entire build?

joelkuiper16:04:46

no just that plugin

martinklepsch16:04:13

you can’t advanced compile your cljs if you access properties from non-externed JS objects

joelkuiper16:04:29

I forgot about that

joelkuiper16:04:39

I guess I can just try to write my own GIS lib that does this 😛

martinklepsch16:04:41

quick and dirty solution: pass :foreign-libs compiler option with {:file “path/to.js” :file-min “path/to/minified.js” :externs “path/to/externs.js”} and write externs as you go.

martinklepsch16:04:49

I assume you don’t use much more than the vectorMap function. As long as you use string keys for properties besides that you’re on the safe side (strings aren’t touched by Closure)

martinklepsch16:04:37

jQuery.fn.vectorMap = function(opts) {} 
could be a start for your externs file

joelkuiper16:04:16

@borkdude mentioned that cljs-oops might also be an option, since we’re already using that

joelkuiper16:04:23

seems like a good start, thanks!

martinklepsch16:04:49

@joelkuiper yeah, I guess you could do something like (def vector-map (goog.object/get js/jQuery.fn “vectorMap”)) and use that function (as far as I understand thats basically what cljs-oops does)

martinklepsch16:04:23

depending on the number of jVectorMap specific properties your accessing that might be feasible… or not. But same problem with externs so you’ll have to bite the bullet somewhere.

martinklepsch16:04:13

Also just to clarify again: > I guess I can get away with just the minified js for now foreign-libs are never part of the advanced compilation pass. They are basically prepended to the build in dependency-order after your Closure compatible code has been optimized. That’s it. No processing done.

hit02318:04:23

Any tutorial series that I can follow to create a simple game in cljs? Want to learn the process of development in Clojure and use figwheel. 🙂

hit02318:04:57

Any game like flappy bird that was used to demonstrate figwheel in the introductory video.

anmonteiro18:04:20

the flappy bird demo is open source and it’s ClojureScript AFAIK

hit02318:04:22

Yes. Will take a look! 🙂

robert-stuttaford20:04:30

so i’m still on the hunt for a good browser testing story for full stack Clojure. ideally i’d like to be able to target http://browserstack.com’s Automate (hosted Selenium grid), so i’m wondering if perhaps a node.js + phantomjs with ClojureScript type of thing which runs on AWS lambda is feasible

robert-stuttaford20:04:08

our team is 100% Clojure/Script, so i don’t want to use another language

robert-stuttaford20:04:22

clj-webdriver is, unfortunately, abandoned

souenzzo21:04:22

Hey, how to get path/query string's from goog.history.Event. (or how to routing with path/query params?)

mattly23:04:46

is it possible to extend a protocol over a js prototype?

mattly23:04:22

there's a third-party library I'd like to wrap a series of functions around to provide uniform behavior with an equivalent JVM library

mattly23:04:36

and I'm looking into if I'll be better served by a protocol or a series of multimethods

mattly23:04:06

to answer my own question: yes