Can't use async functions in clojurescript - clojurescript

I am trying to use a npm package in cljs called "systeminformation"
most of its function are async and some are non-async
but I am unable to use async function, everything else work fine
RELATED IMPORTS
[clojure.core.async :as async]
["systeminformation" :as systeminformation]
Code I am trying to run
(comment
(systeminformation/version) // WORKS FINE
(async/go
(async/<! (systeminformation/cpu))) // Gives me error
)
ERROR:
INFO [mutesync.inspect.electron.background.main:30] - STACK
TypeError: c.cljs$core$async$impl$protocols$ReadPort$take_BANG_$arity$2 is not a function
at cljs$core$async$impl$ioc_helpers$take_BANG_ (D:\Tom\mutesync\.shadow-cljs\builds\electron-main\dev\out\cljs-runtime\cljs\core\async\impl\ioc_helpers.cljs:52:1)
at switch__47338__auto__ (<eval>:8:52)
at <eval>:32:29
at Function.fexpr__47378 [as cljs$core$IFn$_invoke$arity$1] (<eval>:54:4)
at Object.cljs$core$async$impl$ioc_helpers$run_state_machine [as run_state_machine] (D:\Tom\mutesync\.shadow-cljs\builds\electron-main\dev\out\cljs-runtime\cljs\core\async\impl\ioc
_helpers.cljs:43:3)
at cljs$core$async$impl$ioc_helpers$run_state_machine_wrapped (D:\Tom\mutesync\.shadow-cljs\builds\electron-main\dev\out\cljs-runtime\cljs\core\async\impl\ioc_helpers.cljs:45:1)
at <eval>:84:67
at Immediate.cljs$core$async$impl$dispatch$process_messages (D:\Tom\mutesync\.shadow-cljs\builds\electron-main\dev\out\cljs-runtime\cljs\core\async\impl\dispatch.cljs:26:7)
at processImmediate (internal/timers.js:456:21)
ERROR [mutesync.inspect.electron.background.main:68] - uncaught error
TypeError: c.cljs$core$async$impl$protocols$ReadPort$take_BANG_$arity$2 is not a function

Async functions in JS are syntax sugar for functions returning a Promise.
core.async does not work with Promises by default and you need to use the helper function to make them act like channels if you want to. The <p! macro does this for you.
(ns test.foo
(:require
[clojure.core.async :as async]
[cljs.core.async.interop :refer (<p!)]
["systeminformation" :as systeminformation]))
(async/go
(prn (<p! (systeminformation/cpu))))
Alternatively you can just .then or .catch the Promise with a callback. There is no need for core.async when doing so.
(-> (systeminformation/cpu)
(.then prn))
There is also a guide available on the topic.

Related

Clojurscript: extend a Javascript class

I'm trying to use a particular JavaScript framework which requires extending a base class to use it for application.
Basically I want to do the following as idiomatic ClojureScript.
class Foo extends Bar {
constructor() { super("data") }
method1(args) { /* do stuff */ }
}
I tried
(defn foo
[]
(reify
js/Bar
(constructor [this] (super this "data"))
(method1 [this args] )))
Which would work if I'd create a new class from Object, but as shadow-cljs correctly complains, "Symbol js/Bar is not a protocol". Also, I don't want to add methods but create a subclass that inherits somemethods and overloads others.
I thought about using proxy, but "core/proxy is not defined".
Of course I could create an instance of Bar and set! new methods, but that feels like giving up and using an inferior language.
Please see answer below for more current solution!
CLJS has no built-in support for class ... extends ....
You can hack it together yourself with a bit of boilerplate, which you could generate via a macro to make it look pretty.
(ns your.app
(:require
[goog.object :as gobj]
["something" :refer (Bar)]))
(defn Foo
{:jsdoc ["#constructor"]}
[]
(this-as this
(.call Bar this "data")
;; other constructor work
this))
(gobj/extend
(.-prototype Foo)
(.-prototype Bar)
;; defining x.foo(arg) method
#js {:foo (fn [arg]
(this-as this
;; this is Foo instance
))})
CLJS (still) has no built-in support for class ... extends ....
However in recent shadow-cljs versions I added support for class as well as extends. This will emit a standard JS class and does not require any hacky workarounds to get it working.
Translating this example
class Foo extends Bar {
constructor() { super("data") }
method1(args) { /* do stuff */ }
}
would be
(ns your.app
(:require [shadow.cljs.modern :refer (defclass)]))
(defclass Foo
;; extends takes a symbol argument, referencing the super class
;; could be a local symbol such as Bar
;; a namespaced symbol such as alias/Bar
;; or just a global via js/Bar
(extends Bar)
(constructor [this]
(super "data"))
;; adds regular method, protocols similar to deftype/defrecord also supported
Object
(method1 [this args]
;; do stuff
))
More complex examples of defclass can be found here and here.
Currently this only ships with shadow-cljs but technically you can take the modern.cljc and modern.cljs files from here and put them into your project. It should work with all build tools then.

Compile ClojureScript in Java application

I'm trying to compile String that contains Clojure Script code in Java/Groovy. I'm not really happy with using "java -jar ...".execute().
Is there any way to invoke clojurescript library (version 1.8.51) to compile code?
Edit:
So combining these two links I got this code (Groovy):
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("cljs.analyzer.api"))
require.invoke(Clojure.read("cljs.compiler.api"))
IFn emptyEnv = Clojure.var("cljs.analyzer.api", "empty-env")
IFn analyze = Clojure.var("cljs.analyzer.api", "analyze")
IFn emit = Clojure.var("cljs.compiler", "emit-str")
final inputText = "(defn plus [a b] (+ a b))"
emit.invoke(
analyze.invoke(emptyEnv.invoke(), "'$inputText"
)
)
My problem is that emit function return empty string :/. Is there anything I'm doing wrong?
Edit2: The analyze method return this:
{
:op :constant,
:env {:ns {:name cljs.user},
:context :statement,
:locals {},
:fn-scope [],
:js-globals ...removed...,
:form #object[org.codehaus.groovy.runtime.GStringImpl 0x37816ea6 "'(defn plus [a b] (+ a b))"],
:tag any
}
It should be very easy to call ClojureScript Compiler API from Clojure, for example as presented in answers to a similar question on how to do it from Clojure.
But as you want to do it from Java or Groovy, you will have to use some Clojure Java API for calling Clojure from Java.

debugging functions in the clojurescript repl

Does anybody know a good way to debug functions in the clojureScript REPL?
The default, behavior is that the generated JS code of the function is printed.
midi.lib=> (defn f [] (println "hello"))
#'midi.lib/f
midi.lib=> f
#object[midi$lib$f "function midi$lib$f(){
return cljs.core.println.call(null,"hello");
}"]
With longer functions this gets annoying. For many debugging cases, simply the name of the function would be sufficient..
There is currently no support for controlling how function values print in ClojureScript.
But, if you are OK with extending the JavaScript function type, you can evaluate the following in a REPL
(extend-type js/Function
IPrintWithWriter
(-pr-writer [obj writer _]
(let [name (.-name obj)
name (if (empty? name)
"Function"
name)]
(write-all writer "#object[" name "]"))))
and then your example function value would print as #object[midi$lib$f].
Try using the Figwheel REPL, it doesn't print out the generated JS, and behaves similarly to a Clojure REPL.

Reagent: How to get new props from component-will-receive-props?

So signature for component-will-receive-props is such:
https://github.com/reagent-project/reagent/blob/master/src/reagent/core.cljs#L114
:component-will-receive-props (fn [this new-argv])
But new-args seems like it's function or js object. I was expecting it to be map of props. How do I get map of props from new-argv? I can get old props from this by (reagent/props this), but it's old props, not newly received.
Ok I finally found out it's reagent.impl.util/extract-props.
So (reagent.impl.util/extract-props new-argv) will return new props.
https://github.com/reagent-project/reagent/blob/v0.5.1-rc3/src/reagent/impl/util.cljs#L11
I think the correct way to do this is through the props function. An example here shows this.
;; assuming you have reagent required :as reagent
(reagent/create-class
{
...
:should-component-update
(fn [this]
(println "next-props" (reagent/props this))
...
})

Cannot convert function to delegate

I have this function
private ulong Html(ubyte[] data)
{
return data.length;
}
that I want to convert to delegate by using toDelegate() function. I have tried it:
client.onReceive = toDelegate(&Html);
But I'm getting an error message:
lixo.d(722): Error: not a property client.onReceive
/home/$/D/libs/arsd/dom.d(743): Warning: statement is not reachable
/usr/include/d/dmd/phobos/std/range.d(595): Error: static assert "Cannot put a dchar[] into a Appender!(char[])"
/usr/include/d/dmd/phobos/std/format.d(1758): instantiated from here: put!(Appender!(char[]),dchar[])
/usr/include/d/dmd/phobos/std/format.d(1514): instantiated from here: formatRange!(Appender!(char[]),dchar[],char)
/usr/include/d/dmd/phobos/std/conv.d(101): instantiated from here: formatValue!(Appender!(char[]),dchar[],char)
/usr/include/d/dmd/phobos/std/conv.d(757): ... (1 instantiations, -v to show) ...
/usr/include/d/dmd/phobos/std/conv.d(244): instantiated from here: toImpl!(char[],dchar[])
/home/$/libs/arsd/dom.d(2115): instantiated from here: to!(dchar[])
How to fix this?
I guess the /home/$/D/libs/arsd/dom.d library has a struct or class called 'Html'. Try renaming your 'Html' function to something else and see if it helps.
If it still doesn't work you probably have to show us more code as your example seems to work fine for me:
http://dpaste.dzfl.pl/fd729f3d
(I've seen similar errors before. For some reason the "not a property" error occurs a lot since dmd 2.060 even if the real error is not related.)