core.async difference between Clojure and ClojureScript [duplicate] - clojure

This question already has answers here:
Can't seem to require >!! or <!! in Clojurescript?
(2 answers)
Closed 4 years ago.
I am working on my first ClojureScript project, and I am not able to find core.async functions/macros I am used to in Clojure; like thread, <!!. (I checked the source code in github as well and they do not exist in the cljs source)
Is there some reference I can use to find the differences between the usage of core.async in Clojure and ClojureScript?
Also, how do I perform a blocking get operation from a chan outside a go block in cljs? Looks like cljs does not have any blocking operations in core.async
Or just start a separate thread for a function which is not going to return any value?
Google doesn't really seem to provide a lot of info about core.async in cljs
Any help or pointers would be appreciated!

core.async is a separate library. Make your project.clj look like this:
(defproject flintstones "0.1.0-SNAPSHOT"
:min-lein-version "2.7.1"
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.238"]
[org.clojure/core.async "0.4.474"]]
...
and your CLJS namespace should define something like:
(ns tst.flintstones.dino
(:require
[cljs.test :refer-macros [deftest is async use-fixtures]]
[cljs.core.async :as async]
[dinoPhony] ))
and code like:
(let [ch (async/chan)]
(async/go (async/>! ch 42))
(println "dino: async result:" (async/go (async/<! ch)))
Please see this template project for a working example.

Related

Why can't Leiningen always use my :gen-class properly?

Let's say I create a new Leiningen project (lein new app example) and add some code in example/src/example/core.clj that makes use of :gen-class:
(ns example.core
(:gen-class :extends javafx.application.Application))
(defn -start [this stage]
(.show stage))
(defn -main [& args]
(javafx.application.Application/launch example.core args))
If I then create a JAR (lein uberjar) and run it, everything works fine. However, if I instead try to run my app directly (lein run), I get a ClassNotFoundException. In addition, if I open a REPL (lein repl), I first get the same error as before, but after running this code:
(compile 'example.core)
the error no longer appears in lein run or lein repl.
Could someone please explain to me what exactly is going on here, and how I can run my app directly without needing to manually compile my code from a REPL?
Edit: After fooling around a bit more, I found that the solution to this problem is to add
:aot [example.core]
to project.clj. (Thanks #Mars!) I'm still confused, though, because I had previously tried simply removing ^:skip-aot, which (according to the docs) should work:
This will be AOT compiled by default; to disable this, attach
^:skip-aot metadata to the namespace symbol.
But it doesn't. Why?
Another edit (if I should split any of this into a separate question, let me know and I'll do so): I've been playing with hyphens (lein new app my-example), and weird stuff has been happening. This doesn't work:
(ns my-example.core
(:gen-class :extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.core args))
But this does:
(ns my-example.core
(:gen-class
:name my-example.Core
:extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.Core args))
So my class name can either start with a lowercase letter (example.core) or contain a hyphen (my-example.Core), but not both? I really don't get it.
And finally, lein uberjar fails on that final example (with the explicit :name), because
Warning: The Main-Class specified does not exist within the jar. It may not be executable as expected. A gen-class directive may be missing in the namespace which contains the main method.
As far as I can tell, the only way to fix that is to split the Application subclass into a separate namespace. Is there another way?
Agreed with #Mars, the problem is that lein run does not AOT the example.core namespace. The default Leiningen template made the example.core non AOT:
(defproject example "0.1.0-SNAPSHOT"
...
:main ^:skip-aot example.core
...)
My best guess is that you could define your app using defrecord and use that as a class instead of the namespace.

Clojure JavaFX -- Toolkit Not Initialized error

JavaFX 8, Java 1.8.0_31, Windows 7 x64
I have a minimal JavaFX program in Clojure. The (ns...) clause is able to import the required Java packages fine except the classes in javafx.scene.control, such as Button and TextField, etc.
I have to put the import for these after initializing the toolkit. Why can't I import these classes before the toolkit is initialized? I'm not actually creating any objects yet... so I'm guessing JFX is somehow doing something in the background while these classes are imported, requiring the initialization first. Below is my complete lein project (minimized from the actual application where I saw this problem, and without all the nice macros that clean up the JFX syntax):
File project.clj:
(defproject jfx-so "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]]
:main jfx-so.core)
File src/jfx_so/core.clj:
(ns jfx-so.core
(:import [javafx.scene Scene]
[javafx.scene.layout BorderPane]
[javafx.stage Stage]))
(defonce force-toolkit-init (javafx.embed.swing.JFXPanel.))
;; For some reason the following must be imported after initting the toolkit
(import [javafx.scene.control Button])
(defn -main [& args]
(javafx.application.Platform/runLater
#(doto (Stage.)
(.setScene (Scene. (BorderPane. (Button. "Hello"))))
(.show))))
Thanks! :)
I haven't had a problem with this. Perhaps it has to do with your defonce?
I do my imports first. But I do make sure to init the FX-envoronment before instanciating any FX-classes. So after your -main-method I would put:
(defn -main [& args]
;;body here
)
;; initialze the environement
(javafx.embed.swing.JFXPanel.)
;; ensure I can keep reloading and running without restarting JVM every time
(javafx.application.Platform/setImplicitExit false)
;; then
(-main)
Hope this helps.

How to require a namespace programmatically

I'm working on a Liberator project in Clojure. I've defined a series of routes which return JSON data computed by logic in some other namespace. I would like to be able to change the namespace that implements the logic programmatically so I can do something like this:
JAVA_OPTS='-DgameLogicNamespace=foo.logic.mock' lein ring server-headless 8080
I am currently doing it like this:
(ns foo.routes
(:require [compojure.core :refer :all]
[liberator.core :as lib :refer [defresource request-method-in]]
[liberator.representation :refer [ring-response]]))
(require
(vec
(cons (symbol (System/getProperty "gameLogicNamespace" "foo.logic.real"))
'[:as logic])))
This works, but feels a bit clunky. Is there an idiomatic way to accomplish what I want?
One of my main motivations is actually for unit testing routes with mock data, so if there's a nice solution for providing the mock logic only in tests (and not as a JVM system property), suggestions are welcome.
One of my main motivations is actually for unit testing routes with mock data, so if there's a nice solution for providing the mock logic only in tests (and not as a JVM system property), suggestions are welcome.
If you haven't already, take a look at ring-mock for some nice utilities to generate mock requests to test your Ring handlers.
If you're interested in providing mock versions of functions that provide the implementation of your application logic during unit tests, consider using with-redefs; it's pretty much custom-made for this purpose.
(ns my-app.handlers-test
(:require [clojure.test]
[my-app.handlers :as h]
[my-app.logic :as l]
[ring.mock.request :as r]))
(deftest test-simple-handler
(with-redefs [l/my-complicated-logic #(update-in % [:a] inc)]
(is (= {:a 2}
(h/my-handler (r/request :post "/foo" {:a 1}))))))

Clojure: Lein run unable to resolve symbol

I just started on my first clojure project using lein, the code here:
(ns fileops.core
(:use
[clojure.core :only (slurp)]
[clojure-csv.core :only (parse-csv)]
[fileops.core]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(read-file "sample.csv"))
(defn read-file
"open and read the csv file"
[fname]
(with-open [file (clojure.java.io/reader fname)]
(parse-csv (slurp fname))))
I tried to run this using "lein run" but I keep getting this error:
Caused by: java.lang.RuntimeException: Unable to resolve symbol: read-file in this context
at clojure.lang.Util.runtimeException(Util.java:219)
at clojure.lang.Compiler.resolveIn(Compiler.java:6874)
at clojure.lang.Compiler.resolve(Compiler.java:6818)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6779)
at clojure.lang.Compiler.analyze(Compiler.java:6343)
... 52 more
What am I doing wrong?
You have used only slurp from clojure core, meaning that every other core function is now unavailable to you :) Try changing your ns to use :require instead of :use, as this is more idiomatic.
One thing to note is that order does matter in clojure, so if you don't declare a function at the top of your file, as in C and some other languages, the earlier functions will not be able to make reference to them. This is what was causing your error before and is why I like to define my -main function at the bottom. It's a matter of style.
Another thing is that your -main function is taking variable args right now and not using them. In Clojure it is idiomatic to use _ to refer to a parameter that doesn't get used. You could use & _ to avoid error messages, for when the user passes in unnecessary args, but I would just have the -main function parameterless from the start. This is because nothing needs to be provided to main when you run the program, and errors do make debugging easier. It is good to know what is getting used and where. The sample.csv file is already provided and is having read-file called on it, so the program should run if your read-file function is correct and the sample.csv file is in the proper location.
Regarding your -main function, it would be nice to put a little test in there to see if it executes properly when you run it, so I changed it to print out the contents of the csv file onto your console. This way of printing from a file is efficient and worth studying in its own right.
Finally, Make sure you include clojure-csv.core in your project.clj file.
core.clj:
(ns fileops.core
(:require
[clojure-csv.core :refer [parse-csv]]))
(defn read-file
"open and read the csv file"
[fname]
(with-open [file (clojure.java.io/reader fname)]
(parse-csv (slurp fname))))
(defn -main []
(println (clojure.string/join "\n" (read-file "resources/test.csv"))))
project.clj:
...
:dependencies [[org.clojure/clojure "1.5.1"]
[clojure-csv/clojure-csv "2.0.1"]
...]
:main fileops.core
You need to declare fileops.core as :main, as shown above. This tells Leiningen what function to execute when you enter lein run. Very important and tricky stuff.
So now make sure you are in the root of your project directory and at the terminal, run the following:
lein clean
lein deps
lein run
Good luck!
Further reading:
8th light blog on name-spaces
flying machine studios explanation of lein run
read-file should be before main in your source OR you should put before -main a declare cause like that:
(declare read-file)

Why does read-line not return after hitting ENTER (seems like a hang) using lein run, but works with lein repl?

The problem at hand is that when I run my program with lein run it gets to the (read-line) part and I can't get out of it, meaning: read-line never returns.
Here is the relevant code:
(def command (atom ""))
(defn print-prompt []
(print "prompt> ")
(flush)
)
(defn ask-for-input []
(print-prompt)
(let [x (str (read-line))]
(println (str "User input: " x))
(reset! command x)
)
)
I never see the "User input: " string on screen.
The strange part is, if I run lein repl and call (ask-for-input) then it works correctly :S
Try lein trampoline run, it works.
The following is from leiningen FAQ:
Q: I don't have access to stdin inside my project.
A: This is a limitation of the JVM's process-handling methods; none of them expose stdin correctly. This means that functions like read-line will not work as expected in most contexts, though the repl task necessarily includes a workaround. You can also use the trampoline task to launch your project's JVM after Leiningen's has exited rather than launching it as a subprocess.
I tried your source code, but omitted the flush. It worked without a problem. What version of Clojure are you using? I tried the following code with Clojure 1.3.
(def command (atom 0))
(defn print-prompt []
(print "prompt> ")
)
(defn ask-for-input
[]
(print-prompt)
(let [x (str (read-line))]
(println (str "User input: " x))
(reset! command x)
))
Edit:
I altered one of your functions that I copied and tested with, and it works now with standalone and lein run. You had (flush) in your original example.
(defn print-prompt []
(print "prompt> ")
(flush)
)
From what I can garner, println causes a flush, print doesn't, and you need a flush after print.
I am adding this information in case it might be of help. I have a Clojure project called repl-test. Here is my repl-test project's core.clj file header. Your source, already posted, is in this file with some other functions, not related to your post.
(ns repl-test.core
(:gen-class)
(:use clojure.contrib.command-line)
(:require [clojure.contrib.string :as cstr])
(:require [clojure.contrib.trace :as ctr])
(:require [clojure.string :as sstr])
(:use clojure-csv.core))
And here is the project.clj file:
(defproject repl-test "0.0.1-SNAPSHOT"
:description "TODO: add summary of your project"
:dependencies [[org.clojure/clojure "1.3.0"]
[org.clojure/clojure-contrib "1.2.0"]
[clojure-csv/clojure-csv "1.2.4"]
[org.clojure/tools.cli "0.1.0"]
[clj-http "0.1.3"]]
:aot [repl-test.core]
:main repl-test.core)