Fork me on GitHub
#clojurescript
<
2022-10-24
>
steb11:10:50

Hello everyone 👋! Clojure newbie here. I have a question about clojurescript default browser repl (please tell me if this is the right channel for asking these kind of questions): I know it is possible loading static files with js/fetch but I noticed I can load only some kind of files like .html, .json and .png for example, but I can’t load .txt files for example. I browsed a bit the source code in https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/repl/browser.clj and I noticed there is an hardcoded map (`ext->mime-type`) of the “accepted” file types. Does anyone know why can’t we load any kind of file (with text/plain as default mime type) using the default browser repl during development? Thanks!

p-himik11:10:32

Huh, I've never heard of loading static files via CLJS REPLs. How exactly do you fetch the data and how do you use it?

Alexis Schad11:10:48

Hello and welcome to the Clojure world. That's not accepted file types but just automatic mime type conversion from extension. It defaults to "text/plain". How do you use it? How you sure the .txt file you are fetching exist?

steb11:10:11

Actually I am playing with WebGL and I wanted to load shaders sources (which can be considered text files with .glsl extension) on the fly while using the repl (in a repl driven development style)

steb11:10:44

The files are in the correct location, either ./ (the root of the project) or ./out are correct as far as I know

p-himik11:10:43

It seems to me that that map only considers what's useful for CLJS itself or for some HTML. It doesn't really consider arbitrary AJAX requests. But seems like you can circumvent that by adding your own :get handler, just like here: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/repl/browser.clj#L247-L250 Although I have no clue whether it would be considered a proper approach.

Alexis Schad11:10:16

Can you try to put some simpler text file at the root of the public folder? Like next to the index.html, put a test.txt with abc as content. then run in REPL:

(-> "test.txt"
    (js/fetch)
    (.then #(.text %))
    (.then js/console.log))

Alexis Schad11:10:40

It should log "abc"

Alexis Schad11:10:18

I've just tested it and it also work with test.glsl file

Alexis Schad11:10:39

When you say it doesn't work, do you have any error?

p-himik11:10:00

@U01V2D5ALKX Did you try it with the default cljs.repl.browser or with some other REPL? Because the default doesn't work for me and results in HTTP 404 for 1.txt but works for 1.jpg.

1
Alexis Schad11:10:34

Yes sorry it wasn't in the cljs default browser repl

steb11:10:48

Exactly I have the same 404 error @U2FRKM4TW has

p-himik11:10:03

Which one did you use @U01V2D5ALKX? Maybe @U03D7TEUQLF can just try switching to a different tool. I myself almost never use CLJS REPLs.

steb11:10:23

Sure! I am considering alternatives if there are better ones

steb11:10:55

Also I am wondering too if loading static files in that way is the correct approach

p-himik11:10:05

If you envision yourself using NPM, I'd recommend giving shadow-cljs a try. Can't say anything about its REPL but everything else is fantastic.

steb11:10:37

I’ll give it a try! Many thanks!

p-himik11:10:06

@U03D7TEUQLF More often than not, people build full-on websites where a REPL is just a helpful tool and not the main way to interact with something. In those cases, it's very common to have your own web server running in the background, and then serving any kind of a resource is in your total control. A REPL server would be an unrelated one in that case, but that doesn't prevent anything from working since the CLJS context is the very same web page.

p-himik11:10:24

It might very well be that your approach is the best one with your use-case. It's just that it's not a very common approach where REPL is the way to interact with the system and the REPL server process is also the server for the web page. But if there's a tool that supports that, then why not.

Alexis Schad12:10:34

I agree to try shadow-cljs, I don't think there is an alternative as powerful. And if you are using VS Code you can also use Calva which is great especially for using REPL.

steb12:10:40

Yes, I also thought about employing my own static web server (even a simple one built with few lines of nodejs for example), but I wanted to try also the static server built-in the default browser repl (which I guess it is ok for very small and simple use cases). I will try with your suggestions! Thank you for your help!

👍 1
dnolen19:10:26

the built in web server is quite simple and only intended for poking around - it just doesn’t handle all the various things for obvious reasons (just use a real web server) - that said, if you would like to add a mapping submit a patch (remember to submit a CA too)

Fredrik Andersson15:10:54

is it possible to create a dynamic namespace with closure over a variable? For instance in JS I would create a function that returns a object with partial functions

p-himik15:10:13

No, but you can use the same approach as you used in JS. Alternatively, you can create a "static" namespace and just have a var there that points to an atom that you reset when you want to "initialize" that ns.

Fredrik Andersson15:10:47

the atom solution isnt really an alternative. The other solution is ok but cumbersome syntactically i think

Alexis Schad15:10:55

I'm not sure to understand well, can you provide the JS equivalent maybe that would help? As I'm not sure I was thinking of using binding but I could miss the point.

Fredrik Andersson15:10:29

this is my current clojurescript implementation

(defn transaction [thread]
  (.runTransaction (db) 
   (fn [t]
     (thread
       {:create (partial transaction-create t)
        :update (partial transaction-update t)
        :write (partial transaction-write t)
        :delete (partial transaction-delete t)
        :read (partial transaction-read t)}))))
     

Fredrik Andersson15:10:24

it's a firebase/firestore wrapper

Fredrik Andersson15:10:58

this is how I would use it

(f/transaction
        (fn [{:keys [create update write delete read]}]
          (-> (f/doc [:tests :transaction])
              (create)))))))

Fredrik Andersson15:10:35

i realize now that I could create a macro to clean it up

Alexis Schad15:10:42

so yes I think binding should work here, if t is not an argument. You have to define t like this: (def ^:dynamic *t* default-value) (the * * is a convention to help know the variable is dynamic)

(def ^:dynamic *t* default-value)

(def fns {:create transaction-create
        :update transaction-update
        :write transaction-write
        :delete transaction-delete
        :read transaction-read
        :sample (fn [] (js/console.log *t*))})

(defn transaction [thread]
  (.runTransaction (db) 
   (fn [t]
     (binding [*t* t]
       (thread fns)))))

Fredrik Andersson15:10:35

i need to read up on binding

Fredrik Andersson15:10:48

thanks for the pointers

Alexis Schad15:10:27

clojuredocs is better generally because there are examples from the clojure community (than cljs doc)

Alexis Schad16:10:26

The simpler useful example I can think of is with-out-str https://clojuredocs.org/clojure.core/with-out-str As println uses *out* stream, initially to stdout, you can just bind *out* to a string stream and so get the result of all print in a string instead of in stdout

Lone Ranger19:10:43

I'm getting hammered by compiler warnings today and clearly have no idea how to annotate the types for this. Any ideas?

p-himik19:10:43

I can't find a Jira ticket for it, but go loses ^ tags. Extract the code that requires the tags into some functions and call those functions within go.

👍 1
Lone Ranger12:10:36

Thank you! 🌸

leif20:10:44

Any idea why clojurescript would say that (elaborate-fn [this__6__auto__] 42) is not a list?

leif20:10:19

(That s-expression comes from destructuring a macro.)

leif20:10:32

destructuring the syntax past into a macro*

leif20:10:58

But like, that looks like a list to me, and pr-str seems to print out a list.

leif20:10:52

It does seem to be a sequence at least. (Or at least seq? returns true)

Fredrik Andersson21:10:43

if you (doseq [itm list] (prn itm)) what does it output?

Fredrik Andersson21:10:30

maybe it's a string, that would be accepted as a seq too

dnolen21:10:40

@leif but are you checking for the list type?

dnolen21:10:01

seqs are an abstraction, list is a specific type

leif21:10:38

@dnolen I was checking for the list type, but maybe that's wrong?

phronmophobic22:10:43

you probably want to check with seq?

skylize22:10:03

A true "list" is a clojure.lang.PersistentList

=> '(1)
(1)
=> (type '(1))
clojure.lang.PersistentList
But output from "sequence transformation" generally will not be a PersistentList, even though it displays the same.
=> (map identity '(1))
(1)
=> (type (map identity '(1)))
clojure.lang.LazySeq

phronmophobic22:10:03

even syntax quote isn't guaranteed to return something that satisfies list?

> (type `(1 2 3))
clojure.lang.Cons
> (list? `(1 2 3))
false

skylize22:10:36

Personally, I've been leaning on coll? lately because seq? returns true for strings.

1
leif22:10:30

Ah, that makes a lot of sense,

leif22:10:38

and explains why I should use seq? instead. Thanks. 🙂

skylize22:10:54

Sorry, a blatent mistatement above. (seq foo) will make sequence a string, coll?get me around an resulting exception from chars showing in loops.

leif22:10:42

That's....a design decision.

leif22:10:47

Anyway, thanks for that heads up too.

alex22:10:20

X-posting from #nbb as this is a more general Clojurescript q

p-himik09:10:42

is has a return value that depends on what its first argument was. For built-in stuff and usually for third-party libraries as well, it's some truth-ey value when the check is successful. However some libraries might define custom assertions that work differently.

thanks3 1