Fork me on GitHub
#clojurescript
<
2021-08-20
>
zendevil.eth11:08:40

I’m copying the following code to cljs (from https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#deploy):

myContract.deploy({
    data: '0x12345...',
    arguments: [123, 'My String']
})
.send({
    from: '0x1234567890123456789012345678901234567891',
    gas: 1500000,
    gasPrice: '30000000000000'
}, function(error, transactionHash){ ... })
and I have:
6            (<p!
  7              (.. contract
  8                  (deploy (clj->js
  9                           {:arguments [(-> db :creation :name)
 10                                        (-> db :creation :name)]
 11                            :data (.-bytecode (js/JSON.parse resp))}))
 12                  (send (clj->js {:from "0xA599D39F794E7676b829BDbB4D5a2f5F8835C5ad"}) (fn [err tx-hash]))))
But when I add the send line and wrap with <p!, I get the following:
ioc_helpers.cljs:50 Uncaught TypeError: Cannot read property 'length' of null
    at Object.toUtf8Bytes (utf8.js:180)
    at StringCoder.encode (string.js:25)
    at eval (array.js:64)
    at Array.forEach (<anonymous>)
    at Object.pack (array.js:58)
    at TupleCoder.encode (tuple.js:37)
    at AbiCoder.encode (abi-coder.js:87)
    at require.encodeParameters (index.js:121)
    at eval (index.js:439)
    at Array.map (<anonymous>
and ideas how to fix this and what’s causing this? It’s sad that the cljs compiler doesn’t tell you what’s wrong at the cljs level

dnolen11:08:39

the stacktrace tells you what's wrong

zendevil.eth11:08:18

I don't know what length refers to

zendevil.eth11:08:26

There's no length in the code

zendevil.eth11:08:56

Specifically in the send line

zendevil.eth11:08:12

Which is where the error originates from apparently

p-himik11:08:18

The error mentions ioc_helpers.cljs:50. What's there?

zendevil.eth12:08:25

(defn run-state-machine-wrapped [state]
  (try
    (run-state-machine state)
    (catch js/Object ex
      (impl/close! ^not-native (aget-object state USER-START-IDX))
      (throw ex))))
throw ex is line 50

p-himik12:08:11

Don't catch js/Object, catch :default. Can't see anything useful here - go through the code that run-state-machine calls. Or just break on caught exceptions in your browser. NodeJS debugger probably has a similar feature, if you're using that.

zendevil.eth12:08:32

I didn’t write run-state-machine-wrapped

zendevil.eth12:08:33

It’s part of the cljs source cljs.core.async.impl.ioc-helpers

p-himik12:08:38

Well, you have to go there and explore then.

tessocog12:08:10

what is the best way to (de/)serialize clojure/script code for transferring it over the wire and persisting it to disk?

p-himik12:08:06

Do you really mean code and not data?

p-himik12:08:31

Shoo!

4
tessocog12:08:29

I do mean clojure and clojurescript code, not just edn

tessocog12:08:02

using pr-str and trying to read it afterwards gives me errors

p-himik12:08:07

If you have access to the code in its original form, then just send the text. If you only have some function or whatnot and want to serialize it, then it's impossible in the general case.

tessocog12:08:40

here is an example:

(cljs.tools.reader.edn/read-string (pr-str {:hello "world" :foo #"\a"}))

tessocog12:08:51

Execution error (ExceptionInfo) at (<cljs repl>:1). Unsupported escape character: \a.

p-himik12:08:36

You're using an EDN reader on something that's not EDN. Clojure code is a superset of EDN.

p-himik12:08:00

In this particular case, EDN doesn't know about RegEx.

tessocog12:08:01

yes, that's why i'm asking

p-himik12:08:03

First, you need to amend your requirements then and make them accurate and precise. Right now your OP says "transfer over the wire and persist to disk". You don't need any read for that.

tessocog12:08:37

if it's not readable, then how could it even be data?

tessocog12:08:58

using pr-str with cljs.tools.reader/read-string works for clojurescript

p-himik12:08:16

It's not readable by the EDN reader. It's readable by the LISP reader. clojure.core/read.

tessocog12:08:17

i was wondering if there is something akin to transit out there

p-himik12:08:51

Code is usually not transferred. And if you need to transfer it, then it is already in its perfect form - just text.

p-himik12:08:16

@U0P7ZBZCK How would an extra layer of encoding improve the situation?

tessocog12:08:20

it would be sensible to transfer code for certain systems such as smart-contract platforms. there you'd have libs for this very specific thing: serialization+deserialization of code

p-himik12:08:44

And as I mentioned already, you can't do it for the general case, only for some subsets of all the cases:

user=> (pr-str (java.util.concurrent.Executors/newFixedThreadPool 1))
"#object[java.util.concurrent.ThreadPoolExecutor 0x7dc51783 \"java.util.concurrent.ThreadPoolExecutor@7dc51783[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]\"]"
You can't read that #object back it. Yes, you can create a parser for this particular class. But not for every class.

tvaughan12:08:48

Character encodings, and protocol encapsulation issues. If there’s a problem with pr-str just skip it completely

tessocog12:08:22

does Datafy protocol in anway address this? i have no experience with it

p-himik12:08:37

> Character encodings Right, but I assume UTF-8 text can be transferred without issues. > protocol encapsulation issues No idea what that means. > If there’s a problem with pr-str just skip it completely Exactly. That's what I meant by transferring code as regular text, if you have access to the original code itself.

tessocog12:08:01

alright, thanks for the clarifications

p-himik12:08:30

Another fun example, among infinite, on why pr-str has very limited usage:

user=> (pr-str #(inc %))
"#object[user$eval33$fn__34 0x3c443976 \"user$eval33$fn__34@3c443976\"]"
Not only the lambda is not in its original form - you can't even parse that line anymore. The data is lost. So if you already have somewhere text that says "#(inc %)" - transfer that text. Don't try to evaluate it first, get the function object, then serialize that object, and then send it. Just send the text. Also take a look at this example:
user=> (def x 1)
#'user/x
user=> (pr-str #(+ % x))
"#object[user$eval25$fn__26 0x30e92cb9 \"user$eval25$fn__26@30e92cb9\"]"
Same as above, only the lambda now closes over x. You have to know about all such cases as well, and send (def x 1) and all similar forms along.

tessocog12:08:30

one could theoretically speaking have a smallest unit of meaningful code, such as a namespace and serialize within the confines of the namespace

p-himik12:08:58

If that ns is independent, I would just send the whole src/my/proj/ns.clj file over the wire. You still can't serialize evaluated objects, as I showed above.

tvaughan12:08:47

> No idea what that means. I mean the protocol could be CSV or any other thing that could get tripped up by special characters in Clojure source code

p-himik12:08:36

When you use a protocol, usually you don't just give it data that's supposed to be preformatted. If you write a CSV via some library, generally that library will quote and escape all the special characters for you. Same how you don't compose HTTP messages by hand.

Alex Miller (Clojure team)13:08:22

There are a couple of libraries designed to send serialized Clojure functions like this

Alex Miller (Clojure team)13:08:20

But in general if you are asking this question, you’ll want to think really hard about whether this is the best idea

💯 3
✔️ 9
jaihindhreddy04:08:16

Just to add to the couple of libs Alex Miller posted, there's this one as well: https://github.com/helins/fdat.cljc