Fork me on GitHub
#clojurescript
<
2021-01-20
>
tomrbowden01:01:59

Does anyone know of a ClojureScript playground equivalent of CodePen/CodeSandbox?

grazfather02:01:23

I do not, but i imagine you could spin one up rather quickly with figwheel?

tomrbowden02:01:31

I'd like to be able to easily share frontend code and its visual effect with others via a URL...

tomrbowden02:01:17

@dpsutton That's quite nice for embedding live code into a website. But are there are no CLJS solutions for quickly creating a small code sandbox with dependencies (eg. reagent), and sharing it via URL?

dpsutton02:01:37

not that i'm aware.

Grigory Shepelev08:01:37

Hello there! Please help. How do I set up HTML5 history with retit in my webapp on shadow-clj? I used the official example https://github.com/metosin/reitit/tree/master/examples/frontend Except for having shadow-clj instead of figwheel. And basically copy-pasting server and frontend code with the following addition on config:

:dev-http {8000
            {:root "public"
             :handler shadow.server/handler}}

Grigory Shepelev08:01:01

And It show the weird error on any path except / .

Grigory Shepelev08:01:16

Uncaught SyntaxError: expected expression, got '<' web-api.js:1

Grigory Shepelev08:01:59

If there are anyone who set up retit with html5 history in cljs, please answer!

thheller10:01:44

@altjsus that means your server served some HTML when it was expecting some Javascript

thheller10:01:22

maybe your handler didn't properly serve the files

pinkfrog14:01:00

What args will be passed in to the inner function?

pinkfrog14:01:12

(defn timer-component []
  (let [seconds-elapsed (reagent/atom 0)]     ;; setup, and local state
    (fn [a b c d e f g h i j k ]        ;; inner, render function is returned
      (js/setTimeout #(swap! seconds-elapsed inc) 1000)
      [:div "Seconds Elapsed: " @seconds-elapsed])))
If I add arbitrary args, e.g., a, b, c, d, e,fโ€ฆ nothing goes wrong. The function arity is fine. Weird.

thheller14:01:13

the same args as the outer fn

pinkfrog14:01:46

In any case the outer args would change?

pinkfrog14:01:28

[out-func arg1 arg2] They are fixed written in the code.

pinkfrog14:01:44

Fundamentally, when reagent calls the inner func, where are the args from?

thheller14:01:35

as I said ... they are the same args. don't have time to explain this further sorry.

thheller14:01:23

all this does is let you create state once on mount, after that is only calls the inner function

thheller14:01:30

just might change on update, the outer ones you only get once

prnc15:01:57

Hi everyone ๐Ÿ‘‹ what is the correct way to handle uuid deserialisation from transit in cljs? the default is to com.cognitect.transit UUID instead of cljs.core/UUID, Thanks ๐Ÿ™

prnc15:01:50

is it still: just add (extend-type com.cognitect.transit.types/UUID IUUID) to your code, as per: https://github.com/cognitect/transit-cljs/issues/41?

dpsutton15:01:34

yeah go ahead and do that. that's the most confusing bug ever ha

prnc15:01:30

yeah it was pretty surprising to see

Hagenek15:01:06

Do you guys know of any beginner-friendly and free tutorials for ClojureScript // ClojureScript Reagent?

njj15:01:47

And then the reagent docs, once you have a grasp on the clj/cljs syntax

Hagenek20:01:26

Thanks:smiley: I am on chapter 4 of Brave True now

grounded_sage16:01:09

Is there a simple way to escape core async using callbacks?

jtkdvlp16:01:46

there are take! and put! fns using callbacks I guess, but why use callbacks? XD https://clojure.github.io/core.async/#clojure.core.async/take!

grounded_sage16:01:13

How would I rewrite this

(defn async-plus []
    (go (<! (go (+ 1 1)))))
So when I call it I get the result and not the channel.

jtkdvlp16:01:09

cljs or clj?

jtkdvlp16:01:39

so you want make it a sync call?

grounded_sage16:01:09

I want to escape channels

Alex Miller (Clojure team)16:01:32

I think take! is your only option there

dpsutton16:01:44

you've got extra levels of go and <! there just pointing out. And you cannot. Imagine (+ 1 1) took three seconds. There is no notion of blocking or waiting in js like that

jtkdvlp16:01:06

In cljs you cant make it sync, since you are async you will always be async. In clojure you could wait for the result blocking the current thread, on cljs you cant cause its single threaded js

grounded_sage16:01:00

Iโ€™ve got it there for simplicity. Assuming that the (+ 1 1) is an operation that must be async. The <! is to simulate me taking from it. Which also must itself be in a go block.

dpsutton16:01:52

(go (<! (go (+ 1 1)))) would be equivalent to (go (+ 1 1)) they both will be a channel with the value 2 on it

grounded_sage16:01:45

Yea I just thought that between the first go and take. When the value is received. There would be a way to use a callback or something to return it. But I canโ€™t figure it out.

jtkdvlp16:01:29

(take! (go (+ 1 1)) your-callback-fn) or (go (your-callback-fn (<! (go (+ 1 1))))

grounded_sage16:01:46

(defn async-plus []
 (take! (go (+ 1 1)) identity))
returns nil when called

jtkdvlp16:01:03

from the docs:

Usage: (take! port fn1)
       (take! port fn1 on-caller?)
Asynchronously takes a val from port, passing to fn1. Will pass nil
if closed. If on-caller? (default true) is true, and value is
immediately available, will call fn1 on calling thread.

fn1 may be run in a fixed-size dispatch thread pool and should not
perform blocking IO, including core.async blocking ops (those that
end in !!).

Returns nil.

grounded_sage16:01:08

Iโ€™m obviously stuck. Seems there is no solution?

dpsutton16:01:08

no. you can't wait for an async operation to continue and carry on like that. the callback has to be the thing to handle it

jtkdvlp16:01:26

in clojure you could do (println (<!! (go (+ 1 1)))) in CLJS you cant

grounded_sage16:01:47

So I probably have to introduce an atom here and try and figure out some trickery

dpsutton16:01:50

imagine (take! (go (+ 1 1)) identity) the (+ 1 1) takes 20 seconds. javascript has no way to pause the world while waiting for the twenty seconds

grounded_sage16:01:12

I know. I just thought there might be a simple callback solution.

dpsutton16:01:16

it's just going to call (identity 2) in twenty seconds

grounded_sage16:01:38

all good. I think I have to rethink how I want to approach this

Jp Soares18:01:40

Question about self-hosting cljs. Is there a way to use the compiler option :warning-handlers with cljs.js/eval-str? It looks like the available options of [cjs.js/eval-str](https://cljs.github.io/api/cljs.js/eval-str) are limited.

(defn evaluate [s callback]
  (cljs.js/eval-str
    compile-eval-state
    s
    nil
    {:eval cljs.js/js-eval
     :load (partial shadow.bootstrap/load (analyzer/empty-state))
     :warning-handlers [my-fn]
     :context :expr}
    (fn [result] (do (js/console.log result)
                     (callback result)))))
I also tried to pass compiler-options as an argument of analyzer/empty-state but I had no success.

Ivan Curic19:01:29

anyone else is having problems with Calva in VS-Code and clojure-lsp never managing to start?

pez23:01:59

Hello. Can you post this in #calva?

๐Ÿ‘€ 3
Ivan Curic12:01:55

it seems something fixed itโ€ฆ no idea what ๐Ÿ˜… will do if I encounter it again.

pez12:01:56

Deal! ๐Ÿ˜ƒ

babardo20:01:51

Hello im starting to dig into promise chaining in cljs, and for the moment I'm happy with core.async. (https://clojurescript.org/guides/promise-interop#using-promises-with-core-async)

babardo20:01:44

But I don't know how to compose ๐Ÿ˜• Is it an easy way to get back a js/promise from a channel?

babardo20:01:34

I'm thinking on wrapping chan in a js/promise and using take! to trigger the callback. Is there a better way to do?

dpsutton20:01:09

i think it would be something like this:

(def c->p [c]
  (let [p (js/Promise.)]
    (take! c #(.resolve p %))
    p))

๐Ÿ™ 3
dpsutton20:01:16

oh and return p sorry

babardo20:01:19

thx I will add a bit of error handling and it will be nice ๐Ÿ™‚

dpsutton20:01:01

if you check the source of the macro p->c it will be largely like this

dpsutton20:01:52

ah its <p!.

๐Ÿ™ 3
babardo20:01:02

ah yes, it does the error checking :thumbsup:

dpsutton20:01:06

cljs.user=> (source clojure.core.async.interop/p->c)
(defn p->c
  "EXPERIMENTAL: Puts the promise resolution into a promise-chan and returns it.
   The value of a rejected promise will be wrapped in a instance of
   ExceptionInfo, acessible via ex-cause."
  [p]
  (let [c (async/promise-chan)]
    (.then p
           (fn [res]
             (if (nil? res)
               (async/close! c)
               (async/put! c res)))
           (fn [err]
             (async/put! c
                         (ex-info "Promise error"
                                  {:error :promise-error}
                                  err))))
    c))

dpsutton20:01:31

make a channel, use the then of the promise to put on the channel. same idea here. make a promise, when you take from the channel resolve the promise with it

Adam Helins23:01:38

Unless I am mistaken, it looks like having a namespace ending in 'init' (eg. my.namespace.init) completely breaks things. It compiles without a fuzz but other namespaces are loaded as undefined. Does it make sense? Using Shadow-CLJS

p-himik01:01:30

Seems to work just fine for me. Try clearing all the cache and build artifacts, including the .shadow-cljs dir.

Adam Helins12:01:45

There is something else at stake... My namespace which is pretty much empty is loaded but results in being undefined when used somewhere else... I've spent hours on this

p-himik12:01:54

If it still doesn't work after clearing everything build-related, can you create a minimal reproducible example and share the link to its repo?

thheller13:01:35

if you get an exception during load that may prevent the namespace from being created. check the browser console.

thheller13:01:05

other than that my.namespace.init is completely fine and should work

Adam Helins15:01:10

It's madness, utter madness. I've spent a whole day on this! Never ever have I encountered something as maddening and I had my fair share of weird behavior with CLJS... I have a CLJC file in a folder. At this point it's just a namespace with (def foo 42) as a test. When required from a CLJS file, everything is fine, I can access foo. When required from a CLJC file, then suddenly everything breaks. The namespace becomes undefined, even in CLJS. Compilation is fine, requiring is fine (error comes when trying to access foo on undefined later). But wait it gets worse. This very same file, when put in another folder works right away from everywhere. I have a cursed folder name piece and any CLJC file inside of it, when required from another CLJC file, produces this behavior. It's not about permissions, I have even re-created that folder to make sure. I have literally DOZENS of other CLJC files required by CLJC files. It's just about that damn piece folder. Works with piece2. Furthermore, the JS output file looks absolutely fine. Clearing .shadow-cljs, .cpcache and all compiled JS files, the browser cache... doesn't make a difference.

p-himik15:01:41

Please create a minimal reproducible example.

thheller15:01:56

just run shadow-cljs browser-repl

thheller15:01:05

and then (require 'my.namespace.init)

thheller15:01:25

what does "the namespace becomes undefined" even mean?

thheller15:01:32

do you get any warnings? do you maybe have (ns my.namespace.init) and also (ns my.namespace) (defn init []...)?

thheller15:01:14

it is rather unlikely this has anything to do with caching and so on so rather look in your own code for causes

Adam Helins17:01:56

No warnings at all, and I can require and use this namespace from the browser-repl just as from any Clojurescript file. "becomes undefined" means that when it is required from a CLJC file, then is sorts of suddenly disappear. When I try to access a var such as foo from the REPL or anywhere in my code, I get a TypeError: Cannot read property 'foo' from undefined. And it's so weird I have to repeat it, it only happens with namespaces in that one folder. I really can't see what could produce this mystery in my own code or code organization, I am following the same simple structure I've been using for dozens of files... I'll try to make a repro case but since I have no idea what is going on, I'm not sure I'll succeed (and I cannot share the source of this quite massive commercial project).