Fork me on GitHub
#shadow-cljs
<
2023-01-19
>
andrewzhurov11:01:18

While watching compiler returns :infer-warning errors, I would assume setting :compiler-options {:warnings {:infer-warning false}} would communicate that these warnings are not to be shown, but, to my frustration, it doesn't work this way. :thinking_face: Is there a way to hide :infer-warnings? (Aside from fixing them 😄 They happen to be quite some) The build description is

:test {:target          :node-test
         :output-to       "static/tests.js"
         :closure-defines {frontend.util/NODETEST true}
         :devtools        {:enabled false :preloads [hashp.core]}
         ;; disable :static-fns to allow for with-redefs and repl development
         :compiler-options {:static-fns false
                            :warnings {:fn-deprecated false
                                       :redef false
                                       :infer-warning false}}
         :main            frontend.test.frontend-node-test-runner/main}

thheller13:01:28

well the ideal thing is to fix them

thheller13:01:01

you can disable them via :compiler-options {:infer-externs false}, not inside :warnings. true also disables them. I know its weird. if you are sure you don't need any externs in an ns you can also do (set! *warn-on-infer* false) in the file after the ns

thheller14:01:26

do you have some examples for me? maybe the recent changes I did now show too many warnings?

andrewzhurov07:01:47

Thank you for getting me a helping hand so quickly. 🙂 Disabling them via :compiler-options {:infer-externs false} works like a charm! Now when no infer warnings are shown it'll be super comfy to notice other kind of warnings (like :undeclared-var etc.) ^.^ (which show up before :infer-warnings), previously you'd need to scroll up through those 28 infer warns to get to them, which, interestingly, you get used to after a while and stop to notice 😄, but still I've been getting infer warnings while watching :testhttps://github.com/logseq/logseq/blob/9f18a707aa2f4704f7ab126e9a9c49ba9bc20ff6/shadow-cljs.edn#L70, via clj -M:test watch test, as suggested in https://github.com/logseq/logseq/blob/master/docs/dev-practices.md#focus-tests. Log's attached. Hope this can be of help. 🙂 And thanks again for the tip! A solid bump to devUX (Btw, I think it's a good thing that compiler thoroughly warns about infers, these warns are there for a reason, fixing them would be nice indeed)

thheller07:01:30

those all look like legit warnings to me that should be properly fixed

👍 2
thheller07:01:48

ignoring them will inevitably lead to problems if you ever do a :advanced build

thheller07:01:54

(fn [a b] (.multipliedBy a b)) just becomes (fn [^js a b] (.multipliedBy a b)) and the warning is gone

andrewzhurov08:01:51

Ah, and building without infers lead to a broken build (although I thought it would only break with :advanced, and here we've got :none , it seems, huh)

ReferenceError: js is not defined
    at /home/user1/gits/logseq/logseq/.shadow-cljs/builds/test/dev/out/cljs-runtime/malli.util.js:1834:1
    at global.SHADOW_IMPORT (/home/user1/gits/logseq/logseq/static/tests.js:64:44)
    at /home/user1/gits/logseq/logseq/static/tests.js:1764:1
    at Object.<anonymous> (/home/user1/gits/logseq/logseq/static/tests.js:2134:3)
    at Module._compile (node:internal/modules/cjs/loader:1218:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1272:10)
    at Module.load (node:internal/modules/cjs/loader:1081:32)
    at Function.Module._load (node:internal/modules/cjs/loader:922:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:82:12)
    at node:internal/main/run_main_module:23:47

thheller08:01:52

I very much doubt this has anything to do with externs inference

thheller08:01:25

definitely not for :none, as no externs are ever needed

thheller08:01:42

you can set :infer-externs true, which will still do externs inference but never warn

andrewzhurov18:01:02

that makes sense, likely I messed up something in code, sorry for the confusion, compiling now with both :infer-externs set to true and false yields no warns and the build works alright

andrewzhurov18:01:35

thanks for introducing to what :infer-externs true does, logic behind true false keywords clicked 🙂

michihuber15:01:30

I can't seem to type-hint externs properly inside of go-blocks, in some cases. any idea?

michihuber15:01:39

------ WARNING #3 - :infer-warning ---------------------------------------------
 File: /Users/m/code/arcstudiopro/src/app/system/spellcheck.cljs:180:3
--------------------------------------------------------------------------------
 177 | ;;       (outbox [[:spellcheck/learned params]]))))
 178 | ;;
 179 | (defn learn [{:keys [!dict ^js FS] :as system} params outbox]
 180 |   (go
---------^----------------------------------------------------------------------
 Cannot infer target type in expression (. inst_10004 (appendFile inst_10024 inst_10025))
--------------------------------------------------------------------------------
 181 |     (let [^js FS+ (.-promises FS)
 182 |           ^js dict @!dict
 183 |           word (:word params)]
 184 |       (<p! (.add dict word))
--------------------------------------------------------------------------------

michihuber15:01:08

(defn learn [{:keys [!dict ^js FS] :as system} params outbox]
  (go
    (let [^js FS+ (.-promises FS)
          ^js dict @!dict
          word (:word params)]
      (<p! (.add dict word))
      (<p! (.appendFile FS+ (user-dic-path system) (str word "\n")))
      (outbox [[:spellcheck/learned params]]))))

michihuber15:01:10

oddly, dict can be inferred as externable, but FS+ can't? or am I misreading the situation?

michihuber15:01:06

I tried moving the system destructuring inside the go-block, which didn't help

thheller15:01:26

hmm yeah typehints in go are dropped by go unfortunately

thheller15:01:12

may I suggest not using go in the above example at all? if all you do is await a promise then it is absolute overkill to do so

thheller15:01:38

(defn learn [{:keys [!dict ^js FS] :as system} params outbox]
  (let [^js FS+ (.-promises FS)
        ^js dict @!dict
        word (:word params)]
    (-> (.add dict word)
        (.then #(.appendFile FS+ (user-dic-path system) (str word "\n")))
        (.then #(outbox [[:spellcheck/learned params]])))))

thheller15:01:59

it'll also be like 1/100th of the generated code size 😉

thheller15:01:40

.add just doesn't require externs while .appendFile does

michihuber16:01:05

yeah, makes sense. I do have one other situation where awaits are chained, though. any workaround for go blocks in such situations, or just deal nested thens?

thheller17:01:51

(defn learn [{:keys [!dict ^js FS] :as system} params outbox]
  (let [^js FS+ (.-promises FS)
        ^js dict @!dict
        word (:word params)]
    (js-await [_ (.add dict word)]
      (js-await [_ (.appendFile FS+ (user-dic-path system) (str word "\n"))]
        (outbox [[:spellcheck/learned params]])))))

thheller17:01:24

note that they are already chained above with .then, so not sure what you mean

skylize19:01:31

> or just deal nested thens I think it might help to see Promises at work from the JavaScript side of things. Not totally clear if the phrase "nested then" even has any real meaning in JavaScript.

myPromise.then(x =>
  x.then(y =>
    y.then(z =>
      f(z))))
is fully equivalent to
myPromise.then(f)
because, as each intermediate Promise resolves, the result is automatically unfolded into any Promise that contains it. For the more complicated case, where additional work is done at each step, you still do not need to actively handle nesting levels.
myPromise.then(x =>
  transformX(x)
    .then(y =>
      transformY(y)
        .then(z =>
          transformZ(z))))
can be collapsed to the flatter and simpler
myPromise
  .then(transformX)
  .then(transformY)
  .then(transformZ)
without changing the behavior. With this flattened version, you could choose to replace any or all of the transform functions with a function returning a simple value instead of a Promise, with no notable impact. await is just syntactic sugar for forcefully sequential (i.e. not concurrent with other Promises in the same block) .then calls.
async function () {
  const x = await promise1
  const y = await promise2
  const z = await promise3
  return {x, y, z}
}
is strictly equivalent to
promise1.then(x =>
  promise2.then(y =>
    promise3.then(z =>
      {x, y, z})))
The nesting here comes from using closures as a simple way to capture values, because a Promise handler function only takes a single input. We could flatten out of the nesting by jumping through some hoops to pass each partial result through or around the next promise. If being less strict about sequential processing is okay, then this can also be considered equivalent to the await example,
Promise.all([promise1, promise2, promise3])
  .then(
    ([x, y, z]) => {x, y, z})
with probable improved performance due to letting the 3 input promises resolve concurrently (but still synchronizing the time factor before passing the combined result on to the next consumer).

🙏 2
rschmukler22:01:55

Is there any way to overwrite arglists in a macro that produces a variadic function? Eg.

(defmacro my-defn
  [name bindings & body]
  `(defn ~name
     {:arglists '([~'z])}
     ~bindings
     ~@body))
In Cljs:
(my-defn foo [x]
  (+ x x))
(:arglists (meta #'foo))
;; => '([z]) - works as expected

(my-defn foo-variadic [& xs]
  (apply + xs))
(:arglists (meta #'foo-variadic))
;; => '([& xs]) - incorrect!

rschmukler22:01:31

Looks like https://clojure.atlassian.net/browse/CLJS-2351 might encompass it as well as the subsequent discussion in https://ask.clojure.org/index.php/6265/setting-arglists-metadata-when-vararg-is-present To answer @U05224H0W's question as to why it is useful... I have an https://github.com/teknql/plasma which exposes a macro that functions as a custom defn (`defhandler`). In clojure land it is basically just defn but in the front-end it packages up all the arguments into a vector and sends them to an API endpoint that calls the function on the backend and returns the results and wraps it in a JS promise. The function generated on the clojurescript side is variadic (easier to package up in the vector that performs the RPC) but since tooling (cider in this case) uses the :arglists for auto-completion / docs I would like to be able to keep the original version. I hadn't found the thread before I asked here so I'll look into a non-variadic way

p-himik04:01:47

Please don't cross-post. When you aren't sure where to post something, choose the most specific channel first. Given that you've provided more details in this particular thread and seem to have found a way forward, I'm removing your message in #C03S1L9DN.

thheller07:01:56

#C07UQ678E would be the proper channel for this. this is not something I can do anything about from the shadow-cljs side, needs to be done in the CLJS compiler itself.