clojure-dev

Issues: https://clojure.atlassian.net/browse/CLJ | Guide: https://insideclojure.org/2015/05/01/contributing-clojure/
2019-07-02T01:57:45.255600Z

Curious whether gfredricks’s idea might work. For literal check for fn, for var use metadata.

2019-07-02T03:21:30.258300Z

Hard to see what having to tag vars improves over the sam macro above, in both cases you have to specify the Sam type.

2019-07-02T03:29:10.259300Z

In theory, it gets vars to play too.

2019-07-02T03:29:30.259800Z

But they were talking about compiler changes, not a macro.

2019-07-02T10:55:26.260100Z

don't have to specify the method in the var tag case

2019-07-02T10:56:18.260600Z

which might be a bigger deal than just less typing -- the interface name might be a lot more memorable than the method name

ghadi 2019-07-02T12:05:48.261500Z

I'd like to set a challenge to do it with neither method nor interface name

ghadi 2019-07-02T12:07:28.262300Z

Thread.currentThread().setUncaughtExceptionHandler((t, ex) -> body...);

ghadi 2019-07-02T12:08:26.262700Z

(.setUncaughtExceptionHandler (Thread/currentThread) __?__)

nathanmarz 2019-07-02T17:26:14.262900Z

has there been any work done on making clojure objects (e.g. functions, anything reified) efficiently serializable between processes which are running the same code?

nathanmarz 2019-07-02T17:26:26.263100Z

seems like this would need deep compiler integration in order to correctly account for closures

ghadi 2019-07-02T17:27:50.263400Z

to what end?

nathanmarz 2019-07-02T17:28:14.263800Z

like create an object in one process and send it to another process on another machine

nathanmarz 2019-07-02T17:28:24.264200Z

that's running the same code

ghadi 2019-07-02T17:28:29.264300Z

like CORBA?

nathanmarz 2019-07-02T17:29:40.264700Z

not very familiar with that

ghadi 2019-07-02T17:29:43.264900Z

or Java serialization?

nathanmarz 2019-07-02T17:30:00.265400Z

specifically, I need to be able to take a function instance and send it to another process

ghadi 2019-07-02T17:30:10.265900Z

send data and eval it

nathanmarz 2019-07-02T17:30:11.266Z

or an instance of an object defined via reify that has some random closure

ghadi 2019-07-02T17:30:22.266500Z

actual data or a sexp

nathanmarz 2019-07-02T17:30:51.267300Z

that seems too inefficient, and how would you send the closure?

ghadi 2019-07-02T17:31:12.267600Z

but why?

2019-07-02T17:31:37.267900Z

@ghadi I’ve faced this a lot of times

2019-07-02T17:31:43.268100Z

seems there are plenty of reasons

2019-07-02T17:31:46.268300Z

distributed processing especially

nathanmarz 2019-07-02T17:31:59.268900Z

yes, my case would be distributed processing

2019-07-02T17:32:00.269Z

it’s not always super trivial to just eval it on the other side either

nathanmarz 2019-07-02T17:32:30.270Z

anyway, just wondering if there was some branch of clojure somewhere tackling this or any related work

2019-07-02T17:32:46.270500Z

1) have to setup the same context around it (maybe not trivial), 2) I’ve hit issue with max method size errors trying to eval across as well - depending on what you need to send.

ghadi 2019-07-02T17:32:47.270600Z

there's some examples of serializing functions in portkey

ghadi 2019-07-02T17:33:01.270800Z

using java serialization IIRC

2019-07-02T17:33:13.271400Z

I remember doing some extensions and improvements over this https://github.com/technomancy/serializable-fn in a private repo in the past

2019-07-02T17:33:27.272Z

but it was based around just eval’ing forms later, but could get a bit more sophisticated with it.

2019-07-02T17:33:51.272500Z

I think the standard Serializable fails for certain closure situations.

2019-07-02T17:34:32.272900Z

(also, I’ve only did work on any of this for function instances, not things liek reify)

2019-07-02T17:34:49.273200Z

oh and lastly - concerning eval

nathanmarz 2019-07-02T17:34:50.273400Z

i'll take a look at portkey

2019-07-02T17:34:54.273600Z

it is quite slow to eval a bunch of times. you want to “batch eval” but in a way that can not break method size limits

nathanmarz 2019-07-02T17:35:09.273800Z

yea

2019-07-02T17:35:17.274300Z

We did some stuff with this on the clara-rules serialization stuff

nathanmarz 2019-07-02T17:35:41.275800Z

the efficient way to do this would be to serialize an id for the class and the values in its closure

2019-07-02T17:35:47.276100Z

It was super slow to eval tons of “fn’s back again”, so we made a big data structure to hold them all in to do a “batch eval and then relink them to their appropriate places after”

cgrand 2019-07-02T17:35:52.276200Z

Nope, kryo

ghadi 2019-07-02T17:35:54.276400Z

data does not have edge cases: maps, sets, lists, scalars

ghadi 2019-07-02T17:36:21.276500Z

👌:skin-tone-4:

2019-07-02T17:36:48.277Z

sure, but there are useful applications …

ghadi 2019-07-02T17:38:34.277700Z

you can send bytecode, you can have a classloader that is network-controlled

ghadi 2019-07-02T17:39:06.278400Z

sending arbitrary things is an arbitrary requirement, and is going to be challenging

2019-07-02T17:41:29.279300Z

Sometimes, the customer really wants a machete, and all you can do is point out the sharp edges and hope they don't return it later with a bandage wrapped around their hand.

2019-07-02T17:42:02.279600Z

> Sometimes, the customer really wants a machete, and all you can do is point out the sharp edges and hope they don’t return it later with a bandage wrapped around their hand. eh, I don’t really consider this constructive here

2019-07-02T17:42:12.280Z

distributing work across processes is common and this comes up

2019-07-02T17:42:29.280400Z

There are several clj libs out there doing things around this. Ones built around Hadoop stuff/Spark etc

2019-07-02T17:42:56.280900Z

But yeah, I’ve specifically targeted fn’s before, not just “any object”. I know there will be limitations

2019-07-02T17:43:30.281700Z

and I’m not even sure I think there is a “good solution” for clj alone. Basically I’ve always came back to the eval approach, but then have to do things like batching forms together etc

2019-07-02T17:43:42.282300Z

if you don’t want it to be extremely slow for larger things

2019-07-02T17:44:07.283Z

networked style classloader etc, seems interesting indeed

2019-07-02T17:44:54.284300Z

which is an interesting question, if you want to send closures around, why not just turn all your machines in to a single shared memory space

1
ghadi 2019-07-02T17:45:04.284600Z

even with the very general problem statement expressed, I still think you shouldn't pass around closures

ghadi 2019-07-02T17:45:31.285200Z

if you want to send code, have a control plane be in charge of worker machines or classloaders from above

ghadi 2019-07-02T17:46:13.285900Z

roll the code forwards or what not, but limit what's passed in between workers to be data

ghadi 2019-07-02T17:46:45.286600Z

unless that's impossible for the SLA, in which case send the code to the data like Datomic Ions do

ghadi 2019-07-02T17:46:50.286800Z

it works, and it's Really Fast

alexmiller 2019-07-02T17:48:54.287Z

I was actually working at Terracotta at this time, helping Paul, before I was a Clojure user :)

😲 1
alexmiller 2019-07-02T17:49:35.287600Z

working on sending function instances is not something we're likely to do in Clojure proper anytime soon

alexmiller 2019-07-02T17:49:51.288Z

working on being able to send rehydratable var refs is something we're working on

alexmiller 2019-07-02T17:50:47.289100Z

vars are actually serializable now (since 1.9? 1.10? can't remember) and when deserialized, they will re-resolve in the target

2019-07-02T17:52:20.289900Z

very cool

alexmiller 2019-07-02T17:52:31.290100Z

what we're looking at is making the reader support that too

alexmiller 2019-07-02T17:53:20.291100Z

so can read a var and have it become a resolved var again

🚀 1
2019-07-02T17:53:56.291300Z

all seems reasonable. I just chimed in based on some past experiences anyways. fortunately for me, I haven’t been fighting with this situation in recent times.

2019-07-02T17:54:02.291500Z

this var addition seems interesting

2019-07-02T17:56:32.293Z

the other place this pops up (thorny serialization issues) is image based development

ghadi 2019-07-02T17:56:37.293100Z

the var serialization stuff is still a reference to the var, not the actual contents

✅ 1
alexmiller 2019-07-02T17:57:04.293400Z

https://clojure.atlassian.net/browse/CLJ-2165

alexmiller 2019-07-02T17:59:54.294400Z

that might have been my first intro to Clojure actually

john 2019-07-02T18:31:27.294700Z

Nice!

john 2019-07-02T18:32:22.295400Z

@nathanmarz I've been working on a similar thing in CLJS [tau.alpha](https://github.com/johnmn3/tau.alpha/tree/master/src/tau/alpha) that I've recently been working on porting to nodejs.

john 2019-07-02T18:34:17.296400Z

And I've been re-working the fn serialization stuff and it works pretty much like shipping around byte code

john 2019-07-02T18:34:40.296800Z

but javascript as bytecode 🙂

john 2019-07-02T18:36:58.298600Z

And in a web worker env or a tightly controlled cluster, I think it makes most sense to use a fully connected mesh so they appear to have a single shared memory space

john 2019-07-02T18:39:59.298900Z

like @hiredman was saying

john 2019-07-02T19:20:04.300800Z

the more recent version for node is aware of locals and grabs them too, if necessary, so the code looks a little more traditional. In the code above, that version uses a more explicit binding conveyance mechanism for the on macro. Anyway, ping me if you have any questions about it. I'll hopefully have a rough nodejs version out soon

nathanmarz 2019-07-02T19:49:40.301600Z

@john cool, my case though is specifically that the code itself is shared between processes

nathanmarz 2019-07-02T19:49:46.301900Z

so no need to send byetcode, since it's already there

nathanmarz 2019-07-02T19:50:15.302500Z

just need to send an id for the class and whatever the fields are, which would be the closure

ghadi 2019-07-02T19:51:52.303700Z

since you're not dynamically generating code, you don't need to do any of this: which ever class that generates the closures should be the target

ghadi 2019-07-02T19:52:08.304300Z

ask instances of that class to generate the closures

ghadi 2019-07-02T19:52:59.305100Z

(aside: this is no different semantically than passing around RPC-style maps with a target "op" key)

john 2019-07-02T20:18:35.307600Z

@nathanmarz that's sort of how I'm doing it though. The reason what I'm doing mostly works is because the same compiled code is on both sides

john 2019-07-02T20:19:07.308500Z

The byte code becomes mostly the serialized calling convention

john 2019-07-02T20:25:02.309900Z

You can just use tagged literals for the IDs and hydrate them with the read function on the other side

nathanmarz 2019-07-02T20:25:47.310300Z

@ghadi how do you ask instance of a function to generate its closure?

nathanmarz 2019-07-02T20:26:47.310700Z

you mean with reflection?

ghadi 2019-07-02T20:27:06.311100Z

no i mean call a Regular Function that returns a closure

ghadi 2019-07-02T20:27:38.311700Z

with normal arguments, return function that closes over arguments

nathanmarz 2019-07-02T20:29:34.313100Z

do you mean to write or generate the functions that need to be serializable in a special way?

ghadi 2019-07-02T20:30:33.313700Z

(defn callme
  [a b c]
  (fn [x]
    ...use a b c))

nathanmarz 2019-07-02T20:32:58.314400Z

not sure what you mean by that example

nathanmarz 2019-07-02T20:33:05.314600Z

the code I need to be able to write would be like this:

nathanmarz 2019-07-02T20:33:09.314800Z

(defn foo [a] (fn [] a))
(def f (foo 1))
(defn bar [a] (reify SomeInterface (someMethod [this] a)))
(def r (bar 2))
...
(send-to-other-process (serialize f))
(send-to-other-process (serialize r))

ghadi 2019-07-02T20:33:37.314900Z

I get the impression that you're focused on mechanism and might need to step back into the problem space

ghadi 2019-07-02T20:34:07.315200Z

if you control code on all nodes, you don't need to send code

ghadi 2019-07-02T20:34:28.315900Z

send data that can recreate the code

nathanmarz 2019-07-02T20:34:29.316Z

yes, that's been established

nathanmarz 2019-07-02T20:34:44.316400Z

the question is how do I take an instance of an arbitrary function and send that data

nathanmarz 2019-07-02T20:35:04.316800Z

the data would be the class and the fields that comprise that instance (the closure)

nathanmarz 2019-07-02T20:35:47.317600Z

or an instance of some object made with reify

ghadi 2019-07-02T20:35:53.317800Z

send data that can recreate the objects, you can use metadata on the objects

nathanmarz 2019-07-02T20:36:54.319100Z

so how would you imagine my code sample working then?

nathanmarz 2019-07-02T20:37:05.319500Z

foo has to annotate its return with metadata as to what is in its closure?

john 2019-07-02T21:33:20.321300Z

If foo and bar can be custom types, you can customize the print writer so that the other side knows to call foo and bar constructors on them on the other side. Assuming you control foo and bar.

nathanmarz 2019-07-02T21:36:18.321700Z

no, that's not the case

nathanmarz 2019-07-02T21:36:27.322Z

this needs to work for any function instance or reify instance

nathanmarz 2019-07-02T21:37:38.323100Z

it sounds like the answer to my question is there has been no experimental work on the clojure compiler for this

john 2019-07-02T21:39:28.323700Z

Shipping code between run times is a generally frowned upon practice. think there hasn't been a lot of interest in it until recently

ghadi 2019-07-02T21:40:05.324200Z

it's one of those things that every generation re-learns is a bad idea

ghadi 2019-07-02T21:40:47.324900Z

where is the #corba channel? 😉

ghadi 2019-07-02T21:41:06.325200Z

must have died in the late 90's

john 2019-07-02T22:37:07.325700Z

Yeah but latency changes

john 2019-07-02T22:37:46.326400Z

At some point we'll have to reevaluate the tradeoffs