Fork me on GitHub
#clojurescript
<
2018-01-15
>
Joshua Suskalo02:01:04

I've been having issues where I'm trying to create a function in clojurescript that's normally defined with the async keyword.

clojure
(defn on-context-create
  [gl]
  (js/Promise.
   (fn [resolve]
     (let [width (.-drawingBufferWidth gl)
           height (.-drawingBufferHeight gl)
           scale (.get PixelRatio)
           renderer (.createRenderer ExpoTHREE {:gl gl})
           camera (THREE.PerspectiveCamera. 50 (/ width height) 0.1 10000)]
       (doto renderer
         (.setPixelRatio scale)
         (.setSize (/ width scale) (/ height scale))
         (.setClearColor 0x000000 1.0))
       (.set (.-position camera) 5 5 -5)
       (.lookAt camera 0 0 0)
       (dispatch [:set-graphics-renderer renderer])
       (dispatch [:set-graphics-camera camera])
       (dispatch [:set-graphics-scene (setup-scene)])
       (resolve)))))
This is my version of the function, and here is the original:
javascript
onContextCreate = async (gl) => {
    const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
    const scale = PixelRatio.get();

    // renderer
    this.renderer = ExpoTHREE.createRenderer({
      gl,
    });
    this.renderer.setPixelRatio(scale);
    this.renderer.setSize(width / scale, height / scale);
    this.renderer.setClearColor(0x000000, 1.0);

    /// Standard Camera
    this.camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 10000);
    this.camera.position.set(5, 5, -5);
    this.camera.lookAt(0, 0, 0);

    await this.setupSceneAsync();
};
a lot of the logic inside that function isn't particularly relevant, but the issue I'm having is that when this function is being called, I'm getting the following error: "Unhandled promise rejection: TypeError: undefined is not an object" Any ideas how I can adjust my clojurescript function to produce the same result at the async function?

justinlee03:01:24

@suskeyhose What happened to the setupSceneAsync call? One thing that is confusing is that you cljs version doesn’t appear to any anything asynchronous happening. I suspect one of your calls is actually async, and you’re not waiting on it, and that’s why you are trying to read a property from something that isn’t defiend.

Joshua Suskalo03:01:11

The setup scene async call is done via the dispatch with (setup-scene) being called. And I'm actually not attempting to make anything properly asynchronous at the moment. Just trying to put into the promise what's required for the function call to work properly.

Joshua Suskalo03:01:13

And I've gone over where the specific error occurs, and it's deep in library code that I'm not calling, but I don't have access to the full stack trace so I wasn't able to tell where exactly it occurred, but the indications I've gotten is that it's somehow related to the resulting promise of this function.

justinlee03:01:44

Are you trying to call the cljs function from javascript (or pass it in to a library)?

Joshua Suskalo03:01:06

Pass it into a library.

justinlee03:01:33

Let’s do this in a thread so we don’t clutter the channel.

Joshua Suskalo03:01:45

So yeah, this function call is passed into a react-native view as a callback for a particular event.

justinlee03:01:59

Have you just tried passing a blank promise?

justinlee03:01:25

Because it seems like the issue is just that the logic is wrong somewhere and it IS behaving like the original

justinlee03:01:52

The only thing is that the error handling is a little different in your version.

Joshua Suskalo03:01:56

I'll try that. Although I know for certain that the javascript version works as intended.

justinlee03:01:16

You don’t have a rejection handler in your promise, so technically the promise is eating the bug in function.

Joshua Suskalo03:01:40

That's true. Should I do a null check for rejection then?

justinlee03:01:56

I actually just confused myself. I want to think about that for one second, but my point is that I don’t think that’s your problem.

justinlee03:01:29

I think you should make sure a blank promise works, becuase I think the problem is with the translation of the logic, not the way you’ve used promises.

justinlee03:01:51

I do think as a matter of sanity you should wrap the whole block in a try/catch and then call reject if you get an error

Joshua Suskalo03:01:06

Okay. I'll try that.

justinlee03:01:09

also log the error before calling reject

justinlee03:01:22

oh one other thought: if those dispatches are asyncronous and the library depends on them being there, then maybe the error you are getting is actually from the library trying to access something that doesn’t exist?

Joshua Suskalo03:01:15

Maybe. I've been trying to debug why that might be happening and the lack of a full stack trace has been hampering my attempts.

justinlee03:01:55

sometimes that happens when you swallow an error with promises even in js, so the try/catch might help

justinlee03:01:40

basically you’ve got a promise-returning function that is synchronous. that’s bound to break something.

Joshua Suskalo03:01:16

Alright, just tried to do the try/catch and it still gives the same error. Notably, the thing that is being evaluated when this happens is supposed to be the same object as is passed into the function. But in that vein, should I then just wrap the body of the inner function in a go block?

justinlee03:01:07

if gives you an unhandled promise rejection error?

justinlee03:01:47

okay so that is coming from the library. what does the dispatch function do? that sounds async-like. if the dispatch function returns a go-block, then you need to wrap the inner function in a go-block and <! on the dispatch

Joshua Suskalo03:01:57

Although the most recent time it's given me no indication about what is being called specifically.

Joshua Suskalo03:01:31

And the dispatch function just takes a vector of a event and a value and goes and updates the state of the program based on that value.

justinlee03:01:41

is it synchronous?

Joshua Suskalo03:01:02

It is as far as I am aware, and the return value isn't used.

justinlee03:01:55

okay i think you just need to start deleting lines until you make it go away to try to figure out what the offending line is

Joshua Suskalo03:01:59

Just checked the function's code and all it does it push something onto a queue and returns nil.

Joshua Suskalo03:01:05

And is synchronous.

Joshua Suskalo03:01:13

Alright, I'll try that.

justinlee03:01:01

actually it is likely one of the let bindings not working right

Joshua Suskalo03:01:22

Okay. I'll see if that's it.

justinlee03:01:34

is it on purpose that the JS version says “ExpoTHREE” and the cljs version says “THREE”

justinlee03:01:47

oh no nevermind

Joshua Suskalo03:01:36

Those are different vars.

Joshua Suskalo03:01:45

Okay, so I still have it wrapped and I've commented out a couple areas that could be causing errors, but now it's properly printing out an error saying that it's catching an error. Which is interesting and tells me that somewhere I'm doing something that isn't working as intended I guess.

justinlee03:01:19

the only differences i see are (1) that you create the camera before setting the renderer properties and (2) you call dispatch instead of awaiting this.setupSceneAsync.

Joshua Suskalo03:01:27

I guess one of the questions I have that I think might explain the issue I've been having is this: In the js version I have to pass the following form into a function: { gl, } and I'm unsure of how to represent that in cljs. I tried to just pass in both a persistent vector and the same going through clj->js.

Joshua Suskalo03:01:48

That I think might be the real issue.

justinlee03:01:16

well it wouldn’t be a vector. it would be (clj->js { "gl": gl }) if I understand what you mean by { gl, }

Joshua Suskalo03:01:26

I just changed that to (js-obj "gl" gl) and that seems to have changed some things. Instead of the error being from it failing to get a property, there is now something that is saying that it doesn't implement the protocol IDeref. I think this is something that I might be able to do better.

Joshua Suskalo03:01:18

Although to be honest I'm not sure where I'm calling deref.

justinlee03:01:09

yea that’s confusing. for what it is worth, i believe clj->js, #js, and js-obj all do the same thing with a simple map like this

Joshua Suskalo03:01:40

Alright, I figured out where the deref was.

Joshua Suskalo03:01:31

@(.loadAsync ExpoTHREE (js/require "./assets/images/cljs.png")) This is inside the setup-scene function. What is the equivalent of await in cljs? I was trying to do basically that since this function returns a promise, but I guess that doesn't work here.

justinlee03:01:34

ah yes that sounds more like it 🙂

Joshua Suskalo03:01:23

So do you have any suggestions about how I would go doing that? I haven't found anything good online for it.

justinlee03:01:08

wow so there is actual code loading going on there

justinlee03:01:22

I have no idea how that js/require is going to work

justinlee03:01:56

but let’s assume its a normal promise

Joshua Suskalo03:01:01

I could just replace it with a variable.

Joshua Suskalo03:01:21

but it would still be done using the loadAsync

justinlee03:01:21

well you need to load the ExpoTHREE thing before you do stuff to the renderer

justinlee03:01:41

that’s why your’e getting the bug, by the way. you are trying to set properties on the renderer, but not waiting for the promise to return

justinlee04:01:11

so either the .createRenderer call is puking or the .setPixelRatio is puking

justinlee04:01:29

actually it would be the setPixelRatio, I think

Joshua Suskalo04:01:04

But the deref where I'm getting issues is in an entirely different function where the renderer isn't even in scope. Is that really what's causing the issue?

justinlee04:01:20

you need to get ExpoTHREE defined before you use it. i bet if you printed it out, you’d find that it was nil

Joshua Suskalo04:01:41

Oh, yeah. ExpoTHREE is defined by a require right at the top of the file

Joshua Suskalo04:01:18

Yeah, just printed it out and it's massive

justinlee04:01:23

I see so you don’t even need the promise because you got rid of the only thing that is async

justinlee04:01:32

that was async

Joshua Suskalo04:01:54

I'm not getting an async error anymore though as far as I'm aware. I just don't know how to deref the promise that was returned by calling the .loadAsync function from ExpoTHREE since it doesn't implement IDeref

justinlee04:01:41

I’m not sure what’s going on, but to answer you question about async/await: any async function returns a promise, so if you want to “await” the promise in cljs, you just treat the return value as a promise and use then/catch on it

justinlee04:01:28

yea the @ is for dereferencing atoms. that’s a totally different thing. if you want the value of a promise, you need to use then/catch

Joshua Suskalo04:01:59

ah, okay. So just call then. That's going to be interesting since I just want to get the return value out of it, which means I'll have to deal with a crazy lambda like the bind operator from haskell without do notation.

Joshua Suskalo04:01:11

actually, exactly like that since this is technically a monad.

justinlee04:01:19

if you’re doing a lot of promise interop, you might use a library like http://funcool.github.io/promesa/latest/ though I myself haven’t tried

Joshua Suskalo04:01:05

Thanks, I'll take a look.

Joshua Suskalo04:01:42

Guh. Now I know why it's called callback hell. Promises are a pain to work with in js.

Joshua Suskalo04:01:29

Although I suppose most times people aren't talking about promises when they refer to callback hell.

justinlee04:01:25

Promises are better than callbacks in syntactic way. But they introduce another problem: it is now possible to invoke a function in a normal-looking way but actually have it be asynchronous. That is very subtle and the language doesn’t help you out. I wish the language would warn you if you issued an asynchronous call without waiting for it (or expressly ignoring it)

justinlee04:01:01

with callbacks it is very obvious that something is async

Alex H06:01:51

fwiw, it's reasonably easy to write an adaptor from promises to core.async that'd be quite similar to async+await

kenrestivo04:01:37

they're talking about worse: callbacks without promises.

kenrestivo04:01:52

.then .then .then is an improvement over how it used to be

kenrestivo05:01:33

likewise with core.async

carkh09:01:52

A not strictly speaking clojurescript question, but it's in clojurescript code so there goes : I want to make a lazy sequence out of data that is provided from a library callback, rather than from my own recursive calls. Do I have to use core.async for that, or is there some easier way using lazy-seq ?

carkh09:01:03

does it even make sense to do so ? what if I block waiting for the next lazy value in the javascript single threaded context ...

rauh09:01:19

@carkh You can't. lazy-seqs is pull based "give me the next element NOW". Whereas async can only provide a value at some unknown point in time. You can't block in JS so you'd have to throw if the result isn't available yet.

carkh09:01:57

@rauh yep it sure looks like you're right... Looks like I'm stuck with callbacks =(

rauh09:01:06

@carkh Think about it: Once you go async you can never go back to synchronous code for that result until it's available and you run in a new macro task.

rauh09:01:09

You can write syncronous looking code with async/await and core.async though.

carkh09:01:29

I actually am writing an adapter to the sqlite library for node, and I use goog.Promise ... but there is this one funciton that will additionally require an on-row callback for each row it returns

carkh09:01:18

I'll probably never use that one in the current project ... I guess i'll leave it at that =)

carkh09:01:26

thanks for your time !

jakob11:01:53

Hi, clojurescript n00b here. I am trying to figure out how this works: https://github.com/alexanderkiel/phrase. Trying to evaluate an expression straight from the examples:

(defphraser #(<= min-length (count %))
  [_ _ min-length]
  (format "Please use at least %s chars." min-length))
it gives this error:
Use of undeclared Var rapidshop-b2b.events/format

  1                      (defphraser #(<= min-length (count %))
  2  [_ _ min-length]
  3  (format "Please use at least %s chars." min-length))
(it says the min-length variable is undeclared) It's tricky when the most basic example wont evaluate 😄. any inputs?

peeb12:01:42

@karl.jakob.lind (format) is not available in clojurescript. Either use (str) or something like cuerdas https://funcool.github.io/cuerdas/latest/ - which contains a (format) function, and many others.

jakob12:01:37

when I use str, it says min-length is not defined. and when I remove min-length it says - is not defined :thinking_face:

akiel12:01:38

@karl.jakob.lind The examples are only tested in Clojure. I currentliy work on examples which work both in Clojure and ClojureScript.

akiel12:01:21

In ClojureScript you need to require defphraser as macro: (require '[phrase.alpha :refer-macros [defphraser]])

jakob12:01:03

ok! do I also need to require phrase-first as a macro?

akiel12:01:46

than you have to require goog.string format: (require '[goog.string :refer [format]])' and (require [goog.string.format])

jakob12:01:52

Now I got this error: Invalid :refer, macro phrase.alpha/defphraser does not exist in file <cljs repl>

akiel12:01:14

no phrase-first is a function

akiel12:01:27

what kind of ClojureScript REPL do you use?

jakob12:01:58

I use Emacs and Cider, not 100% what happens under the hood 😄

akiel12:01:26

Can you please try planck with: planck -D phrase:"0.2-alpha1"

jakob12:01:24

sure. just need to install planck. give me a few mins

jakob12:01:13

hm. wasn't that easy to install on fedora...

jakob12:01:30

I use a repl that i start from lein I think.

akiel12:01:32

But it’s a ClojureScript REPL - right?

akiel12:01:27

In planck it loads everything fine, but at the end the message is not generated. That’s why I also need some time. On the other hand, I have tests which I run with nashorn and phantom which work and phrase also works the browser.

akiel12:01:39

Did you manage to install planck?

jakob13:01:16

not yet. trying to build from source

jakob13:01:36

is phrase not yet ready for production use?

jakob13:01:11

trying to use it on clojure now, get this when trying to require it in (`[phrase.alpha :refer [defphraser phrase-first]]`):

1. Unhandled java.lang.IllegalAccessError
   defphraser does not exist

jakob13:01:44

maybe it's something with my project setup...

akiel13:01:50

Phrase works in many of my own projects very good. But until now I had not a lot of feedback. So it’s quite possible that it integrates now well in other projects.

akiel13:01:56

In Clojure: Do you use a Leiningen project with phrase as dependency?

jakob13:01:59

yes I added it [phrase "0.2-alpha1"]

jakob13:01:39

require it like this works: [phrase.alpha :as phrase]

jakob13:01:01

i mean it doesnt give error when require it in when i do it like that

jakob13:01:13

but it still can't find dephraser in phrase 😞

akiel13:01:38

If you don’t require defphraser in your namespace, you have to refer to it by namespace alias or full name: phrase/defphraser.

jakob13:01:53

Talked to the clojure guy on our team. He says we are using [clojure-future-spec "1.9.0-beta4"] and clojure 1.8. Maybe that could be the issue.

jakob13:01:06

yes i tried to access phrase/defphraser

flowthing13:01:08

Which ClojureScript version are you using?

akiel13:01:47

Ah yes, you need Clojure 1.9

jakob13:01:22

ok, hope it's a smooth process to upgrade 😄 thanks for the help @akiel! :thumbsup:

akiel13:01:21

I found the reason why phrase doesn’t work in a ClojureScript REPL (Planck). It has something to do with self-hosted ClojureScript. However phrase works fine when compiled with the Clojure-based ClojureScript Compiler.

fbielejec15:01:43

I have a cljs project with nodejs backend and your regular reagent + reframe front-end and I'm exploring options for end-to-end testing. I reckon I need to: 1) Write a .clj test with a fixture which compiles .cljs code with a nodejs target and boots the server (how?). 2) The same file has a single test which compiles (targeting browser), runs and reports test suite (with a doo runner for example). Has anyone been doing something similar?

justinlee17:01:54

can someone point me to documentation that describes the differences between something like (.-key jsobj) and using good.object.getValues directly? i’m trying to figure out why a library does all of its javascript object manipulation with the gcc interface instead of the cljs features for doing so (i.e. is it a preferences or is there a difference in semantics)

thheller17:01:45

@lee.justin.m .-key property interop may result in renaming by the closure compiler :advanced and relies on externs protecting against that if unwanted. js interop via goog.object is safer in that regard if you don't want to bother with externs

justinlee18:01:52

question about cljsjs and externs: do people provide externs out of an abundance of caution or do most libraries really have issues with gcc? reading through the conventions required for advanced optimizations, I wouldn’t have thought that many libraries would really need externs. seems like if a library doesn’t have issues, it would be preferable to include the library rather than extern it

thheller18:01:11

IMHO quite often its just a misunderstanding of how externs work and overproviding externs (generated) is just the easy path

darwin18:01:51

@lee.justin.m correct, better include libraries as part of your closure compiler build, but that is not always possible, the library must be closure-compiler-compatible(tm)

darwin18:01:07

I wrote a pretty sophisticated wrapper library for using string names, basically a replacement for goog.object.getValues: https://github.com/binaryage/cljs-oops

justinlee19:01:31

@darwin I hereby award you a medal of honor for an excellent readme

justinlee19:01:01

so if you use that library to access external libraries, you can just avoid the whole externs mechanism?

justinlee19:01:11

that seems totally awesome

justinlee19:01:58

(two side thoughts: why does gcc stop at renaming string-based property accesses for properties it knows it going to mess with? how do normal minification tools do all this without chaos and would it be better to use gcc’s DCE withOUT the symbol munging?)

darwin19:01:16

@lee.justin.m thanks 🙂 yes, I’ve been living without externs happily for quite some time, cljs-oops is also useful for working with data incoming over wire, e.g. when you receive a JSON data via AJAX, you want to use string names to prevent advanced mode munging

justinlee19:01:09

oh wow i never even considered the json issue. so when i do a http/post and get back a response body, my code is probably broken right now because later when I do a (get-in response [:body :user-id]) or whatever, the :user-id symbol might get altered by gcc?

darwin19:01:34

as for your questions, 1) it is a convention, I think, gcc does not rename string property access because in general case it might not see what the strings are (they can be computed dynamically), and trying to treat static strings differently would cause more confusion, they have a simple rule now “we don’t touch string names”

darwin19:01:27

2) I don’t know, I think minification tools run into the same issue as gcc, when trying to munge something which is accessed by string for example

justinlee19:01:28

yea, as for (2) I would have thought the same thing, which is why i’m surprised that so many libraries apparently need externs, since virtually everything survives minification

darwin19:01:01

yes, DCE is harder problem than minification, things which survive minification won’t necesarily survive gcc’s DCE which is pretty aggresive, so you won’t help yourself trying to combine the two tools

darwin19:01:35

3) (get-in response [:body :user-id]) will work, get-in will use a protocol to access underlying native javascript object, which will convert keywords to strings and use string access, I believe

darwin19:01:45

(.-body response) would be a mistake

justinlee19:01:34

ok that makes sense. thanks!

darwin19:01:00

@lee.justin.m does that get-in code really work for you? I just did a quick test in planck: (get (js-obj "key" 1) "key") returns nil

justinlee19:01:21

i’m not doing advanced optimizations 😞

darwin19:01:42

me neither, just playing in REPL

justinlee19:01:29

if that doesn’t work, i must say some of my confidence will be shaken. how could anybody ever use a library like cljs-http if that doesn’t work and surely that would be written down on the readme of the project? (right?)

akiel19:01:32

@karl.jakob.lind Phrase works now in Self-Hosted ClojureScript. I have documented a complete example using Planck: https://github.com/alexanderkiel/phrase#complete-example-in-clojurescript-using-planck

jakob08:01:13

nice! I now got that example to work in my project!

darwin19:01:20

@lee.justin.m: one could implement ILookup protocol for a native javascript object, and then it would work, but that has some sharp edges

mfikes19:01:23

@akiel One idea to consider is to have phrase.alpha require itself for its own macros, with the end goal being so that ClojureScript users can simply require the namespace and refer Vars in it without regard to whether they are functions or macros.

mfikes19:01:15

So you can (portably) write

(require '[phrase.alpha :refer [phrase-first defphraser]])

akiel19:01:39

@mfikes Thanks. Never done that. Can I simply require my defphraser macro in phrase.alpha even if it’s defined in the same namespace?

justinlee19:01:52

@darwin i could do that, but surely the pattern of get-json-over-http and then do-things-with-it is a pattern common to all web pages. there must be some normal way of doing this that doesn’t involve me reinventing the wheel

mfikes19:01:04

@akiel The namespace simply needs to :require-macros on itself so that you then get the Implict macro loading capability described under (doc ns)

darwin19:01:11

@lee.justin.m I think people usually call js->clj, which is dirty, but does the right job in 99% cases

justinlee19:01:27

@darwin and I just re-read your question above. i am calling get on whatever cljs-http is returning, which i assumed was a cljs map, not a js-obj

mfikes19:01:57

@akiel Since you want to support Clojure, ClojureScript and self-hosted ClojureScript all in the same namespace, things can get convoluted fairly quickly, but the payoff in the end is that all clients of your library end up using it in a uniform fashion.

justinlee20:01:43

@darwin Ah yes great. whew 🙂

akiel20:01:49

@mfikes Thanks. It works! Another question: Do you have a solution for referring to goog.string.format? If I use (require '[goog.string :refer [format]]) it errors in planck but works in JVM compiled ClojureScript.

mfikes20:01:39

@akiel The patterns described here for goog.string.format should work and be portable: https://clojurescript.org/reference/google-closure-library#requiring-a-function

akiel20:01:07

@mfikes Yes that works, but than I have to call (gstring/format ...) in cljs and (format …) in clj

mfikes20:01:35

@akiel I see. Are you explicitly causing the format code to be loaded in ClojureScript? Just the require alone doesn't appear to be sufficient:

$ clj -m cljs.repl.node
ClojureScript Node.js REPL server listening on 57812
To quit, type: :cljs/quit
cljs.user=> (require '[goog.string :refer [format]])
nil
cljs.user=> (format "%05d" 123)
repl:13
throw e__8152__auto__;
^

TypeError: goog.string.format is not a function

akiel20:01:56

@mfikes In planck:

cljs.user=> (require '[goog.string :refer [format]])
Could not parse ns form cljs.user
Invalid :refer, var goog.string/format does not exist
cljs.user=> (require 'goog.string.format)
nil
cljs.user=> (format "%05d" 123)
"00123"

akiel20:01:31

Problem is that a tach test terminates at the error message

mfikes20:01:33

Yeah, same in Lumo. I wonder how JVM ClojureScript is working...

mfikes20:01:55

@akiel I suppose if you are in a cljc file you could hack it with a #?(:cljs (def format goog.string/format))

mfikes20:01:00

It might be good to avoid goog.string/format altogether if possible, to be honest, given its essentially deprecated nature.

akiel20:01:44

@mfikes Yes I do this already in my test. But for the README it’s not nice. I think about just to remove format from the README.

mfikes20:01:32

Yeah, for the README, maybe just (str "Please use at least " min-length " chars.")

lxsameer20:01:03

hey guys, I'm looking for something like Manfold library for cljs, any recommendation ?

Dos21:01:16

Hello, how to check if is valid datetime in cljs-time lib? I want something like:

(valid-date? (formatter "dd.MM.yyyy hh:mm A") "01.01.2018 10:10 AM") ;true
(valid-date? (formatter "dd.MM.yyyy hh:mm A") "32.01.2018 10:10 AM") ;false

Dos21:01:41

upd: solved this way:

(try (parse datetime-formatter "32.01.2018 10:10 AM")
   (catch :default e (some-fn)))

justinlee21:01:47

are some cljsjs projects hosted outside the cljsjs project? I see references to cljsjs/react-dnd here https://github.com/johnswanson/reagent-dnd/blob/c2b3b0e42c8e534f6d33f19fd763ec9e5bfaa6e6/project.clj#L8 but I don’t see that project on the actual cljsjs website and am beyond confused

kenny22:01:03

Is this intentional?

(let [my-uuid (random-uuid)
      encoded (transit/write (transit/writer :json) my-uuid)
      decoded (transit/read (transit/reader :json) encoded)]
  (uuid? decoded))
=> false
It's false because the type of decoded is #object[Transit$UUID] and it doesn't look like Transit$UUID has a IUUID implementation: https://github.com/cognitect/transit-js/blob/212a917eb3ca6a2bab6718a90935f30f035a855a/src/com/cognitect/transit/types.js#L273-L357

kenny22:01:38

A simple fix for now is to add this to your code:

(extend-type ty/UUID
  IUUID)

kenny22:01:57

Didn't see that one. Any idea if there is a reason for the types being different?

kenny22:01:26

Rather, com.cognitect.transit.types/UUID not extending IUUD in transit-cljs?

dnolen22:01:17

@kenny I think the main issue here is that transit-cljs actually works with ClojureScript 1.7.170? IUUID protocol was introduced in 2016, but transit-cljs project is from 2014

dnolen22:01:44

hrm but I guess protocols do generate a function? So we could test that it exists? and then extend?

deadghost23:01:36

I thought it might've been a cljs-ajax issue but if the success handler gets triggered then it's on re-frame