sci 2022-09-27

alright what am I doing wrong here (clojurescript environment)

(ns demo.sidecar
  (:require [cljs.core.async :as a]
            [cljs.core.async.interop :as ai]
            [sci.core :as sci]))

(def ctx (sci/init {:classes {'js goog/global}
                    :namespaces
                    {'cljs.core.async
                     (sci/copy-ns
                      cljs.core.async
                      (sci/create-ns 'cljs.core.async))
                     'cljs.core.async.interop
                     (sci/copy-ns
                      cljs.core.async.interop
                      (sci/create-ns 'cljs.core.async.interop))}}))

(sci/eval-string*
 ctx
 (apply str
        '((ns gogosci.core
            (:require [cljs.core.async :as a]
                      [cljs.core.async.interop :as ai]))
          (a/go))))
;;=>
#error
 {:cause #error
          {:data {:column nil,
                  :file nil,
                  :line nil,
                  :phase "analysis",
                  :type :sci/error},
           :message "Could not resolve symbol: a/go"},
  :data
    {:column 86,
     :file nil,
     :line 1,
     :message "Could not resolve symbol: a/go",
     :phase "analysis",
     :sci.impl/callstack
       #object
        [cljs.core.Volatile
         {:val ({:column 86,
                 :file nil,
                 :line 1,
                 :ns #object [sci.lang.Namespace]})}],
     :type :sci/error},
  :message "Could not resolve symbol: a/go"}

the rest of core.async works, it's just not happy about the channels

ok think I have a workaround

Are you trying to use core.async with SCI?

in CLJS, I mean?

I have the most horrible hack

For CLJS, I usually recommend using the platform's promises in combination with promesa https://github.com/funcool/promesa There is a configuration here which you can directly use: https://github.com/babashka/sci.configs#funcoolpromesa

The core.async go macro is a bit too complicated for my taste to use in CLJS

but if you got it working, I'm interested :-)

using promesa would be the right thing. But why would you do the right thing when you could do something horrible like this:

(defmacro make-sci-macro-fn
  "makes a function with multi arrity dispatch and returns it.
   used to make a macro for sci
   expands to:
   (do (defn fn-name 
         ([a] (sym a))
         ([a b] (sym a b))
         ([a ... z] (sym a ... z))
         ...)
       fn-name)"
  [fn-name sym]
  (let [fn-name#             `~fn-name
        multi-dispatch-decl# (let [alpha "abcdefghijklmnopqrstuvwxyz"]
                               []
                               (loop [n   (dec (count alpha))
                                      res []]
                                 (if (zero? n)
                                   res
                                   (let [args    (mapv #(symbol (str %)) (drop-last n alpha))
                                         go-form (list* sym args)]
                                     (recur (dec n)
                                            (conj res
                                                  (list args go-form)))))))]

    `(do
       (def ^:sci/macro ~fn-name# (fn  ~@multi-dispatch-decl#))
       ~fn-name#)))

could you describe in words what you did there? :)

It expands to this:

(do (defn ^:sci/macro fn-name 
         ([a] (sym a))
         ([a b] (sym a b))
         ([a ... z] (sym a ... z))
         ...)
       fn-name)
which I used in the sci macro machinery

So I made a context

(def ctx (sci/init {:classes {'js goog/global}
                      :namespaces
                      {'cljs.core.async
                       (merge (sci/copy-ns
                               cljs.core.async
                               (sci/create-ns 'cljs.core.async))
                              {'go  (make-sci-macro-fn go a/go)})}}))

then tried it out:

(a/go
      (def data
        (a/<!
         (sci/eval-string*
          ctx
          (apply str
                 '((ns gogosci.core
                     (:require [cljs.core.async :as a]))
                   (a/go 1 2 3 4)))))))

and data resolves to 4

I'm not gonna pretend it's elegant 😅

I just don't want to have to write thousands of lines of core.async code to promesa

if it works for you, it's fine :)

ah sadly this does not actually work

it breaks down when a/<! gets involved. That's alright, it will be a useful constraint

yeah promesa was a great recommendation, the switch is rather painless

that's a lot of work!!

@borkdude but it should be possible and not hard to get core.async working with sci, right?

might need a lot of copying I guess so more annoying work rather than being complicated, right?

@mkvlr it should be possible, but I don't think it's worth doing so. you need to basically include a CLJS version of tools.analyzer at runtime, etc.

👍 1

just use promises on JS