Fork me on GitHub
#clojurescript
<
2017-05-17
>
john01:05:36

How do you extend the IFn protocol to string? I tried (extend-protocol IFn string (-invoke [this] (.toUpperCase this)))) but then ("hi") returns #object[TypeError TypeError: "hi".call is not a function] instead of "HI"

anmonteiro01:05:00

@john I think in the primitive case you need to extend it to js/String

anmonteiro01:05:17

(this only applies to IFn)

john01:05:57

which is bad practice, right? The compiler sends warnings.

anmonteiro01:05:01

in Lumo:

cljs.user=> (extend-protocol IFn
       #_=>  js/String
       #_=>  (-invoke
       #_=>   ([this]
       #_=>    (.toUpperCase this))))
            ⬆
WARNING: Extending an existing JavaScript type - use a different symbol name instead of js/String e.g string at line 1
#object[Function "function (){
var this$ = this;
return this$.toUpperCase();
}"]
cljs.user=> ("hi")
"HI"

anmonteiro01:05:27

right but I don’t think you can get around it for the IFn case

anmonteiro01:05:38

I could be wrong

john01:05:49

That's what it seems like

anmonteiro01:05:06

also note you need to wrap the arity in an extra set of parens

john01:05:31

Is there a way to "catch" a warning, so alarm bells in figwheel and elsewhere don't trigger?

john01:05:29

Okay, thanks @anmonteiro. I'm going to forego this route. Don't want to break anything.

cmal03:05:54

Hi, how can I send a POST request using cljs-ajax library and specify its Content-Type to application/x-www-form-urlencoded? Thanks!

cmal03:05:53

@bostonaholic Thank you. What should I use in the :format field?

bostonaholic03:05:01

since it says :format sets the Content-Type header, then just send :format "application/x-www-form-urlencoded"

cmal04:05:17

@bostonaholic Thank you! Need I add a :write field and give it a function in :format? The doc says that :format should be a map in which there are two keys :write and :content-type, like this: https://github.com/JulianBirch/cljs-ajax/blob/29e5c57c916dac73007eeb117351f61e30c420c1/src/ajax/core.cljc#L260

Niclas13:05:20

Does anybody here have experience with lazy loading node modules for performance?

djebbz14:05:57

@looveh server-side node.js ? require("my-module") is sync by design, I think it's impossible to lazy-load stuff, unless they implemented System.loader JS spec under node, which looks unlikely to me.

giovaferra14:05:54

Hi everyone! I was writing asyncronus tests using the cljs.test macro's deftest, is, testing async ecc. I have had the need to binding a dynamic var to a different value and I have used the "binding" macro properly. Unfortunately if I place the binding before the async call, there is no binding and if I place it in the async block it is permanently rebounded. This problem is reported in cljs documentation of binding (last two examples): https://clojuredocs.org/clojure.core/binding. How can I work around this problem? I tried to use "with-redefs" but the situation is the same changing...

dnolen14:05:32

@giovaferra you will need to manually bind and restore probably via a test fixture

john14:05:51

@dnolen Great talk at the GOTO!

john14:05:07

Working on one of those 8 hour bugs myself atm lol

giovaferra14:05:59

@dnolen thanks! I will try that way 🙂 I haven't looked at the code of "cljs.test/async" yet, but I was wondering why there is this problem with binding, I mean, why it shouldn't work in an asynchronous context? :thinking_face:

jimmy14:05:33

@dnolen nice, I have something good to watch later today 👍 I enjoy most of your talks. just in case another want to watch as well: https://www.youtube.com/watch?v=lzXHMy4ewtM

john15:05:44

cgrand is braving the storm though: https://github.com/cgrand/dynvars

cgrand15:05:18

@john very much in flux

john15:05:36

@cgrand what's the prognosis, doc?

john15:05:19

Seem feasible?

cgrand15:05:29

feasible yes, what’s still in flux is the impl and the API as bound-fn is imo suboptimal in an async world. For three reasons: two minor (perf and verbosity) and one major (`bound-fn` captures a flat snapshot of vars while in CPS you’d like to capture the whole bindings stack)

dnolen15:05:11

@giovaferra bindings are established in a sync scope and they’ll be gone by the time the async code runs - this isn’t specific to ClojureScript btw

john15:05:56

@cgrand Gotcha. Very interesting problem.

richiardiandrea15:05:31

There is a solution, which is darwin's zones, although it would probably need some perf test. It seems to be working fine but cgrand found out that the set walks the entire object and that could be a perf issue

cgrand15:05:10

darwin’s zones are an interesting implementation but the API is not solved

john15:05:17

aye, @giovaferra you could try zone's with the test/async https://github.com/binaryage/cljs-zones

cgrand15:05:31

you still need to sprinkle bound-fn all over the place

john15:05:40

So I'm sending a message to a worker. It's failing silently on the postMessage. I have an object that manages a pool of workers. If I define that object in another namespace, it works...

john15:05:01

The problem is right in front of my face and I can't see it! 😂

giovaferra15:05:44

Perfect! Thank you guys! Your answers have been very usefull! I'm going to read all the documentations in order to understand the solution to this problem 😉

metametadata16:05:14

@giovaferra as an alternative to with-redefs, to manually change var values you can also directly use set!: https://cljs.github.io/api/cljs.core/setBANG

john16:05:56

Welp, worked around that hairy bug. 😄 Loading the object lazily fixed it.

anmonteiro17:05:56

ClojureScript now has 7000 stars on GitHub https://github.com/clojure/clojurescript

puzzler21:05:37

I'm a bit confused about how to deal with properties of a foreign library object in advanced compilation mode. I'm trying to use phaser from http://cljsjs.io https://github.com/cljsjs/packages/blob/master/phaser/resources/cljsjs/phaser/common/phaser.ext.js but there don't seem to be any entries for properties, and I'm getting errors in advanced compilation mode, for example. the make property of the game object, as documented here: http://phaser.io/docs/2.6.1/Phaser.Game.html#make

puzzler21:05:51

So, for example, if game is my object, this line doesn't work (.make.sprite game ...). make is a property, and sprite is a method of the object stored at make.

puzzler21:05:01

So my first question is to figure out whether I'm misunderstanding externs/properties. Beyond that, I want to better understand how to more quickly debug or avoid externs using the new modes available in clojurescript.

dvingo21:05:36

instead of using cljsjs you can also use the library itself as the externs file

dvingo21:05:41

so you can install phaser from npm and use :foreign-libs to its path in the node_modules directory

dvingo21:05:01

you'll get a lot of warnings from the closure compiler, but it should leave your properties in tact after advanced compilation

dvingo21:05:09

another option is to forgo externs altogether and use string based property accessors

dvingo21:05:39

this library makes that much nicer

dvingo21:05:52

i'm starting to think that's the way to go. dealing with externs is awful

puzzler21:05:55

Is the new extern inference feature relevant to the kind of issue I'm having?

dvingo21:05:33

perhaps, but it's not magic, you'll most likely still have to make edits to the externs file

darwin21:05:33

@puzzler you just walked into ClojureScript fire swamps: https://www.youtube.com/watch?v=tpXfASdPteI

dvingo21:05:00

re-reading your message, you might just need to use the .- instead of .

dvingo21:05:41

.- is used for property access

puzzler21:05:30

@danvingo I believe you only use .- when the final thing in the series of dots is a property. Here it is a method, and the intermediate step is a property.

dvingo21:05:49

gotcha, i think this may be the incantation: (.sprite (.-make game) ...)

dvingo21:05:37

this will fail to bind this though, and just call sprite as a function

dvingo22:05:46

actually, i think it will call it as a method

dvingo22:05:02

verified output here:

dvingo22:05:41

(let [game #js {:make #js {:sprite (fn [] (js/console.log "hi"))}}]
      (.sprite (.-make game) "arg1"))

dvingo22:05:52

that spits out

dvingo22:05:55

game_25.make.sprite("arg1");

puzzler22:05:00

Has anyone had success with the pseudo-names compilation option? I just tried it, and it didn't appear to change the error in any way.

puzzler22:05:37

@danvingo, hmm, when I try it in my sample, it is still munging the .make call.

puzzler22:05:04

Og.Fb.sprite(a,b,"on")

dvingo22:05:28

you mentioned the property wasn't in the externs?

puzzler22:05:44

I don't see it anywhere in the externs (file linked above).

puzzler22:05:04

What is the proper way to add an extern for a property?

puzzler22:05:18

On the clojurescript reference page for externs, it says nothing about properties.

john22:05:21

Hey folks. Not ready to ANN this on the list yet, as this is a super experimental preview. But here's my new CLJS concurrency library I'm calling Tau: https://gitlab.com/johnmn3/tau

dnolen22:05:42

@puzzler just make your own externs file and define it

john22:05:43

Once it reaches "alpha" status, I'll move it over to github for more visibility.

dnolen22:05:49

@puzzler I’m somewhat confused as to what you are doing, it seems like you need to make some kind of object and pass it to Phaser?

john22:05:15

Admittedly, the project is pretty dirty atm. Pushing it out now because I need advice on a number of pieces.

puzzler22:05:49

@dnolen In phaser, the first thing you do is create a game object. That part works. The game object has properties on it that are handles to a lot of different systems. For example, the make property leads to a object factory that has constructor methods for many different kinds of things. One of the methods in this factory is sprite which creates new sprites.

darwin22:05:01

@john I remember telling you it can’t be done, and you did it anyway, good job! 🙂

dnolen22:05:20

@puzzler right so I understand, you need to provide something to Phaser that satisfies some interface

dnolen22:05:33

but the problem is this value is going to be exported to some foreign library outside of Closure compilation

dnolen22:05:41

so you need externs for that interface you are satisfying

john22:05:42

@darwin I was thinking about putting that somewhere in the rationale: "Because, they said it couldn't be done." 😉

john22:05:06

Still might be a bad idea though lol

puzzler22:05:05

The game object is successfully created, and satisfies the interface because it was created by Phaser. The problem is that all the property names are munged. I'm not sure what the syntax would be to add this to an extern file. The documentation says Foo.bar; but I'm not sure what the Foo would be here.

dnolen22:05:19

just make something up

puzzler22:05:42

So as long as blah.make; appears somewhere, then .-make will never get munged?

dnolen22:05:10

or make a JS constructor helper and write an extern for that

dnolen22:05:28

(defn PhaserIFace []) … yada yada

dnolen22:05:31

and write externs for it

dnolen22:05:01

@puzzler yes it should work, but in case it doesn’t, the other suggestion will definitely work

dnolen22:05:30

the real problem here is not the object is getting munged by the way

dnolen22:05:54

it’s that you’re trying to use .-make in your own code

dnolen22:05:06

if you were just blindly passing it along to Phaser everything would be OK

dnolen22:05:01

@puzzler also (.-make ^js/PhaserIface x ...) should work if you want to go the externs inference route

dnolen22:05:52

@puzzler so then the problem is that the existing Phaser.Game externs are wrong

puzzler22:05:23

I don't see any property externs in the file. The phaser externs on http://cljsjs.io was built with jmmk/javascript-externs-generator tool, according to the README. Is there a better way to get all the properties?

dnolen22:05:32

you don’t need property externs

dnolen22:05:07

Phaser.Game is a JS type, you just need correct externs for it … period

dnolen22:05:30

if they are wrong then you can provide an additional externs file with additions

dnolen22:05:48

Phaser.Game.prototype.make = function() {}; ... etc.

puzzler22:05:48

Is the trick of making a library its own extern file more likely to work than the tool used for the http://cljsjs.io file?

dnolen22:05:19

using the library as an externs is only going to work if the top level if sensible - which it usually isn’t

dnolen22:05:12

@puzzler in this case I think you can probably get away with externs inference to avoid writing your own file

puzzler22:05:18

My first choice would be to figure out how to generate a more robust externs file automatically (i.e., find a better generator tool) since the http://cljsjs.io one seems incomplete. But I can try to get up and running with extern inference. I'm not sure I'm on the latest clojurescript/figwheel, so I'll have to update those to give it a whirl. Thanks for the tips.

dnolen22:05:28

@puzzler the challenge with auto-generating externs is static analysis, JS libraries often dynamically create properties so this is an uphill battle

dnolen22:05:56

not saying that issue applies to Phaser, but that’s why no such tool exists

darwin22:05:26

generating robust externs is a $1M task, you would make whole npm ecosystem google-closure-compatible over night

puzzler22:05:31

How much analysis is clojurescript doing during compilation to determine whether to munge? Does an extern that includes, e.g., a scale method on any object prevent .scale from ever munging on any object, or does clojurescript try to do some analysis to figure out whether this is a scale that can be safely munged or not?

john22:05:00

@puzzler Closure compiler does the analysis and munging. ClojureScript only flags certain functions with ^:export to prevent munging.

darwin22:05:26

I just got a million dollar idea, given a code which has tests passing in dev mode, you could write a loop compiling your sources in :advanced mode with :pseudo-names true, and running your tests - if they fail, you parse the js error message, identify the failed pseudo name and auto-generate an extern for it, rinse and repeat, until it starts passing the tests 😉

darwin22:05:01

basically automating current human work…

puzzler22:05:24

Here's what I'm getting at: it sounds like the Closure compiler doesn't really care what "type" the method or property name is on. So why can't the externs file simply be a list of method or property names not to munge. All the externs files I've seen have a whole bunch of hierarchy to them, and I don't see why that is necessary if it is being ignored.

john22:05:06

@puzzler and no, declared externed properties must be associated with a particular object to prevent munging, afaik

darwin22:05:52

@john I believe you can also specify externs on Object which are effective on any js object

john22:05:19

ah, that was an assumption on my part. hmm

john22:05:11

well, the more you do that, the less likely you'll produce efficient code

thheller22:05:53

that is incorrect

thheller22:05:27

since most of clojusescript is untyped anyways it actually doesn't matter where properties in externs go

puzzler22:05:37

So when I do a call that corresponds to object.make.sprite() How does the closure compiler know that the make property is a type that has a corresponding extern for the sprite method? Nothing in the externs indicates the type of a given property, just that it exists, right? So how would the compiler be able to figure out not to munge sprite?

john22:05:35

I recall not being able to get externs to work without having the correct hierarchy, but it's been a while.

thheller22:05:38

I spent quite a bit of time recently working out the detailed effects on externs

thheller22:05:46

just need to finish writing it up

puzzler22:05:01

If the hierarchy doesn't matter, it seems like extern files could be way simpler.

thheller22:05:23

@puzzler basically just define Foreign.prototype.anyProperyYouWantToKeep; in your externs

thheller22:05:51

with /** @constructor */ function Foreign() {};

thheller22:05:06

Or Object doesn't matter

thheller22:05:51

While that isn't ideal if you ever want to type check your code with closure

thheller22:05:56

it works well enough until you do

thheller23:05:47

didn't release that yet but shadow-cljs has a --check option that will tell you about any undefined properties

thheller23:05:01

so much stuff to write ...

john23:05:30

@darwin how about this idea... instead of non-deterministic munging, deterministically munge names into base64 identifiers. Most words would probably shrink down to 2 or 3 chars. Then it's reversible 🙂

noisesmith23:05:16

wait, how would base64 make anything smaller?

john23:05:17

not sure how js deals with base64 chars in names though

noisesmith23:05:35

oh, sorry, I see what you mean

darwin23:05:49

@john not sure what problem you are trying to solve 🙂

john23:05:50

Take a string, treat it like a number, convert it to base64

noisesmith23:05:21

so you aren’t talking about base64 encoding, which always makes strings longer

darwin23:05:26

humans prefer seeing pseudo names to base64, and machines do not care

john23:05:56

the point is, most of the char space is wasted in human language

darwin23:05:05

does not matter in my proposal, that detection of js-errors is just dev-time thing, when it fixes the externs issues, the final :advanced build is done as usual

john23:05:08

I'm condensing uuid's in my lib to base64, just to make them a bit friendlier

darwin23:05:33

or maybe I got puzzled by this whole thing, maybe you responded to something else? 🙂

john23:05:47

Yeah, totally tangential idea

darwin23:05:46

anyways externs wasted many of my brain cycles again, I’m out for today, bye

john23:05:50

nah, base64 probably wouldn't save much... but if you built a "most common programming words" dictionary for the compression, you could make it reversible and maybe save some space.