Fork me on GitHub
#clojurescript
<
2017-05-09
>
iku00088800:05:16

I posted this on the ClojrueScript Mailing list as well, but has anyone ever ran into this kind of issue? https://groups.google.com/forum/#!topic/clojurescript/34lhl94m1fo

john00:05:09

Does the function itself return null?

iku00088800:05:17

When calling that function yes it does return null (I think)

iku00088800:05:34

((.-getId (.-record js/some)))

iku00088800:05:21

Calling the returned function like above to test

john00:05:20

do you have a repl up at the same time that you're able to call the function from the console? You're sure that js/some.record is the same object?

john00:05:46

as the one you're calling in the repl?

iku00088801:05:24

@john Thanks for the suggestions! I poked around with your suggestions in mind and figured out that it was a js load timing issue, and that there was a specific load handler provided for that site.

iku00088801:05:58

Sorry for the noise and thanks for being so helpful again!

john01:05:15

Nice! No prob

PotatoDepaulo01:05:17

Hello all! I’ve got a reagent app with lein and figwheel set up for development. I’m doing my first deploy and I’m having trouble with lein-cljsbuild. I can’t for the life of me figure out how to get it set up with multiple nested files. Does any one have any docs that I could read towards the right direction?

iku00088801:05:05

Could you clarify 'multiple nested files'?

PotatoDepaulo01:05:44

e.g.

src
└── frontend
│       ├── components
│       │   └── my_file.cljs
│       ├── item
│       │       └── other_file.cljs
|       ├── core.cljs

PotatoDepaulo01:05:31

Does that clarify what I’m trying to do?

john01:05:20

folders and files should somewhat mimic your namespace structure

john01:05:16

also, if your namespace has a dash in it, your file will need to have underscores in their place.

john01:05:46

so, above, you might have (ns frontend.components.my-file ...

john01:05:05

Or was your question about something else?

PotatoDepaulo01:05:37

Yup! I don’t have a problem accessing my namespaces. And the files are compiling with figwheel. My only problem is setting up cljsbuild to get those doubly nested folders and files.

john01:05:24

If figwheel is compiling, your cljsbuild is set up

noisesmith01:05:28

what makes something double nested?

noisesmith01:05:59

I mean, I have file paths that go much deeper than that in my app

PotatoDepaulo01:05:23

@john my namespaces are following the convention you mention. 🙂 that’s why I’m so confused…

PotatoDepaulo01:05:34

Do y’all have an idea about why there would be a problem with the “min” vs the “figwheel” settings?

PotatoDepaulo01:05:45

I’m really groping in the dark on this one…

john01:05:03

but you say figwheel is compiling? Do you have a repl up?

PotatoDepaulo01:05:46

Yea, I can get a figwheel REPL going. Everything compiles fine

PotatoDepaulo01:05:57

My goal is to deploy the minified files to production. It seems like when I run lein cljsbuild once min it’s not compiling properly. I imagine that it’s a noob mistake that I’m making

john01:05:56

How is it not compiling properly? 🙂

PotatoDepaulo01:05:09

Well looks like “could not load :main” is showing up in my console. But :asset-path is set to the correct dir…

john01:05:53

yeah, that's what I was thinking. You have :main set?

john01:05:30

Oh, might not need that in min

PotatoDepaulo01:05:12

thanks for the help, btw

john01:05:00

eh, I've got it in one of mine. If you want to post it up in a gist or pastebin somewhere some folks here will probably take a look.

john01:05:32

And I do not have :asset-path specified in the "min" id

john01:05:21

:advanced will output to a single js file and all assets (if I understand :asset-path correctly) will be included in the single js file

noisesmith01:05:58

does the main function have the ^:export annotation on it?

noisesmith01:05:09

because anything that doesn't will be renamed in a minified build

john01:05:30

yup, that's another common gotcha

noisesmith01:05:34

with that config, as far as I know the only reason it wouldn't find main is if there is no function called main defined in the namespace, or you didn't apply the export metadata

PotatoDepaulo01:05:14

Okay, I’ll try with the ^:export annotation. Thanks for the tip!

noisesmith01:05:33

(defn ^:export main [...] ...)

john01:05:58

Yeah, I don't see any issues

kurt-o-sys07:05:24

I'm using a js library that has functions that return js promises. How to handle js promises in cljs? How to translate something like:

async function writeToFile() {
  const fileContents = 'This is a my content.';
  await FileSystem.writeToFile('my-directory/my-file.txt', fileContents);
  console.log('file is written');
}
into cljs? When I run:
(.then (.writeToFile FS "whatever.txt" "testing") #(.log js/console %))
I get Possible Unhandled Promise Rejection (id: x): TypeError: undefined is not an object (evaluating ...)

kurt-o-sys07:05:56

(and nothing is written to the file 'whatever.txt')

kurt-o-sys07:05:58

my.ns=> (def w (.writeToFile FS "whatever.txt" "testing"))
#'my.ns/w
my.ns=> w
#object[Promise [object Object]]
Also gives above Promise Rejection.

kurt-o-sys07:05:31

my.ns=> (.then w #(.log js/console %))
#object[Promise [object Object]]
tells me I can ignore previous messages (since the Promise Rejection is Handled). However, there is a new Possible Unhandled Promise Rejection.

thheller07:05:33

@kurt-o-sys a promise then takes two functions, you are calling the first one yourself

kurt-o-sys07:05:43

oh... ok 🙂

thheller07:05:52

#(.writeToFile FS "whatever.txt" "testing")

thheller07:05:29

oh wait .. thats the one returning the promise right?

thheller07:05:39

sry read that wrong

thheller07:05:30

no looks correct then

kurt-o-sys07:05:45

my.ns=> #(.writeToFile FS "whatever.txt" "testing")
#object[Function "function () {
return my.ns.FS.writeToFile("whatever.txt","testing");
}"]

kurt-o-sys07:05:33

returns a function.

thheller07:05:13

yeah read that wrong sorry

thheller07:05:57

what is FS?

kurt-o-sys07:05:19

oh, that's 'FileSystem': import FileSystem from 'react-native-filesystem';

kurt-o-sys07:05:36

(or rather: (defonce FS (.-default (js/require "react-native-filesystem"))))

kurt-o-sys07:05:17

that's the module/js lib I'm trying to use 🙂

thheller07:05:55

ah .. so its a lint warning of some kind? I think it tells you that the .then call should take a second argument

thheller07:05:05

(.then promise resolve reject)

kurt-o-sys07:05:13

it has a method static writeToFile (path: string, content: string, storage: string?): Promise

kurt-o-sys07:05:23

ok... let me try that one 🙂

kurt-o-sys07:05:47

right, nice... no warning anymore. (Still doesn't seem to work - it's not writing to a file - but I should be able to handle it from here.) Thx.

rauh07:05:03

@kurt-o-sys The problem isn't that you don't provide a second argument. That's optional for js Promises. The problem is that the promise fails and you didn't handle the rejection.

kurt-o-sys07:05:28

right... get it. so it just fails on writing.

kurt-o-sys07:05:38

Makes sense, since it didn't write 🙂

jimmy08:05:11

Has anyone tried react sketchapp with clojurescript ?

stevechan08:05:38

` WordCloud(document.getElementById(‘my_canvas’), { list: list } ); `

lepistane10:05:26

are there any tutorials or books or resources on how to make cljs frontend with spring(boot) backend? i only made simple clj/cljs app to see what is going i am not expert and i dont understand everything so details are welcome do i start spring on localhost (return json for all services) use get/post on those services?

val_waeselynck12:05:32

@lepistane I guess your frontend communicates with your backend via HTTP (REST) endpoints

val_waeselynck12:05:43

so there's little to do to make them interop

val_waeselynck12:05:32

your frontend won't rely on the specifics of your backend anyway

val_waeselynck12:05:01

and yes essentially use AJAX calls from ClojureScript

val_waeselynck12:05:09

to communicate with your backend

val_waeselynck12:05:48

You can use JSON as the communication format, although Transit (if supported by your backend) would probably make your life easier

jumar13:05:40

@lepistane yeah, I also think there's nothing specific to Spring here. You probably want to create REST endpoints in Spring (btw. any reason to not implement the backend in clojure?) and just call them from cljs - you can use cljs-ajax for this combined with reagent (there are some examples in "Web development with clojure" book).

kai234213:05:46

hi. has anyone experience working with webrtc using clojurescript? i'm looking for the best way to use it. maybe use webrtc-adapter through cljsjs. does that sound reasonable?

lepistane14:05:06

@jumar it's collage project. i got specification they want me to make backend using spring and front is needed but optional so i wanna be the cool kid and learn it 😄 thank you @val_waeselynck i will use spring on backend so i dont know why is transit better?

jimmy14:05:43

@lepistane transit is like grpc for clojure

cpmcdaniel14:05:05

newb question: I have a not-small data file (plain text or edn) that I want to load from my server, what is the best way to do this in cljs? I was initially using a macro to read it into a var, but that’s not very memory efficient if I only need a subset of the data at runtime

cpmcdaniel14:05:53

never mind… just found cljs-ajax

val_waeselynck15:05:04

@lepistane it's hard to encode everything using JSON due to the lack of extensibility - to the point ad hoc encoding logic often leaks into your application code. EDN (the data format on which the clojure syntax is based) solves this issue (https://github.com/edn-format/edn) and Transit applies the same principles but makes their implementation more efficient by leveraging existing, highly-optimized JSON / Messagepack encoders / decoders. (https://github.com/cognitect/transit-format)

val_waeselynck15:05:30

but don't worry with that at first - one unknown at a time!

Björn Ebbinghaus16:05:56

So I forked a project and started hacking. I launched a figwheel session. Nothing unusual. Until:

dev:cljs.user=> #{[1 2] 3}
#object[TypeError TypeError: cljs.core.PersistentHashSet.createAsIfByAssoc is not a function]
nil
dev:cljs.user=> #{1 2 3}
#{1 3 2}
I tried the same in a figwheel session of another project from a while ago. No changes since. Same story. Anyone got some clues?

cpmcdaniel16:05:19

definitely looks like a bug

noisesmith16:05:45

@mroerni have you changed any deps recently? If so have you cleaned up your partial compilation artifacts since changing hte deps?

noisesmith16:05:05

I regularly see nonsense errors like that (about methods or functions not existing) in my figwheel when I change deps

noisesmith16:05:25

especially when doing version changes

Björn Ebbinghaus16:05:53

@noisesmith In the first (the forked) project. I upgraded clojure, cljs, figwheel and cljsbuild to the most recent version. In the second project i didn't changed anything. I deleted all compiled js files and the target folder

dnolen16:05:23

@mroerni fwiw, can’t repro that here

lepistane16:05:09

hi guys I added ajax.core to deps in project that i created new lein figwheel my-cljs i (:requred it in namespace) i type (GET "http:/localhost:8080/api/categories") - (returns json when i type it in browser) i get org.mozilla.javascript.EcmaError: ReferenceError: "XMLHttpRequest" is not defined. (rhino.clj#41)

lepistane16:05:17

why is that?

john17:05:44

I could be wrong, but seeing rhino.clj there tells me that you're using a JVM rhino repl. Did you intend to do that? Usually, people repl into a running browser instance.

john17:05:45

are you running lein figwheel dev to launch your repl?

lepistane17:05:21

i tried that and i also used emacs cider cljs repl (start cljs repl from clj repl and that's the error you see)

Björn Ebbinghaus17:05:41

@dnolen @noisesmith With a new figwheel project (lein new figwheel) this doesn't occur. 😕 I will stop working for today. Maybe its just a 'im too tired and did something dumb' glitch

noisesmith17:05:31

@mroerni a simple thing to try is a fresh clone of your project in a different directory

Björn Ebbinghaus17:05:19

noisesmith: That works with the second, never touched project. The first I have modified. I want to understand what the cause of this is.

noisesmith17:05:32

sure, you modified it, but if you are using version control you can clone it

noisesmith17:05:45

my suspicion is the cause is not in your code

Björn Ebbinghaus17:05:19

Jeah, sure. But what is it then? Looks like I have done something wrong. I just wanted to test some functions. 😕 frustrating

john17:05:58

hey folks, I'm implementing a then (similar to a Promise's then) for my custom ref type. It seems that with other implementations, then returns another Promise. But looking at examples like here: https://funcool.github.io/promesa/latest/#promise-chaining ... there's nothing asynchronous about the functions the results are being forwarded to. Is it expected that functions in a then chain can be either synchronous or asynchronous? And the subsequent thens will always wait for the result of the prior then, whether synchronous or not?

john17:05:15

@lepistane ah, sorry, I don't use emacs. Not sure what could be happening there. Running lein figwheel dev shouldn't give you that error though, I believe.

lepistane17:05:20

@john could you just walk me through that step by step? lein figwheel dev it loads i get localhost:3449 what then? how do i send ajax request? (i added cljs-ajax to project.clj i might be doing somethinig stupid

john17:05:15

@lepistane I'm guessing that lein new figwheel is giving you a reasonable project.clj that already has the necessary compiler options in their correct places. Once you've added cljs-ajax to your project.clj dependencies section, you should be able to require it in the repl.

lepistane17:05:48

(:require [ajax.core :refer [GET]]) ?

john17:05:48

(require 'cljs-ajax.core) or whatever the correct ns is.

john17:05:01

take off the colon

lepistane17:05:07

see? that was stupid of me

john17:05:22

It's what you'd expect, because of the ns command.

john17:05:30

but that's a keyword there

john17:05:44

which is weird, but you get used to it, for ns forms.

john17:05:52

The only time you should expect to see a keyword in the beginning of a list (in the function position) is when you are using the keyword as a function to look up a keyword in a map or in an (ns (:require ... form.

noisesmith17:05:14

or in a case form

john17:05:11

There's probably a few other edge cases too

john17:05:24

but in general

noisesmith17:05:34

case breaks all the generalizations we could otherwise make about usage of parens in clojure

noisesmith17:05:43

I really don’t like it, but too late for that of course

john17:05:11

it's performance is decent, right? 🙂

noisesmith17:05:29

right, I’m just complaining about the syntax, which won’t change

cpmcdaniel17:05:51

well, pretty much every language has it’s warts

cpmcdaniel17:05:06

unfortunate though it may be

john17:05:43

Another question re: callbacks. In addition to providing a then function, @dnolen suggested I also provide a vanilla callback interface, on the function signature itself, and let users use their own callback system, if they desire. I'm wondering where to put the callback in the signature though.

john17:05:52

usually, with ref types, you have some something like (swap! ref-typ some-fn second-thing third-thing)

john17:05:20

or the (send ... counterpart.

john17:05:40

So, where would you squeeze a callback in that signature?

john18:05:56

and how would you go about making that callback optional?

john18:05:28

Seems difficult to provide (swap! a f x y & more) without callbacks, and then another version with callbacks.

john18:05:51

I suppose i could provide a second function for each function. Like an async-swap!

jr18:05:22

why do you need callbacks for a swap?

john18:05:50

@jr I probably shouldn't have used swap! as an example. For these ref-types, all mutations are asynchronous, as opposed to normal CLJS ref-types. But they have a similar functionality, with watches, validators, etc.

john18:05:49

I'll use the term swap-off!, to signify that the swap is taking place on a remote object.

jr18:05:21

what are you trying to do? execute tasks in a web worker and send the result back to the main thread?

jr18:05:57

seems like the agent should be responsible for the callback

jr18:05:42

PromiseAgent(callback) or AsyncAgent(channel) ?

john18:05:48

You're adding the callback to the agent using a separate function from the mutator function?

jr18:05:04

when you make an agent you provide it with a callback

john18:05:22

Would you do that directly prior to the mutation invocation?

jr18:05:53

some-fn mutates the internal state of the agent by providing a new state

jr18:05:10

after that new state has been verified then call the callback

jr18:05:49

(-> (->PromiseAgent {:callback #(js/console.log %)})
      (send! some-fn arg1))

john18:05:40

(-> (->PromiseAgent {:callback #(js/console.log %)})
      (send! some-fn arg1)
      (send! some-other-fn arg1))

john18:05:36

that would fire the callback on both invocations, correct? In JS land, do you usually provide a unique callback (or set of callbacks) for each mutation?

jr18:05:59

what's the point of the callback? to synchronize UI state?

john18:05:19

To do something after the mutation

john18:05:39

since it is asynchronous, the user won't have a reference to the result

jr18:05:02

so create a new agent for each callback?

john18:05:20

That's possible

jr18:05:29

then the agents can work on a pool of workers

john18:05:40

Yup, that's how I've implemented agents. They're all in-memory, not backed by individual workers. They just farm out to a pool. So they can be spawned and trashed easily.

john18:05:35

A goal of mine though is to simulate Clojure's ref-types as closely as possible. Only because people have already learned them, so the less new idioms they have to learn, the easier it will be to pick up.

jr18:05:54

it isn't a ref-type though

jr18:05:08

it's just an async process that produces results

john18:05:42

With the then function, I can just do a (then (send a inc) #(dec %)) and the signature remains the same.

jr18:05:10

it's confusing to call it a ref type that doesn't actually synchronize state

john18:05:19

what do you mean?

john18:05:40

Do atoms synchronize state?

jr18:05:49

yes and then you deref them to view the current state

john18:05:06

How is a remote atom not synchronizing state?

jr18:05:21

it's returning state not synchronizing

john18:05:14

All mutation operations are protected by a compare and swap. And all computation in the remote context is a synchronous JS context. Not sure where the synchronization is not happening.

jr18:05:40

where do you deref the remote atom? the callback just returns the result of the computation yeah?

john18:05:49

State is stored on both the local and remote context. Derefs happen locally. I'm debating returning [:pending cur-val] when a particular operation is pending.

john18:05:00

callbacks are stored and executed on the local context.

john18:05:10

Only one context is the "owning" context.

lepistane18:05:20

@john thank you for your help 1. rhino does return error 2. figwheel returns object 3. regular repl returns json as string too i will continue tnx for helping me setup cljs-ajax

john18:05:22

which executes the mutation operation.

john18:05:49

@lepistane no problem! Glad you figured it out. Enjoy.

john18:05:54

I suppose callbacks could be executed on the remote, owning context. Not sure what the user expectation would be. I figured the local, subscribing context. Used similarly to a one-off add-watch.

jr18:05:53

why not keep atoms local and synchronize the state when the remote returns data?

john18:05:17

That's kinda what happens

jr18:05:26

the callback returns state and the worker doesn't have any internal state

john18:05:54

That's essentially what happens

john18:05:13

workers currently do hold state, but they don't have to

john18:05:32

It's just easier if the worker (which here I'm calling the remote, owning context) maintains the val, so that you don't have to keep sending the value over the wire, if all you want to do is inc

john18:05:25

the resulting value will be sent back over the "wire" but not having to send the cur-val over the wire saves time

john18:05:14

the results are then stored in the local, subscribing context's atom. But the local subscriber cannot mutate this particular atom directly. It can only mutated it via the remote context.

jr18:05:14

aren't those web worker states going to get out of sync without sending the initial state on every mutation?

john18:05:40

All mutations are protected by a compare-and-swap

john18:05:54

Ah, so yes, I am sending the old-val...

john18:05:07

Oh, but I'm going to update that to only send a hash of the old val

john18:05:55

(which is a sweet trick :))

jr18:05:07

sounds hacky

john18:05:25

yeah, well 🙂

jr18:05:41

web workers are good at performing computations off the main thread

jr18:05:54

why not just perform those computations and merge in the results (from the callback on the main thread) to the main data set instead of copying it all over?

jr18:05:39

if all you want to do is inc and you know the path....send the number to be inced

john18:05:31

But you may have some 10 meg image file that you're updating. The function may be simple, but the computation and payload may be large.

jr18:05:00

so you want to store that 10 meg file across N workers?

jr18:05:05

and then synchronize the changes to that?

john18:05:25

The fastest and most efficient way to accomplish that is to leave the image on the worker and issue updates to it. passing only image back to the main thread when all work is done.

jr18:05:41

have you measured that?

john18:05:11

I've done quite a bit of testing, to get an idea of the performance characteristics.

jr18:05:12

what if it is binary and applying the hash is expensive?

john18:05:07

Not across N workers, necessarily. Depends on the method used. Using agents, yeah, you're sending the whole image down the pipe on each turn.

john18:05:44

But with a send-off you could do it all on one worker or a user defined pool

john18:05:04

or with a swap-off you could just bang away on one worker.

john18:05:58

haven't looked at the hashing mechanism, but my understanding is that they can be pretty performant

john19:05:06

much faster than sending 10 megs over the wire.

jr19:05:45

web workers are local right?

john19:05:21

not thread local

jr19:05:51

yeah so the only overhead is serialization of a data type

john19:05:35

interestingly, the message passing interface for webworkers is very similar to websockets. So distributed objects could be a thing with this method, if you can get the same compiled artifacts on both ends.

jr19:05:49

sending 10mb to a web worker is not "over the wire"

john19:05:59

there's a cost

john19:05:26

It's not pass by reference

jr19:05:29

sure but the computation is the expensive bit

john19:05:18

depends on the bit-per-operation, as to what is more expensive. Transport will probably be your bottleneck, unless you have some real work to do.

jr19:05:52

why is transport the bottleneck. wouldn't you just do everything on the main thread if you didn't have a cpu bottleneck?

john19:05:08

Many folks won't have really heavy-duty work to do, they'll just want to off-load as much computation as possible, to keep the ui thread as jank free as possible.

jr19:05:41

off-loading computation that isn't a bottleneck does not make sense to me

john19:05:57

If your UI thread isn't getting crowded up with a ton of little computations, and you don't have any heavy work, you won't want to use this solution. You should do your work on the main thread.

jr19:05:40

there is a cost to adding events to the main thread as well in regards to context switching

john19:05:37

yeah, there is

john19:05:36

I've got an example app with 8 cellular automatons. 4 run on the main UI and 4 run on an agent pool. Certain parameters show how using the main UI thread is more efficient. Others show the agent pool being more efficient. It depends entirely on your work load.

john19:05:09

Larger automatons bring the UI to a crawl. Passing data between workers in a stupid way can bring your workers to a crawl (but at least your UI doesn't freeze). Knowing when to use what will be dependent on some amount of experience.

john19:05:43

So anyway, I'm approaching something presentable. I'm trying to smooth out the callback system now. Next step is to bake in a consistent, global error handling system. Then I think it'll be ready for release.

jr19:05:32

conflating callbacks (pass data) and refs (store data) is why it feels complicated

jr19:05:08

so yeah separate functions might do the trick

john19:05:18

@jr I appreciate you taking the time to think through some of these questions with me. Very helpful! And if anyone has any ideas on how they would green-field a callback system in CLJS (or links to good examples of prior work), please advise!

john22:05:11

speaking of a consistent error system, I suppose I could allow then to have an optional second argument, like (then ... #(handle-success %) #(handle-error %)). Does anyone find that unclojury though? I'll probably also implement a container on the object that stores the last error, ala clojure's agent-error and set-error-handler etc.

Geoffrey Gaillard23:05:56

I have an application built in ClojureScript that weights ~540kb compiled in :advanced and gzipped. For sure it's a big app with Re-Frame, core.async, etc , but I'd like to shink it as much as possible. Is there a way to diagnose what is imported from which library once compiled ? I tried to pretty-print the output .js file and to search for every goog.provide calls but of course name are mangled … I have a "feeling" that some library is taking way more space that it should.

darwin23:05:37

look at the produced file with :pseudo-names true and try to spot anything unexpected

Geoffrey Gaillard23:05:51

I missed this one ! Thank you !