planck

Planck ClojureScript REPL
johanatan 2016-06-24T00:37:46.000355Z

hmm, i kinda of like the idea of combining them

johanatan 2016-06-24T00:38:13.000356Z

it's less plumbing

johanatan 2016-06-24T00:38:27.000357Z

and seems like it would be pretty intuitively understood by most

johanatan 2016-06-24T00:39:29.000358Z

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

johanatan 2016-06-24T00:40:05.000359Z

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

johanatan 2016-06-24T00:41:15.000360Z

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

johanatan 2016-06-24T00:49:01.000361Z

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

johanatan 2016-06-24T00:50:03.000362Z

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.

mfikes 2016-06-24T00:50:25.000364Z

@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.

johanatan 2016-06-24T00:51:25.000365Z

@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]

johanatan 2016-06-24T00:53:24.000366Z

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

mfikes 2016-06-24T00:55:37.000367Z

Sounds fine

mfikes 2016-06-24T00:57:43.000368Z

@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

mfikes 2016-06-24T01:00:43.000369Z

@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

mfikes 2016-06-24T01:03:13.000370Z

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

mfikes 2016-06-24T01:05:14.000371Z

Perhaps that may not actually work in the end

johanatan 2016-06-24T01:50:25.000372Z

Hmm, that should work

johanatan 2016-06-24T01:50:40.000373Z

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

johanatan 2016-06-24T02:00:13.000374Z

@mfikes: ^

mfikes 2016-06-24T02:01:51.000375Z

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

johanatan 2016-06-24T02:53:52.000376Z

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

johanatan 2016-06-24T02:54:17.000377Z

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]
}"]

mfikes 2016-06-24T02:54:32.000378Z

fn? or ifn?

johanatan 2016-06-24T02:54:44.000379Z

ahh, yep

johanatan 2016-06-24T02:54:47.000380Z

fn? it is

johanatan 2016-06-24T02:54:48.000381Z

thx!

johanatan 2016-06-24T03:18:32.000382Z

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

johanatan 2016-06-24T03:18:41.000383Z

I got the clojure.spec :as s one

johanatan 2016-06-24T03:19:11.000384Z

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)

mfikes 2016-06-24T03:20:52.000385Z

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

mfikes 2016-06-24T03:24:32.000386Z

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

slipset 2016-06-24T13:51:46.000387Z

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

slipset 2016-06-24T13:52:07.000388Z

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

slipset 2016-06-24T13:53:08.000390Z

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

mfikes 2016-06-24T13:53:35.000391Z

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

slipset 2016-06-24T13:53:44.000392Z

No stress 🙂

johanatan 2016-06-24T19:11:54.000394Z

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

johanatan 2016-06-24T19:12:24.000395Z

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

mfikes 2016-06-24T19:23:49.000396Z

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

mfikes 2016-06-24T19:28:51.000397Z

@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"

mfikes 2016-06-24T19:29:39.000398Z

(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 )

johanatan 2016-06-24T19:33:15.000399Z

link broken?

mfikes 2016-06-24T19:37:02.000401Z

@johanatan: Fixed

mfikes 2016-06-24T19:38:25.000402Z

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.

johanatan 2016-06-24T19:43:23.000403Z

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?

johanatan 2016-06-24T19:44:15.000404Z

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).

johanatan 2016-06-24T19:45:22.000405Z

@mfikes: ^

mfikes 2016-06-24T19:47:56.000412Z

I think they share some conceptual similarity

johanatan 2016-06-24T19:49:13.000413Z

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

mfikes 2016-06-24T19:49:22.000414Z

Yeah.. that second snippt

mfikes 2016-06-24T19:50:02.000415Z

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.

mfikes 2016-06-24T19:51:14.000416Z

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.

johanatan 2016-06-24T19:51:16.000417Z

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 ...) ?

mfikes 2016-06-24T19:51:51.000418Z

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

johanatan 2016-06-24T19:51:54.000419Z

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)

johanatan 2016-06-24T19:51:58.000420Z

ya, that's what i figured

johanatan 2016-06-24T19:52:03.000421Z

so this should work

mfikes 2016-06-24T19:52:54.000422Z

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.

johanatan 2016-06-24T21:06:07.000423Z

hmm, perhaps but i already went with the ID approach

johanatan 2016-06-24T21:06:11.000424Z

🙂

johanatan 2016-06-24T21:06:29.000425Z

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))

johanatan 2016-06-24T21:07:38.000426Z

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

johanatan 2016-06-24T21:08:12.000427Z

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

mfikes 2016-06-24T21:08:24.000428Z

Cool

johanatan 2016-06-24T21:12:03.000429Z

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

johanatan 2016-06-24T21:12:18.000430Z

where do_callback calls its this with res

johanatan 2016-06-24T21:13:41.000433Z

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

johanatan 2016-06-24T21:14:01.000434Z

and let do_callback pull the func and result from its this

johanatan 2016-06-24T21:14:42.000435Z

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

johanatan 2016-06-24T21:15:52.000437Z

@mfikes: do you think that could work?

johanatan 2016-06-24T21:18:18.000439Z

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

johanatan 2016-06-24T21:18:33.000440Z

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

johanatan 2016-06-24T21:19:02.000441Z

[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]

mfikes 2016-06-24T21:20:51.000443Z

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

johanatan 2016-06-24T21:21:33.000444Z

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

johanatan 2016-06-24T21:21:57.000445Z

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

johanatan 2016-06-24T21:22:38.000446Z

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

johanatan 2016-06-24T21:22:50.000447Z

otherwise, when would they ever get freed?

johanatan 2016-06-24T21:26:19.000448Z

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

mfikes 2016-06-24T21:26:55.000449Z

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.)