Fork me on GitHub
#planck
<
2016-06-24
>
johanatan00:06:46

hmm, i kinda of like the idea of combining them

johanatan00:06:13

it's less plumbing

johanatan00:06:27

and seems like it would be pretty intuitively understood by most

johanatan00:06:29

can a function support more than one spec? we could have an alias for sh with a spec supporting :cb and let the existing sh barf if you provide :cb with the default spec

johanatan00:06:05

i.e., the alias could put sh into the cb-accepting mode; i.e., select the :cb-accepting spec rather than the regular one

johanatan00:06:15

this way i wouldn't have to duplicate the CLJS -> JS -> C plumbing but consumers would still see two different functions

johanatan00:06:01

oh, even if that isn't supported, there's an easy workaround:

johanatan00:06:03

1 - rename current sh to sh-private and mark private so consumers can't touch it. 2 - relax spec on sh-private to accept :cb. propagate :cb all the way through to C if provided. 3 - create new sh that calls sh-private appropriately with original spec disallowing :cb. 4 - create new sh-async that calls sh-private appropriate and has spec accepting :cb.

mfikes00:06:25

@johanatan: sure, give a combined API a try and see how it feels. We can see if a more complicated spec works out. From a spec perspective it seems that the :fn part would let us express the relation that the return is nil if :cb is present. Return values no longer appear to be checked anyway.

johanatan00:06:25

@mfikes: what do you think of the above steps 1-4? seems like best of both worlds to me [shared plumbing but different external functions/specs]

johanatan00:06:24

and if you ever wanted to go with combined, it would be as easy as removing sh and sh-async and renaming sh-private to sh

mfikes00:06:37

Sounds fine

mfikes00:06:43

@johanatan: It may make sense to somehow make the new API fail at runtime indicating that it is not supported if called under 1.x

mfikes01:06:43

@johanatan: I'm curious if the value of js/PLANCK_VERSION can be used to wrap the (defn sh-async ... ) so it only exists in Planck 2.0

mfikes01:06:13

(when-not (string/starts-with js/PLANCK_VERSION "1.")
 (defn sh-async ,,, ))

mfikes01:06:14

Perhaps that may not actually work in the end

johanatan01:06:25

Hmm, that should work

johanatan01:06:40

defn operates at the global level regardless of where it appears no?

mfikes02:06:51

@johanatan: Yes. It is worth trying. I’m wondering if the initialization sequence has js/PLANCK_VERSION setup early enough.

johanatan02:06:52

@mfikes: any idea how to test if something is a function in CLJS? clojure.test/function? doesn't exist

johanatan02:06:17

and the output from this isn't very useful without a string compare hack:

cljs.user=> (type (fn [] 9))
#object[Function "function Function() {
    [native code]
}"]

mfikes02:06:32

fn? or ifn?

johanatan03:06:32

What requires did you need for (s/exercise ...) to work?

johanatan03:06:41

I got the clojure.spec :as s one

johanatan03:06:11

but it's complaining about clojure.test.check.generators now:

cljs.user=> (s/exercise :planck.shell/sh-args)
Var clojure.test.check.generators/simple-type-printable does not exist, clojure.test.check.generators never required
	cljs.core/-deref [cljs.core/IDeref] (cljs/spec/impl/gen.cljs:15:3)

mfikes03:06:52

You need a copy of test.check with TCHECK-105 applied. There is a copy of that JAR in this gist: https://gist.github.com/mfikes/4b41b7c406c57228489b5edfb6ffe6a7

mfikes03:06:32

It will be normal for it to fail when encountering the need to generate values conforming to #(instance? File %).

slipset13:06:46

@mfikes: I’ve pushed some new suff to the socket-pr (as you might have seen)

slipset13:06:07

As far as I can see, I can now implement a web-server using this stuff.

slipset13:06:08

If you have time to take it for a spin, I’d be very happy.

mfikes13:06:35

Yes. Perhaps I can give it a shot this weekend.

slipset13:06:44

No stress 🙂

johanatan19:06:54

@mfikes: do you happen to know how to unpack a function from a JSObjectRef?

johanatan19:06:24

i.e., if i pass in a CLJS function from the CS side, how do I unpack it from the JSObjectRef on the C side

mfikes19:06:49

@johanatan: Interestingly, it looks like that whole question was side-stepped with the implementation of setTimeout

mfikes19:06:51

@johanatan: If there is no clear way to do it from C, then perhaps you can write some ClojureScript to retain the callback somewhere, and then when the results are available, execute a “script” that says “take this value and apply the callback to it"

mfikes19:06:39

(Funky stuff like that was done for function values in Planck’s eval implementation. See the bottom of http://blog.fikesfarm.com/posts/2016-01-22-clojurescript-eval.html )

johanatan19:06:15

link broken?

mfikes19:06:25

There are probably many ways to skin that cat. Just pointing out that if it is difficult to deal with functions as values, they can be bijectively mapped to numbers so that you deal with numbers on the C side.

johanatan19:06:23

Can you point to an example of the "execute a "script" that says "take this value and apply the callback to it"" part of this?

johanatan19:06:15

I think the best way to handle it will be to assign a unique ID to each cb on the way in, stash it in a datastructure on the CLJS side, pass the ID into C and then let C callback to the CLJS with that ID and the return value (it's the last leg of that which I need help with).

mfikes19:06:56

I think they share some conceptual similarity

johanatan19:06:13

ahh, i see. so you mean generate a literal JS script as string and execute it 🙂

mfikes19:06:22

Yeah.. that second snippt

mfikes19:06:02

Who knows if all of that is really just a hack. But it has some germ of an idea on how to cobble together something.

mfikes19:06:14

It is probably cleaner with most of it on on the ClojureScript side, and the C just calling something on that side with the function ID and the results.

johanatan19:06:16

this should work. one risk i have here though: is the JSContext a relatively global concept? i.e., would it change between successive calls to (sh ...) ?

mfikes19:06:51

Hmm… I think it is the one and only JSContext that Planck runs.

johanatan19:06:54

for the async part, i'm going to have to put a 'wait' on a bkg thread. but when that completes, i still only have the original JSContext (the parent thread/process having returned already)

johanatan19:06:58

ya, that's what i figured

johanatan19:06:03

so this should work

mfikes19:06:54

You may not even need to translate from a the function object to and ID and back again. Perhaps you can opaquely pass it into C and then back out again.

johanatan21:06:07

hmm, perhaps but i already went with the ID approach

johanatan21:06:29

this is the translation on the way into C:

(def ^:private sh-async-cbs "sh-async-cbs")
(aset js/window sh-async-cbs (clj->js {}))
(def ^:private cb-idx (atom 0))
(defn assoc-cb [cb]
  (let [idx (swap! cb-idx inc)]
    (aset js/window sh-async-cbs idx cb)
    idx))

johanatan21:06:38

so, C will just generate some js like so: function() { window.sh-async-cbs["{ID}"]( res ); }

johanatan21:06:12

oh, and remove the function assoc to that ID from window.sh-async-cbs afterwards

johanatan21:06:03

I like the opaque idea though. Could look something like: JSEvaluateScript(ctx, "function() { window.do_callback(res); }();", (JSValueRef)opaque_ref, NULL, 0, NULL);

johanatan21:06:18

where do_callback calls its this with res

johanatan21:06:41

better yet: pack both the func to call and the result into an object and specify it for the 'this'

johanatan21:06:01

and let do_callback pull the func and result from its this

johanatan21:06:42

[would eliminate having to encode the res as a string (and the existing impl already encodes it as a JSObject anyway)]

johanatan21:06:52

@mfikes: do you think that could work?

johanatan21:06:18

there is the complication of not knowing how/when the JSValueRef[] passed into my C function gets cleaned up though.

johanatan21:06:33

passing an int from the main thread to the background thread sidesteps that concern/risk

johanatan21:06:02

[i.e., it's possible that the JSValueRef which the background thread has been passed from the main thread will not be valid when the result comes back]

mfikes21:06:51

@johanatan: I'd try an experiment to see if it works out

johanatan21:06:33

kind of a costly experiment though especially since i'm already down the int road quite a ways

johanatan21:06:57

seems like someone should know when that JSValueRef[] gets cleaned up too

johanatan21:06:38

given C's manual memory management, i'm almost certain that the JSValueRef's are being explicitly freed after the function returns to its caller

johanatan21:06:50

otherwise, when would they ever get freed?

johanatan21:06:19

I think I will just finish this int approach since I'm confident it will work. And then revisit the other approach after

mfikes21:06:55

There was some good coverage of memory management with JavaScriptCore in the WWDC talk about the Objective-C bridge, but I suspect we are off in an area where none of that applies. (Yes integers are hard to leak.)