Fork me on GitHub
#clojurescript
<
2021-06-17
>
craftybones02:06:10

Is there a difference between (vec (concat v1 (repeat n x))) and (vec (take n (concat v1 (repeat x))) ?

craftybones02:06:18

I’m facing a weird problem that I am unable to understand.

craftybones02:06:38

I’m iterating over a sequence and generating divs contained in a flex(reagent).

craftybones02:06:18

When the sequence is generated with the first form above, the flex renders with the correct wrapping. The second form has a margin that I can’t explain. And there is absolutely no other difference in the code

craftybones02:06:07

This makes no sense, but here is some reference

craftybones02:06:31

the dom is the same, the styles are the same. I’m completely stumped as to what is happening

dpsutton02:06:19

Seems like there is a difference in that in general. Consider if v1 has more elements than n. Your resulting sequence would have 0 x’s and in the first form would have n x’s

craftybones03:06:09

It is not the same n between the two forms

craftybones03:06:28

the n in the first form is (- total-n (count v1))

craftybones03:06:41

whereas in the second form it is total-n

craftybones03:06:36

My issue isn’t with the number of elements or the content of elements per se. It seems to have something to do with the iteration across the type, and I am unable to understand why or what. There is no reason why two exact same sequences should render differently just because they were generated differently

craftybones03:06:56

Assuming (= v1 v2) , then (for [x v1] [some-component x]) should necessarily equal (for [x v2] [some-component x]) given that some-component is pure

phronmophobic03:06:20

how have you confirmed that v1 and v2 are equal?

craftybones03:06:53

I’ve checked both content and type. Both (= v1 v2) and (= (type v1) (type v2))

phronmophobic03:06:00

when you check the resulting dom in the devtools inspector, what's the cause of the extra padding/margin?

craftybones03:06:45

@smith.adriane - that’s the weird part. Every computed property is the exact same!

craftybones03:06:06

all the margins, padding, borders are the exact same.

craftybones03:06:16

I’m using a flex-wrap

phronmophobic03:06:56

the computed properties are the same for each component and for the parent?

craftybones03:06:04

Yeah. Exact same!

phronmophobic03:06:14

I don't believe you 😛

phronmophobic03:06:01

are there the same number of child elements in each case?

craftybones03:06:03

Argh. Thanks @smith.adriane - The problem was with the count. The last div was an empty div in one of the cases that was being added by some other event 🙂

🎉 2
craftybones03:06:28

Spent the last half an hour losing my hair on this and I’ve such little hair left to begin with

phronmophobic03:06:52

this matches my experience with CSS

2
craftybones03:06:18

For some vague reason when the flex has only enough elements to fill a row without any spillovers it borks the margins. CSS - Clearly Screwball S**t

😂 2
chepprey23:06:20

"can't style s##t"

craftybones03:06:36

Thanks a ton. we totally forgot about it

Hlodowig04:06:43

Wacky question, is it possible to develop a Chrome extension in Clojurescript?

👍 2
Marcus07:06:55

Hi! 🙂 Which libraries do you use for working with numbers and time (parsing, formatting, validation, manipulation etc)?

magnars08:06:54

Maybe https://www.juxt.land/tick/docs/index.html could be of interest? It's "java.time in cljs", which is a pretty good place to start.

Marcus08:06:18

perfect @U07FCNURX thanks! 🙂

magnars08:06:36

I am transitively using foreign-libs (through a cljsjs package). This package has declared :file and :file-min , but it doesn't use the minified file when I do advanced compilation. How is this decided, and does it work transitively?

henryw37409:06:07

according to the docs: https://clojurescript.org/reference/compiler-options:file-min (Optional) Indicates the URL to the minified variant of the library. This will be used in preference to `:file` if `:optimizations` is set to either `:simple` or `:advanced`. sounds like a bug if that's not working

magnars09:06:23

I agree, but I'm not sure where to start looking.

henryw37410:06:51

jira, small repro and crack open the compiler are where I've looked next for this kind of thing

henryw37410:06:49

http://ask.clojure.org does not seem to be monitored for clojurescript, but in theory that's another option

Tomaz Bracic10:06:27

I am really new to Clojurescript (and Clojure for that matter) and would like to ask the community 🙂 few things. So, I am looking around what could I start using as alternative to pure JS (React) and Typescript. Those are last on my list. I was really into Elm for a while, but elm community has some other problems too I could say. What I would like here is if I would go with Clojurescript... can I distance myself from NPM problems, how is with refactoring projects after a while. Is it any better by using CS or is pretty much the same as if I would work with React? I assume Reagent and similar follow development of React so there are changes? I read somewhere that this CS ecosystem is pretty much stable for the last 7 years... This type inference to what degree can i rely on it? With Elm ... you really get a lot of safety, but with the cost of decoupling JS World a lot and only allow interoperability via Ports

Tomaz Bracic10:06:50

How good is CS for bigger projects?

Tomaz Bracic10:06:45

What I meant with "can i rely on it' ... can I do something like compiler driven development.... ? like in elm?

borkdude10:06:38

@tomaz.bracic Type inference is mostly used for performance in CLJS, not for safety. In the CLJS or Clojure in general ecosystem there are a few tools / linters (e.g. clj-kondo) that can help you avoid making silly mistakes, but they don't provide a static type system. There is also #lsp which can help you navigate projects. If you use #calva you get both tools integrated into VSCode, which may be a good way to start, but you can make it work with any editor really. To get "safety" it's best to write a test suite. There are also runtime validation libraries like #clojure-spec and #malli which can help you. Another piece of advice: if you don't know the CLJS tooling well, start with #shadow-cljs. This makes it easy to integrate with NPM libraries, but if you are using JS libraries from NPM, naturally you are going to get the pain of breaking changes from that ecosystem. CLJS is very good for big projects. I've been working on a big project for 5 years and if I would start over, I would pick CLJS again. For bigger apps, it might also be good to look into #re-frame if you are using #reagent.

Tomaz Bracic10:06:41

O @borkdude I really appreciate your feedback. Thanks!

borkdude11:06:20

(and wow, the youtube url has geeK in it)

Tomaz Bracic11:06:41

yea... I saw this. Nice talk... though I was a bit shocked at the beginning because my other tech is Elixir too 🙂

borkdude11:06:08

So what are the problems you perceive in the elm community?

borkdude11:06:21

Just out of curiosity.

Tomaz Bracic11:06:56

uf... there is a lot written on this

Tomaz Bracic11:06:09

the feeling around how the whole developoment is going on

Tomaz Bracic11:06:34

Evan (author) ... has Elm ... at least this is the feeling... like a private / closed project put out to public for use. So when you read about complaints... (but then there is another side... who is really happy) it is really hard for some changes to happen. It is not really up for discussion

Tomaz Bracic11:06:45

how the future will be ... what will be supported or not

Tomaz Bracic11:06:03

those decisions are done in really really closed group if not only in Evans head

borkdude11:06:15

Clojure's development is pretty much managed the same way: a small group of people, lead by Rich Hickey, decide what's going into Clojure and what the roadmap is. The community can vote on issues, etc. But Clojure, being a Lisp, allows a lot of stuff to be done in user land so I don't perceive this as a problem (maybe even a feature), especially with the "promise" of non-breakage.

simongray13:06:22

Clojure the project is, but the Lispiness of Clojure the language makes extending it quite trivial and there is no such nonsense as found in Elm such as forbidding unsanctioned interop with the host platform - rather the opposite: interop is touted as a major strength of CLJS.

simongray13:06:58

@borkdude I believe @tomaz.bracic is talking about stuff like this: "Why I'm Leaving Elm | Hacker News" https://news.ycombinator.com/item?id=22821447

Tomaz Bracic11:06:26

i will have a heavy websocket based app. Elm removed support for those with 0.19 version... so you have to use ports now

Tomaz Bracic11:06:51

I saw Sente library

borkdude11:06:02

Remove support for websockets...?

Tomaz Bracic11:06:18

yes, from the core. You deal with those via ports

Tomaz Bracic11:06:23

it works quite ok

Tomaz Bracic11:06:57

but yea... 🙂 this decoupling from JS brings a lot of great benefits but "problems" too.

borkdude11:06:30

That's at least one problem you don't have to worry about in CLJS land: removing = breaking and the Clojure community (inspired by its BDFL) has a culture of not breaking, only accreting.

Tomaz Bracic11:06:09

just need to figure it out what would be better. Still can't really shake off the feeling that the whole clojure(script) dev environment is huge... not really light. All those repl configurations, etc. Just my first impression

borkdude11:06:38

I'm sure there are starter templates for reagent apps, etc

borkdude11:06:04

Perhaps you could look at https://luminusweb.com/ which is a full stack tutorial / template

Tomaz Bracic11:06:25

based on the fact... that I just stepped into this

borkdude11:06:30

@tomaz.bracic FWIW, I don't even use a REPL for my (browser) CLJS projects, I just rely on hot-reloading

☝️ 2
Tomaz Bracic11:06:19

With Elm ... I saw the opportunity to have a toolset / environment that would allow me build the project and deploy it with confidence that there will probably not be any issues. And even more important... have this environment that would allow me ... to come back to the project after a year (for example) ... and change few things... and not break them. And that it would still work.

Tomaz Bracic11:06:37

And this is a huge benefit for me as being a solo freelancer

borkdude11:06:02

@tomaz.bracic You can have the same with CLJS but you will have to figure out the toolset you like in the beginning. But once you have something working, you can re-use this for years on end.

Tomaz Bracic11:06:37

i guess this is true only if you keep the distance with NPM and js libraries / packages, rigth?

borkdude11:06:06

yes, CLJS doesn't have any tricks to magically manage breakage from the JS ecosystem

borkdude11:06:36

the policy in the app I've been working on was for a long time: don't use NPM, don't use JS libs, unless you really really have to ;)

💥 2
raspasov21:06:53

That’s a GOOD policy!

👍 2
Tomaz Bracic11:06:35

what would you say is "the go to" library in clojurescript for websockets? I was actually searching for something that would allow me to use Phoenix Channels with ease... but not sure.

borkdude11:06:20

In the app I'm working on I just use direct JS interop. Browsers have support for websockets, why use a library?

borkdude11:06:24

I think this is all the code that we have:

(defn websocket! []
  (let [ws (js/WebSocket. ws-url)]
    (set! (.-onopen ws) (fn [_] (reset! websocket-error-count 0)))
    (set! (.-onmessage ws) (fn [ev] (on-message ws (read-string (.-data ev)) sse-fallback)))
    (set! (.-onerror ws) (fn [_]
                           (swap! websocket-error-count inc)
                           (reconnect!)))))

Tomaz Bracic11:06:43

by default... if you use phoenix templates... you get provided phoenix.js which is official js library. That handles all sorts of high level / abstract actions... towards channels i assume

borkdude11:06:00

ok, in that case, you could interop with that JS library

borkdude11:06:27

just use it. no need for writing a CLJS wrapper library. Interop with the host ecosystem is a feature of Clojure.

Tomaz Bracic11:06:27

aha... so without Sente or something like that?

borkdude11:06:27

sente is a library which probably does something similar to your phoenix stuff: it tries to make integration between server and client easier. but if your server is already managing websockets, and it also provides this phoenix.js thing, use that, don't use sente

Tomaz Bracic11:06:32

and with all those new terms... Leiningen, Shadow-cljs.... for purely closurescript project , a newbie ... and all that ... what would you suggest? Reagent and Re-freame did promise a lot, when I was looking at that

Tomaz Bracic11:06:03

what kind of "stack" would help me progress

borkdude11:06:10

if you're only doing a CLJS project, no server side, then only shadow-cljs is what I would advise

Tomaz Bracic11:06:35

yes, only CLJS

borkdude11:06:31

and try to make it work.

Tomaz Bracic11:06:32

One of the things I noticed... I must admit that I am really pleasantly surprised with the fact that you are so helpful

Tomaz Bracic11:06:38

I really appreciate

Tomaz Bracic11:06:02

this can be really important for people like me... checking this new world of Clojure(script)

Tomaz Bracic11:06:03

thanks!

👍 18
rich 2
Jakub Šťastný16:06:56

Hey guys, I've been watching many Clojure talks and I would really love to use Clojure, but I can't stand the bloody JVM. And so my first question is: how does CLJS compare to CL? I know it has differences, I read a bit about it, but I'd like to hear real-world experiences, rather than just theory. Of course it doesn't have Java interop, but I don't care. It's got JS interop, that sounds perfect for me. Seems to me CLJS is primarily used with Reagent/Reframe to build web apps, but do people use it to build non-web things much at all? Such as that GH repo contains the CLJS code whereas the NPM package just includes the compiled JS? Thanks in advance for any insights 🙏:skin-tone-3:

Jakub Šťastný16:06:48

If anyone's interested what's my beef with JVM: 1. The startup time is killing me, I code on a free AWS EC2 instance, so it's really not fast. I'm used to MRI Ruby/Node.js startup times and would be aiming for something like that. I know that you should keep the REPL running and load your file into it, i. e. using Emacs/Cider and that's all good, but that only works during the development. 2. Secondly, you cannot pack it into a small Docker image, since one has to include the whole JRE, which takes 175MB using the openjdk-11-jre-headless package.) 3. Stack traces. Anyway, matter of personal preferences I suppose.

borkdude16:06:36

CL = Common Lisp? Or did you mean clojure?

borkdude16:06:10

What do you develop, what kind of apps?

borkdude16:06:01

The biggest difference between CLJS and Clojure is its host platform obviously. JS doesn't have threads, everything is callback-based, but in Clojure you can do true concurrency, blocking, etc.

borkdude16:06:50

Also Clojure is more dynamic, because you always bring the compiler with you into your application, whereas with CLJS it's not available in your build (in the mainstream case).

borkdude16:06:37

If startup time is killing you, then take a look at graalvm, which can compile your entire app into a single binary with instant startup time.

borkdude16:06:01

For scripting (in the sense of bash build scripts) you can use babashka, which is a Clojure interpreter with instant startup. It's not a full replacement, but covers what you typically do when writing scripts around your main application.

Apple16:06:37

Do development on a performant machine and deploy the production code to AWS. Or check the oracle cloud which offers free VM and the ARM ones are pretty good. 4cpu 24G ram. Slow JVM startup can be mitigated with 'reloadable' workflow and REPL. Check https://www.youtube.com/watch?v=gIoadGfm5T8, https://pragprog.com/titles/dswdcloj3/web-development-with-clojure-third-edition/, https://luminusweb.com/docs/repl.html Docker: someone reported 42MB here https://stackoverflow.com/questions/40958062/how-to-reduce-my-java-gradle-docker-image-size. Or do you consider graalvm https://www.graalvm.org/reference-manual/native-image/? (I have no production experience)

emccue16:06:53

As far as the whole JRE thing goes - you can make a reduced JRE using the new module system stuff, so its not a huge blocker https://openjdk.java.net/jeps/282

emccue16:06:34

generally though, while some people do use clojurescript on the backend it is probably one of the least common uses of clojure

emccue16:06:53

that being said, it works. idk what the standard is nowadays, but i remember this https://github.com/anmonteiro/lumo

Jakub Šťastný17:06:43

@borkdude sorry, I meant CL = Clojure. I mostly develop system utilities (out of my work that is), so nothing long-running. Interesting about GraalVM, I didn't know I can do that! Single binary with instant startup time would definitely solve my pain points there, I'll check it out. Babashka also sounds cool 😎

Jakub Šťastný17:06:36

@UP82LQR9N thanks, will take a look. 24GB RAM/4 CPU free? Not sure if I believe it :) I will check though. 42 MB Docker image would be great, I will check GraalVM then!

Jakub Šťastný17:06:53

@U3JH98J4R interesting, I wasn't aware of the module system either, cheers!

Jakub Šťastný17:06:26

OK, so what seems to be the consensus is that very few people run Clojure (ClojureScript that is) on the server on one of the JS platforms such as Node.js, but rather everyone seems to prefer JVM (or GraalVM). I will check that out then. What you guys said about GraalVM does sound promising!

borkdude17:06:15

There is also a #graalvm channel. Here is a small tutorial. https://github.com/borkdude/jayfu The talk about this project will come online soon.

👍 2
Jakub Šťastný17:06:46

Cool @borkdude, I'm checking it out.

valtteri18:06:57

For system utils Babashka sounds like a perfect fit. However there’s nothing wrong going with Node & CLJS either. Especially if you’re already familiar with the Node & npm ecosystem. I’ve done a few Node projects with CLJS and deployed them to AWS Lambda.

quoll18:06:24

Before Babashka I actually did a lot of tools using ClojureScript on node. I still use a few of them. I’ve had to wrap a few things (like I/O), but it’s a nice platform to use. And the cljs repl is easy to start up now GraalVM is nice, but I would prefer to use it for large projects only (which I’ve only just started doing), because the compile time is extremely frustrating.

Jakub Šťastný13:06:06

Hey @U6N4HSMFW and @U051N6TTC, thanks for the feedback. I'm currently exploring GraalVM (it has both startup time and Docker image size sorted, which is really important to me) and babashka, which looks really amazing and quite what I was after. I suppose I'd be using both. I'll definitely come back to Clojurescript for frontend dev. And so finally I can switch to Clojure now, good times!

🎉 12
myguidingstar19:06:04

@U024VBA4FD5 I also write system utils with cljs + quickjs. No access to nodejs ecosystem but really low memory.

markbastian18:06:35

Hey all, js interop question here. I am trying to understand how to invoke a js function that looks like this.load.image('imagekey', 'assets/pics/imagename.png');in Clojurescript. For example, https://github.com/photonstorm/phaser3-examples/blob/master/public/src/textures/get%20pixels.js#L14-L17 from the http://phaser.io project. I’ve discovered http://clojurescriptmadeeasy.com/blog/how-to-use-this-in-cljs.html to get at this, but I’m still stumped on the rest. I’ve tried (.image (.load this) key path) as well as (.image (.-load this) key path) but to no avail. I am sure it’s something simple. Any ideas?

p-himik18:06:56

(.image (.-load this) key path) will work only if this.load.image does not need this (which is this.load for it). If it does need it, you will have to find the function first.

p-himik18:06:58

(let [load (.-load this)
      image (.bind (.-image load) load)]
  (image key path))

🐝 2
markbastian02:06:55

I tried that fragment and I think this isn’t what the docs seem to indicate. I think it is supposed to be a Phaser game object, but it is a Window. I tried this and I am still getting some interesting results. I can invoke

(this-as this
  this)
in a cljs REPL. This is what shows it is a window. If I log it I can do some inspection in the Chrome dev tooling. Looks like it has a Phaser object. Based on that, I can do
(this-as this
  (-> this .-Phaser .-Loader))
and that produces a js object that I assume is a loader. I still can’t seem to locate a .image method. Any ideas?

markbastian02:06:29

For reference, another simple example I am trying to port is https://gist.github.com/photonstorm/46cb8fb4b19fc7717dcad514cdcec064.

p-himik11:06:53

Can't really tell what's going on. If you create a minimal reproducible example with the exact steps on how to build and run it, I could take a look.

markbastian15:06:47

Thanks. Yeah, I’ll try to do that and post it.

markbastian18:06:56

Ok, figured it out and wanted to close the loop. Your example,

(let [load (.-load this)
      image (.bind (.-image load) load)]
  (image key path))
does indeed work. I think my mistake was var-quoting the calling function. js interop is definitely different than Java. Anyways, thanks for the help!

👍 2
lgessler19:06:13

do I understand correctly that I should expect resolve to break when using advanced compilation? I would like to use resolve on symbols referring to clojurescript vars, e.g. (resolve '=), (resolve clojure.string/includes?) and it appears to work in my dev repl, but name mangling in advanced compilation will disrupt this, right?

thheller19:06:24

given that it can only resolve symbols statically it'll be fine in advanced. unlike CLJ the use of resolve in CLJS is pretty limited

lgessler19:06:26

ah, yes I see, it is expecting a symbol as input 😢 thanks, guess i'll have to find another way

thheller19:06:34

FWIW any kind of dynamic lookup will break in :advanced, unless all the things you look up are ^:export'd

borkdude19:06:09

maybe @lgessler can back up a bit and explain what problem he's trying to solve?

borkdude19:06:28

chances are that resolve isn't the right answer

lgessler19:06:30

the situation is that I have an AST describing a kind of query on entities, where one kind of AST node is a clojure function applied with args to that entity, e.g. ['= :my-entity-field "foo"] , which would result in the query interpreter executing (= (:my-entity-field entity) "foo")

thheller19:06:33

just using a multi-method, works great and is easily extensible

lgessler19:06:30

ooh that's very similar to what we have, except your code is much cleaner! thanks for mentioning it, good to know this design's been successfully used elsewhere 🙂

thheller19:06:44

yeah, the serializable part was very important and things work great. could use symbols too but I didn't want to deal with quoting 😉

lgessler19:06:14

yeah heh keywords might make things a little more comfortable

lgessler19:06:00

i want the AST to be trivially serializable which is why i'm not just putting the function in it directly

borkdude19:06:51

just use a hash-map of all the possible functions supported in your AST/DSL

👍 2
borkdude19:06:23

unless you need a full blown interpreter, you could also look at https://github.com/borkdude/sci

lgessler19:06:56

yeah, that's what I was thinking--set up a function registry mapping symbols to functions and populate it once by walking supported namespaces

borkdude19:06:02

advanced compilation will cut out functions that might be needed by your interpreter, so you need to hold on to them by some way anyway

👍 2
lgessler19:06:24

oh yeah good point

borkdude19:06:36

this is essentially what sci is also doing

lgessler19:06:45

my situation seems small enough that a handmade interpreter will probably do but i'll definitely keep sci in mind--could see this problem growing hairier. thanks @thheller and @borkdude 🙂

👍 2