This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-08-31
Channels
- # announcements (3)
- # aws-lambda (1)
- # babashka (122)
- # beginners (241)
- # calva (28)
- # cider (7)
- # clara (7)
- # clj-kondo (43)
- # clojars (5)
- # clojure (326)
- # clojure-europe (60)
- # clojure-italy (2)
- # clojure-nl (4)
- # clojure-spec (21)
- # clojure-uk (4)
- # clojurescript (162)
- # cursive (30)
- # datomic (3)
- # editors (5)
- # emacs (4)
- # figwheel-main (1)
- # fulcro (24)
- # gratitude (3)
- # helix (7)
- # honeysql (20)
- # improve-getting-started (1)
- # introduce-yourself (11)
- # jobs (3)
- # joker (2)
- # kaocha (15)
- # lsp (21)
- # lumo (2)
- # meander (3)
- # off-topic (34)
- # re-frame (6)
- # reagent (1)
- # releases (4)
- # rum (2)
- # shadow-cljs (37)
- # spacemacs (16)
- # tools-deps (16)
- # vim (23)
- # xtdb (32)
@here ^
@thomas611 I don't know what the latest state of electron is or what bundlers are supposed to do with the electron
package. it appears to fail because shadow-cljs maybe bundled it incorrectly. or something in your code uses it incorrectly. do you use existsSync
somwhere in your code? can't see your code
Uh, how are async fixtures supposed to work?
(use-fixtures :once
{:before (fn [] (async done (.destroy (pouchdb "test") #(println "before" (done)))))
:after (fn [] (async done (.destroy (pouchdb "test") done)))})
(deftest test-put
(async done
(println "test") [...]
Result
Testing hipflask-test
test
before #object[cljs.core.async.impl.channels.ManyToManyChannel]
So seems like the :before fixture is completing after the test
But the docs seem to suggest this is supposed to work correctly: https://clojurescript.org/tools/testing#async-fixturesDoes someone have an example how the :target :bundle
CLJS works for single node scripts?
(context: https://clojurescript.org/news/2020-04-24-bundle-target)
Uhm... dumb question but what's the every?
equivalent of alts!
, I want to wait for all the results. Seems like it should be super obvious but I fail to find the correct search terms apparently.
I think you just loop thru all the channels and add each result to a collection of results. Each channel will block until it has a result, but since you’re waiting until you have all the results anyway, it doesn’t matter.
Or I should say, “park” not “block”.
@dnolen I'm trying to reproduce the following use case using "normal" CLJS: term-size (the newest version) is an ES Module
@pepijndevos https://github.com/clojure/core.async/blob/master/src/main/clojure/cljs/core/async.cljs#L694
lol, so it's not possible to write a script using normal CLJS where you use the newest term-size
?
@borkdude maybe worth playing around w/ this https://www.npmjs.com/package/esm
@dnolen I'm already using dynamic import in nbb, and the require is looking like ["term-size$default :as term-size]
. I just want to know how this is normally done in CLJS to be compatible.
so any example that reproduces how to load (the newest) term-size in a CLJS node script is something I would look at
@borkdude so you're asking for more clarification - sorry I though this was a new question
but there's a tipping point now with .mjs
but as far as I can tell the end result is the same as it is for :bundle
since nbb is still young and I want to be as compatible as possible with CLJS, I can still make changes: this is why I want to know if there's any precedence here
w/ the caveat that we don't support ES6 import in CLJS and we aren't going to because of all the problems
it's my opinion that trying any attempt fit into the Node.js / JS style or reconcile these problems is ill-considered
oh I'm not fighting that opinion. but it seems I've got nothing to look at in terms of existing scripts that use ES Modules then. The JS churn just leaks through and there's nothing to do about it in terms of trying to be compatible with anything I guess?
every time I read something like this - https://gils-blog.tayar.org/posts/using-jsm-esm-in-nodejs-a-practical-guide-part-1
but suppose :bundle
worked for node scripts, how would the :require
for term-size look like
At least I've done my best to not invent something new, that's what I wanted to make sure
so clearly we have a thing w/o all these pointless differentiations for what might appear like different situations
the reason that (require [window$console ...])
doesn't work yet is because the symbol has to be resolveable as a namespace
I noticed that some libs even have two nested defaults... https://github.com/huygn/nbb-scripts/blob/e96cd2af1f7c13edf85a7f71084b713f6e582535/github.cljs#L5
@borkdude that does seem weird to me - you should probably at least try some web compatible case with :bundle
import * as is from 'ink-spinner'
console.log(Object.keys(is.default));
// prints default
is.default.default
is the actual thing you want to use from that lib`, the Spinner thing
if you do this:
import Spinner from 'ink-spinner'
console.log(Spinner);
then it prints:
{ default: [Function: Spinner] }
note that this happens specifically for this lib (and some others), for most libs there is no such nesting
exactly, I've just implemented $
similar to how CLJS does it: just look up the thing from the module (and any other properties like $default
and $foo.bar
)
I get an infinite re-render loop when i update the state in a child component. How do i fix this? Here is my app structure:
(defonce app-state (r/atom {:geolocation nil
:input {"age" 25
"net-worth" 200000
"monthly-income" 6500
"monthly-expenses" 2500
"investment-return" 8
"investment-tax" 0
"withdrawal-rate" 4}
:modal {:active? false}}))
(defn child [app-state]
(let [input (:input @app-state)
cashflows (:cashflows @app-state)
monthly-expenses (get input "monthly-expenses")
geolocation (:geolocation @app-state)
withdrawal-rate (/ (get input "withdrawal-rate") 100)
age (.parseInt js/Number (get input "age"))
cities-table-data (map #(city-table-data % geolocation cashflows withdrawal-rate) cities)
cheapest-city (first (sort (fn [city1 city2] (< (:fire-number city1) (:fire-number city2))) cities-table-data))]
(swap! app-state assoc-in [:a :path] cheapest-city) ;; This swap causes infinite re-renders
[:div "nothing"]))
(defn app []
[child app-state])
@simon.el.nahas Probably don't use app-state
as an argument
Just move the chepest-city
computation to the level where you already have the required data.
@U2FRKM4TW In this example where would you move the computation to?
(defn child []
(let [input (:input @app-state)
cashflows (:cashflows @app-state)
monthly-expenses (get input "monthly-expenses")
geolocation (:geolocation @app-state)
withdrawal-rate (/ (get input "withdrawal-rate") 100)
age (.parseInt js/Number (get input "age"))
cities-table-data (map #(city-table-data % geolocation cashflows withdrawal-rate) cities)
cheapest-city (first (sort (fn [city1 city2] (< (:fire-number city1) (:fire-number city2))) cities-table-data))]
(swap! app-state assoc-in [:a :path] cheapest-city)
[table cities-table-data]))
I have now tried to move the computation. I'm not sure where to, but at least this doesn't work.
(defn get-cities-table-data []
(let [input (:input @app-state)
cashflows (:cashflows @app-state)
geolocation (:geolocation @app-state)
withdrawal-rate (/ (get input "withdrawal-rate") 100)
cities-table-data (map #(city-table-data % geolocation cashflows withdrawal-rate) cities)]
cities-table-data))
(defn update-top-city! [cities-table-data]
(let [cheapest-city (first (sort (fn [city1 city2] (< (:fire-number city1) (:fire-number city2))) cities-table-data))]
(swap! app-state assoc-in [:a :path] cheapest-city)))
(defn child []
(let [cities-table-data (get-cities-table-data)]
(update-top-city! cities-table-data)
[:div "A component which uses " cities-table-data]))
> where you already have the required data
And you seem to already have all the data at the very top level, right where you have defined app-state
.
In case you change the input data somewhere, re-compute cheapest-city
right there. Never change the state in the render function (as opposed to event handlers).
Your code is still conceptually wrong. You should compute cheapest-city
right along with defonce app-state
.
And as borkdude as mentioned - if you find data dependency and state changes hard to manage, take a look at re-frame.
I'm not sure i understand. This doesn't fix it:
(defn cheapest-city [cities-table-data]
(first (sort (fn [city1 city2] (< (:fire-number city1) (:fire-number city2))) cities-table-data)))
(defn child []
(let [input (:input @app-state)
cashflows (:cashflows @app-state)
monthly-expenses (get input "monthly-expenses")
geolocation (:geolocation @app-state)
withdrawal-rate (/ (get input "withdrawal-rate") 100)
age (.parseInt js/Number (get input "age"))
cities-table-data (map #(city-table-data % geolocation cashflows withdrawal-rate) cities)
cheapest-city (cheapest-city cities-table-data)]
(swap! app-state assoc-in [:a :path] cheapest-city)
[:div "nothing"]))
perhaps calculate it before you store it in the state and then update the state with that atomically
or use something like re-frame where you can use subscriptions to derive state from the app state
Y'all got any more of those go gotchas? I'm getting Uncaught TypeError: p.then is not a function
when using <p!
but when I print the promise at the same spot it's definitely a promise. Actually, I commented out all the <p!
and it's still doing it! WTF?
Uncaught TypeError: p.then is not a function
cljs$core$async$interop$p__GT_c interop.cljs:19
switch__29745__auto__ main.js line 486 > eval line 775 > eval:10
Ok found it nvm
1. i'm not sure i understand the first approach you suggest. 2. I'm also considering re-frame, for my next refactor. But right now I just need to get the cheapest city.
@simon.el.nahas 1) Instead of storing state and then deriving data + storing that in the state you could do that in one go right?
so I would have another function like this:
(defn state-updater [old-state]
;...
new-state)
keep in mind the cities-table-data
is needed in a child of the child
(defn child []
(let [input (:input @app-state)
cashflows (:cashflows @app-state)
monthly-expenses (get input "monthly-expenses")
geolocation (:geolocation @app-state)
withdrawal-rate (/ (get input "withdrawal-rate") 100)
age (.parseInt js/Number (get input "age"))
cities-table-data (map #(city-table-data % geolocation cashflows withdrawal-rate) cities)
cheapest-city (cheapest-city cities-table-data)]
(swap! app-state assoc-in [:top-city] cheapest-city)
[:div "a table using " cities-table-data]))
@simon.el.nahas My idea was to calculate the cheapest city in state-updater
so you could just use (:top-city @app-state)
directly in your component without any calculations
yeah, re-frame is a little bit different in that the derived state is not in the app db
I have now tried to move the computation. I'm not sure where to, but at least this doesn't work.
(defn get-cities-table-data []
(let [input (:input @app-state)
cashflows (:cashflows @app-state)
geolocation (:geolocation @app-state)
withdrawal-rate (/ (get input "withdrawal-rate") 100)
cities-table-data (map #(city-table-data % geolocation cashflows withdrawal-rate) cities)]
cities-table-data))
(defn update-top-city! [cities-table-data]
(let [cheapest-city (first (sort (fn [city1 city2] (< (:fire-number city1) (:fire-number city2))) cities-table-data))]
(swap! app-state assoc-in [:a :path] cheapest-city)))
(defn child []
(let [cities-table-data (get-cities-table-data)]
(update-top-city! cities-table-data)
[:div "A component which uses " cities-table-data]))
Is there like an into
that deletes keys with nil
values? If not, what would be a reasonable way to write it? I guess just a copy of into
with something other than conj
If you have a sequence of pairs, maybe (remove (comp nil? second))
before your into?
You could also use (filter second)
, but that would fail if the value might be false
. So (filter (comp some? second))
would be better.
That doesn't do what I want. I want nil
to act like a dissoc on the map.
Yea I just copied into and replaced conj with my own function and it seems to work.
I'v just released my new library, which is an atom (or ratom!) backed by PouchDB. It's an atom that syncs, hopefully great for writing offline-first collaborative apps in Reagent, Rum, or other reactive libraries. Feedback appreciated. Now, on to actually refactoring my app to use it. https://github.com/NyanCAD/hipflask
Hello everyone, I have a question, I'm planning to build a new SaaS in September and I'd like it to be in ClojureScript. I just started JavaScript this year and don't really feel comfortable with it I'd really be better with ClojureScript. The fact is I bought a SaaS boilerplate in JavaScript to speed up everything, I have 2 options to use ClojureScript in addition to this boilerplate 1. Translate the full boilerplate (3000 lines) in ClojureScript 2. If there is way, extends the JS boiletplate with ClojureScript (is that even possible?) 3. Third option would be to begin with a ClojureScript SaaS boilerplate but does that even exist? I'm thinking of executing 1 ASAP, to me it'd take the full month to translate the boilerplate in ClojureScript but I think the time investment is worth it. Would you have an idea on how I could be faster? On the short term it'd make more senses to just use JavaScript with this boiletplate but on the long term I think I'd be faster using ClojureScript that's why it's so important to me to begin with ClojureScript. I'm open to any ideas or recommandations you could have, thanks by advance ! Bryce Tichit
Hey friends, having another interesting problem with a JS library import using shadow-cljs.. a lib called "timeago.js". I've imported it:
(:require ["timeago.js" :refer [format]])
as I would for an import like
import { format } from 'timeago.js';
I then try and use it with a mapped JS DATE like so:
(format (js/Date. (get note :updated-date))
And nothing.. I think maybe format has a collision?Oh, turns out I needed to :rename it like so:
["timeago.js" :rename {format ta-format}]
ANd that did the trickthat shouldn't be necessary? do you maybe have another format
in the scope shadowing the :refer
? (defn do-something [format] (format (js/Date. ...))
or so?
@tichit.bryce both 1) & 2) are possible - 2) seems faster since you can convert as you go
Could you send me some documentation that would talk about possibility number 2? Do you mean that I could write my CLJS code, compile it into javascript and from javascript use the compiled code? Thanks a lot for the answer !
You can just call the SaaS boilerplate, or pass it CLJS functions, though you will know more "interop" than most of us by the time you are done. 🙂 https://lwhorton.github.io/2018/10/20/clojurescript-interop-with-javascript.html is one resource, another is a bit older but still good: https://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/ Good luck with the project!
Thanks a lot for the information really helpful ! I think I will go with the translation of the boilerplate to CLJS, it's a better choice IMO and I will have a CLJS SaaS boilerplate then that I could reuse multiple times. Interop is going to add a linear cost with the size of the program, I don't want that. Better to pay fixed price right now and reduce complexity in the future
"Interop is going to add a linear cost with the size of the program" I do not think so. As a Common Lisper going back into the last century, because so few libraries were available, it was commonplace to use C libraries by writing just enough CL code using C FFI to "wrap" the C library in CL, and then Just Write CL(tm). We wrapped just the bits we needed, and we got on with the fun much more quickly, adding bits as we discovered the need. The most fun ever was doing this with OpenGL. Hell, I even did this with qooxdoo, a JS library, so I did not have to write (much) JS, HTML, or CSS. qooxdoo handled the latter two. This site is a Common Lisp application running on a server pushing JS/qooxdoo to the client. http://tiltonsalgebra.com/# So which will be faster? Translating 3K or wrapping it in a CLJS API?
And when it comes to the long view (I loved your analysis approach!) which makes you a more powerful Clojurian? A 3KLOC slog that makes one library accessible, or becoming a champ at interop (with JS anyway)? With your black belt in interop the world of JS is your oyster, not just one SaaS template. Remember, one of the main ideas behind even creating Clojure was the vast universe of Java libraries that would be just a bit of interop away. Have fun either way!
Thanks a lot for your feedback @U0PUGPSFR very interesting, your point of view is interesting. It's true that I'm unable to take the optimal decision because I'm only a beginner in Clojure/LISP. I need to play around with the interop first to understand everything but you are right, mastering interop is a powerful skill.
In the end I was just thinking, the boilerplate itself like any codebase contains a lot of functions that you would compose in the end at the top-level. In React the top Level would be index.js, what if I write the top level composition in CLJS and use interop to compose JavaScripts functions ? What I mean is that the boilerplate is a JavaScript tree, I'm proposing to only replace the head of the tree in CLJS and then connect that head to the rest of the tree (which is JS) using interop. This should work nope? It seems too easy, there may be mistakes in my reasoning. What do you think? Thanks a lot 🙏:skin-tone-2:
Anyways you convinced me that I should go the interop road, thanks a lot 😉
np! Hope you do not regret it! 🙂
I think as you work on the rest of the app you will naturally see what your wrapper will have to do with the JS code to get the job done. In some cases you will want to tweak the JS code to make the interop easier. In most cases not. You will quickly develop solid interop skills, then hide the interop in CLJS functions so you can forget interop: then the JS template will seem like a CLJS template.
You will also discover the wrapper can offer a better interface than the JS template, by automating things the JS template makes you work at. A trivial case when I wrapped OpenGL3 with Lisp was (struggling with memory) that there was some sequence of commands that had to be surrounded by calls to begin
and end
. Apparently this was a frequent source of bugs. In CL I just wrote a macro, with-begin-end
and never had to think about it.
That is quite typical of wrapping things in more powerful languages.
Happy coding.
Amazing @U0PUGPSFR I cannot wait to have my boilerplate ready with CLJS so I can build the whole app in CLJS. I've been coding since university and used a handful of language, everytime something felt wrong and I cannot be productive enough without ever knowing why. The discovery of LISP dialects and Clojure really changed my life, especially Clojure because it is highly functional programming oriented as well. s-expressions feel like the natural way of programming, I'm running out of money right now to build my SaaS but that's no problem I can easily spend 1 month to sharpen my CLJS stack. On the long term I believe I will have a big advantage 🙂
This community is really nice as well !
"I've been coding since university and used a handful of language, everytime something felt wrong and I cannot be productive enough without ever knowing why. The discovery of LISP ...." You may as well get started on your submission here: https://web.archive.org/web/20120106121645/http://wiki.alu.org/The_Road_To_Lisp_Survey
Very interesting but I cannot submit as this page is now offline 🙂
I think I will decide between both options by the end of this week
I understand the marshalling problem but don't know how big it would be
@dnolen (and @thheller):
I was able to require term-size and some other ES module using shadow's :esm
build type and it yields the same style of "requires" as nbb, so I'm even more confident now that I'm doing the right thing.
https://gist.github.com/borkdude/7e548f06fbefeb210f3fcf14eef019e0