Fork me on GitHub
#clojurescript
<
2023-03-26
>
Clojuri0an01:03:19

I would appreciate some help understanding how to interpret this clojurescript error, I don't understand what it is saying (see thread):

Clojuri0an01:03:03

-------------------------------------------------------------------------------
  51 | (defn stripe-checkout-form [] 
  52 |    (println "trying promise")                           
  53 |    (let [payment p/try (. stripe confirmCardPayment (client-secret {
----------^---------------------------------------------------------------------
Syntax error macroexpanding cljs.core/let.
Call to cljs.core/let did not conform to spec.
-- Spec failed --------------------

  ([... ... (.
             stripe
             confirmCardPayment
             (client-secret
              {:payment_method
               #object[cljs.tagged_literals.JSValue 0x36d3d9b3 "cljs.tagged_literals.JSValue@36d3d9b3"]})) ...] ... ...)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

should satisfy

  simple-symbol?

or

  vector?

or

  map?

lilactown01:03:10

just reading the line of code it's pointing at, it looks like you might have an uneven number of forms in your let binding

Clojuri0an01:03:29

Here's the full let bining

(let [payment p/try (. stripe confirmCardPayment (client-secret {
   :payment_method #js { 
   :card ^js (.getElement (elements CardElement))
   :billing_details #js {
   :name "Jane Smith"}}}))
    (p/catch :default e
     (prn :error e))]

Crispin01:03:31

payment=`p/try` and (. stripe confirmCardPayment (client-secret {... = ?

Clojuri0an01:03:04

Sorry for the ambiguity. p/try is actually trying a promise, kichen-async.promise as p

Crispin01:03:05

you are binding p/try to payment, and something to a full form. You can only bind to symbols, or destructuring forms

Crispin01:03:17

you probably meant to do

Crispin01:03:30

(let [payment (p/try (...

Crispin01:03:40

the parens around p/try

Clojuri0an02:03:42

This seems correct

Clojuri0an02:03:41

I am now receiving the even-number-of-forms error. I'll post the function as I'm a bit lost here

Crispin02:03:10

you will need your p/catch in the p/try form too, yeah?

👍 2
Clojuri0an02:03:10

(defn stripe-checkout-form [] 
   (println "trying promise")                           
   (let [payment (p/try (. stripe confirmCardPayment (client-secret {
   :payment_method #js { 
   :card ^js (.getElement (elements CardElement))
   :billing_details #js {
   :name "Cardholder Name"}}}))
    (p/catch :default e
     (prn :error e))   
    (if (.-error payment)
      (println "error in paying")      
      (println (.-message (.-error payment))))
       (when  (-> client-secret ^js (.-paymentIntent) (.-status) "succeeded"
        (println "payment successful"))))
         [:f> [:form {:on-submit payment}
          [stripe-card-input-section]
               [button {:style "button-basic"};; add disabled
                "Confirm order"]]]]))

Crispin02:03:25

not familiar with p/try p/catch, but just make sure let binding block is all paired up

lilactown02:03:33

your formatting is very bad, it makes it hard to read

Clojuri0an02:03:48

How should I format it?

Crispin02:03:03

its just lacking indentation

Crispin02:03:59

clojure
(defn stripe-checkout-form [] 
  (println "trying promise")                           
  (let [payment (p/try
                  (. stripe confirmCardPayment
                     (client-secret {:payment_method
                                     #js { 
                                          :card            ^js (.getElement (elements CardElement))
                                          :billing_details #js {:name "Cardholder Name"}}}))
                  (p/catch :default e
                    (prn :error e))   
                  (if (.-error payment)
                    (println "error in paying")      
                    (println (.-message (.-error payment))))
                  (when  (-> client-secret ^js (.-paymentIntent) (.-status) "succeeded"
                             (println "payment successful"))))
        [:f> [:form {:on-submit payment}
              [stripe-card-input-section]
              [button {:style "button-basic"} ;; add disabled
               "Confirm order"]]]]))

👍 1
lilactown02:03:08

it looks like the [:f> ,,,] is inside the let bindings vector. it should probably come after it

lilactown02:03:50

you can look at tools like cljfmt which will automatically format code for you

lilactown02:03:08

and clj-kondo will show you common syntax errors without having to compile it

Clojuri0an02:03:46

I thought that :f> needed to be within the let in order to call the assigned value of payment. Is this not true?

Clojuri0an02:03:55

My intention is to use let to define a variable with local scope. I used to nest (def inside of (defn but was told this is bad practice. I'm trying to define payment as a local variable, and then call payment within the scope of stripe-checkout-form. I'm a bit confused by the let errors as it's difficult for me to understand why it's asking for an even number of forms

lilactown02:03:24

(let [x 1
      y 2]
  (+ x y))
the expression needs to come after the vector of bindings

lilactown02:03:53

what you have right now is

(let [x 1
      y 2
      (+ x y)])
which is incorrect

Clojuri0an02:03:49

Ty, fixed. It's saying that it doesn't conform to spec as the [payment] vector has an uneven number of forms. It's difficult for me to understand this because I usually am not thinking of my variable definitions in terms of the number of forms

Clojuri0an02:03:08

What I see when I read the code is a variable definition for payment, that is the result of a process function

Clojuri0an02:03:47

I don't really understand what a form is, and I don't understand why there needs to be an even number of them. The closest understanding I have is thinking of a form like a key value pair, and even-ness is needed to make sure that there isn't a mistake. I'm not sure where to look in the documentation for an answer that doesn't raise many more questions or references to other concepts that I am unfamiliar with.

lilactown02:03:29

"form" is a word that means a clojure syntax tree. so 1 is a form, + is a form and (+ 1 2) is also a form [x 1 y 2] is a form and (let [x 1 y 2] (+ x y)) is too. when it says "an even number of forms" it is trying to tell you that the binding vector should have pairs of forms inside of the [] brackets. so that it is like key value pairs. if you format the binding vector where each pair is on a new line, the left hand side should be forms that are simple symbols like x and payment and the right hand side are any form that returns what you want to bind the symbol to

👍 2
skylize19:03:05

> How should I format it? > I suggest you let your editor auto-format for you. Every major editor has a way to use cljfmt or something that applies a similar formatting. For example Calva automatically applies formating to Clojure files as soon as you install it in VS Code. The "correct" format will work its way into your brain rather quickly when reading your own code in that shape. You can help that along by occasionally writing small bits of code (such as a tiny snippet in Slack) with a conscious effort to replicate what the editor does. After getting comfortable with formating, though, I generally let the Calva do the work and copy-pasta to Slack, unless I'm responding on my phone or only writing 1-2 lines of code. (Using the editor also helps ensure balanced parens/brackets, and runs clj-kondo to catch some bugs in my likely-untested code, before I post it for the world to see.) Also a good idea to read through https://guide.clojure.style/. There are few points that might draw up some debate (e.g. the 80 character limit suggestion). But most of what it says is generally accepted as the way Clojurians expect their code to look.

hifumi12305:03:09

Is it possible to use a js map literal in a macro? Or is cljs.core/js-obj the only viable way to statically embed a js map in code?

thheller07:03:15

see cljs.tagged-literals/JSValue. your macro can return that

M J10:03:03

Hello, I am looking to have an automatic scroll on a page. When I click on the bottom 10% of the page, it scroll down another 20% to show me the rest of the questions. Any idea how I can do that?

p-himik10:03:05

I'd say you should definitely start with looking up solutions for that in JS, and then simply adapt one of such solutions to CLJS.

M J10:03:45

I tried using this and its not working (js/scrollBy 0 100)

M J10:03:33

i want an on-click function, not css

p-himik10:03:56

Have you read the documentation for scrollBy?

p-himik10:03:44

Oh, my bad - it's also a part of the window, so it doesn't require an element.

p-himik11:03:01

In any case, it works fine for me - when there are scroll bars due to the <html> being too tall. Perhaps it didn't work in your case because your <html> is exactly the height of the window and some inner element must be scrolled instead.

p-himik11:03:29

(Ironic how the MDN page for scrollBy actually has badly positioned content so scrolling is out of whack)

M J11:03:45

oh true, the window is fixed, and the inner element in the body is what i want to scroll

M J11:03:12

nope its still not working, with window being scrollable

p-himik11:03:09

Can't add anything new without an MRE.

p-himik11:03:16

Minimal Reproducible Example.

skylize19:03:43

That ^ means put together a complete code sample showing the problem, which someone else can use to reproduce your results, stripping out as much irrelevant code and data as you possibly can. Linking to a public git repo of your reproduction is ideal.

kennytilton12:03:32

I am taking a bunch of CSS and bringing it into in-line, CLJS maps. The translation from "color: #f00" to :color "#f00" is surprisingly tedious, so much so I ofetn just type it out. The Google offers nothing, ChatGPT suggest css-to-clj.core which I cannot find. Help! :ring_buoy:

p-himik12:03:05

The first step would be to question whether you really need to move CSS into CLJS in the first place. :) But if you really do need it, I'd probably use some JS tool for that and ran js->clj on its result later.

1
fjsousa12:03:48

do you use emacs mate? seems like the perfect use case for macros

👍 1
p-himik12:03:47

Macros would help with a rule-by-rule translation, and pretty much any reasonably "serious" editor supports them nowadays. I was assuming that the translation needs to be done in bulk.

kennytilton13:03:12

Emacs? Blimey, no! Text went out in the 80s! 🙂 But yeah, this is a good argument for emacs. I use IntelliJ/Cursive. "The first step would be to question whether you really need to move CSS into CLJS in the first place. :)" In a world where separation of concerns rules the land, @U2FRKM4TW, Web/MX prefers what seems to have become buzzworded as "co-location". aka, no action at a distance, please: the styling for the widget on which I am working is right in front of me, or a "find definition" away. I also look at the mad schemes for combining CSS via classes and think, "OK, there's a problem there somewhere." The power of CLJ to combine maps beats that, I think. A final issue is that Web/MX extends reactivity out to individual tag properties, and if we want to go crazy with an MX-powered style object, right out to separate formulas for separate style properties. So we will perforce have a mix of class-sorted CSS and dynamic in-line styling. Sure, dynamic class-juggling works, but now we are once-removed from the CSS that is generated. Web/MX is a thin wrapper, though, so class-driven CSS still works fine. Well, I'll do a few manually just to demo co-location, leave the rest in CSS files when I get bored with the typing. Thx all! 🙏

kennytilton13:03:40

This was fun (because I can tightly control the input):

(do
  (def cs "border-color: orange;\n  border-style: solid;\n  border-width: 2px;\n
    max-width: 40em;\n  min-width: 40em;\n  margin-top: 9px;")
  (defn css-to-map [css-string]
    (as-> css-string out
      (str/replace out #"\n" "")
      (str/split out #";")
      (map str/trim out)
      (map #(str/split % #":") out)
      (map (fn [[k v]]
             [(keyword k) (str/trim v)]) out)
      (into {} out)))
  (css-to-map cs))
=> {:border-color "orange",
 :border-style "solid",
 :border-width "2px",
 :max-width "40em",
 :min-width "40em",
 :margin-top "9px"}
I dealt with the \ns because the default IJ paste sticks those in. Thx for the push!

Matthew Downey14:03:52

I used to use vim macros for this stuff (and I know the emacs ones are even more powerful) but now I just use copilot. In this case I would probably paste the CSS in a comment, then add a comment saying the next line should be in EDN format, and then start typing the map, and it would figure out the rest. E.g.

; css
;color: #f00;
;font-size: 1em;
; same in Clojure
{:c ; <- from here it should auto-complete, just keep hitting tab
Then I delete the pasted-in CSS.

Matthew Downey14:03:35

But if it's a lot of CSS I see how this would also get tedious

kennytilton15:03:18

Oh, man. How many times have I thought, while mechanically transforming, "Geez, editor. Can't you see what I am doing?!". My first editor, DEC RSTS/E "edt" had a macro capability involving watching my typing and then being able to replay it. But it was not really "watch", I had to type the sequence without feedback, and hope I did not miss a step. It took weeks of reluctant practice, but eventually I could knock off an elaborate sequence on the fly and complete an onerous editing task painlessly. I'll keep co-pilot in mind. Thx! 🙏

phill20:03:07

The tension between CSS locations optimal for various purposes still wants proper resolution. Indeed there must already be a body of philosophy on this subject. Because when the server is run with prudence, you have either to declare "unsafe-inline" in the headers, which is undignified and a bore and a bother, or put CSS in a distinct resource, which is impractical and a bother and a bore. Meanwhile, there is CSS that could be called "thematic", with which the fortunate programmer receives "help" whose talents, though numerous, might be at sea amidst the code, vs CSS that could be called "operative", which should not under any circumstances be where the "help" can mess it up. 🙂

skylize20:03:17

> I also look at the mad schemes for combining CSS via classes and think, "OK, there's a problem there somewhere." The power of CLJ to combine maps beats that, I think. > You might also want to be aware that classes apparently show a meaningful performance improvement over inline styles. I have no idea how big the difference is, but it's enough that styled-components (and its biggest competitors) generates classes in the background and attaches classnames instead of styles to the components. (Perhaps it only matters if you can find shared properties to aggregate into a class? Or maybe classes follow a simpler code path due to legacy? I have no idea.)

reefersleep12:03:43

@U2FRKM4TW as an avid Vim macro user, I tried using the macros in IntelliJ, and as I recall, they were incredibly slow.

kennytilton13:03:56

Come to think of it, I did record a few macros when I embedded some raw HTML and wanted tp wrap things in sth like <code></code>. I forget the deets, but I recorded two different approaches, and one of them was indeed slow, the other quite zippy. But, yeah, playing back keystrokes as slow as they might be typed was fun to watch but quite a time-waster.

Clojuri0an19:03:05

My object is printing as a promise instead of as the object I want. Any idea how to access the object?

rickheere19:03:55

Put an @ in front of it?

Clojuri0an19:03:23

This is called dereffing right? Returns error ''No protocol method IDeref.-deref defined for type object: [object Promise]''

rickheere19:03:21

Ah, ok, then we are probably dealing with a normal js promise. Maybe just (.then the-promise #(console.log %))

rickheere19:03:19

But when dealing with promises I always use this fantastic library https://github.com/funcool/promesa

🔥 6
Clojuri0an19:03:08

I think I'll use this, I was using kitchen-async and I think I don't know how to use it correctly. Would you mind explaining what derefing means in the context of promises or pointing me to where to read about this? Thanks for the suggestio

Clojuri0an19:03:54

Also I don't understand the code example.

(require '[promesa.core :as p])

(->> (p/promise 1)
     (p/map inc)
     (deref)
;; => 2                     
When I read this I see anonymous function, bind the function to 1, map it to inc, then ...? ... convert the form to its syntax tree?

Clojuri0an19:03:13

oh wait just found their documentation, sorry didn't see originally @ https://funcool.github.io/promesa/latest/

rickheere19:03:27

Great. Deref in my mind means something like, give me the actual value that is inside. So, promises are meant to be a variable that you can pass around but it is not sure if the value is already available or not. So the value that is in the variable is always the promise. But when you want to get the actual value you have to ask the promise to give you the value inside (if it is already available. In normal js you do a then or the more fancy await. In clojurescript there is a special "thing" for this called deref, it let's you wait for a value to be there and when it's there it wil continue and give you the value, kind of like await.

rickheere19:03:52

I hope I got that right, I'm not the biggest clojure stare around.

Clojuri0an20:03:11

Ah I see, @foo is just a way of writing (deref foo) (it's a macro expression?)? Can it be helpful to think of this like a pointer or is that off?

p-himik20:03:31

Definitely off. In CLJ, deref can block, although it cannot in CLJS.

p-himik20:03:59

It's more of a getter with a well-known semantics in specific contexts.

👍 1
rickheere20:03:50

Nice, thanks

hifumi12321:03:38

Be careful with using Promesa. The example code in the README does not work on CLJS, and in general the documentation does not make clear what you can and can’t do in CLJS. Every time I’ve tried using it in CLJS I’ve hit dead ends, so I just use JS interop directly whenever functions return promises

1
p-himik21:03:16

Same, JS interop with promises has never failed me.

1
Clojuri0an22:03:08

I will transition to vanilla promises given that what I am trying to do is quite simple so there isn't much need to make things semantically minimal

hifumi12322:03:37

The other nice part about JS interop for promises is that it’s always with you in CLJS, and it’s already fairly minimal thanks to various syntax Clojure offers OOTB. The pattern is almost always

(-> (function-returning-promise)
    (.then  #(do something with result))
    (.catch #(do something with error)))

skylize20:03:58

Promesa makes Promises "feel like Clojure" a bit better and optionally supports a bind op in place of then that correctly follows monad laws. And it works in Clojure too, if you want to bring the Promise model back with you to the Java world, adding some thread-related features that don't make sense in JS.

Clojuri0an22:03:23

I'm being told that a js function I know exists does not exist. It is from a package installed with npm. When I'm using emacs, I can lookup what elisp functions are available and frequently do this if I'm given an error of this kind occurs in my emacs window manager. I don't have my clojurescript IDE (well) setup yet in emacs, is there a way without an IDE to lookup whether a function exists along with its documentation? If not I'll probably have to stop putting it off and finally set up my tooling correctly

Clojuri0an22:03:54

I decided I really need to setup my IDE correctly at this point since my project is getting slightly complex and not having it configured is causing time sink. Still, if there's any way to do this from the REPL please let me know

p-himik09:03:18

> It is from a package installed with npm I'd use regular grep if you don't have an instance of the type that must have that function. All depends on what you mean by "available".

skylize19:03:43

That ^ means put together a complete code sample showing the problem, which someone else can use to reproduce your results, stripping out as much irrelevant code and data as you possibly can. Linking to a public git repo of your reproduction is ideal.