Skip to content

Instantly share code, notes, and snippets.

@eerohele
Last active November 6, 2021 19:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eerohele/e06551b115c6a31d1280a115c4c2abb2 to your computer and use it in GitHub Desktop.
Save eerohele/e06551b115c6a31d1280a115c4c2abb2 to your computer and use it in GitHub Desktop.
A minimal, annotated example of using Clojure prepl with core.async
  1. Acknowledge that you might actually want to use Propel instead of relying on this Gist.

  2. Clone this Gist:

    git clone https://gist.github.com/eerohele/e06551b115c6a31d1280a115c4c2abb2 prepl
  3. Open the project in your favorite editor/IDE.

  4. Start evaluating the forms in prepl.clj one by one.

{:deps {org.clojure/core.async {:mvn/version "0.5.527"}}}
(comment
(require '[clojure.core.async :as async])
(require '[clojure.core.server :as server])
(import '(java.io PipedReader PipedWriter))
;; Start a prepl server.
;;
;; The prepl server is started in a new thread, so this call doesn't block.
(def server
(server/start-server {:accept 'clojure.core.server/io-prepl
;; Passing in 0 causes the prepl server to pick a
;; random available port number.
:port 0
:name "jvm"}))
;; Just in case you need to stop the server.
#_(.close server)
;; Create a pipe comprising a PipedWriter and a PipedReader.
;;
;; You can send the output of one program (or thread) to the pipe and have
;; another program (or thread) read that output from the pipe.
(def writer (PipedWriter.))
(def reader (PipedReader.))
;; Connect the reader to the writer. The pipe won't work unless the two halves
;; (reader and writer) are connected.
;;
;; You can also pass the reader to the writer's constructor or vice versa. The
;; order doesn't matter.
(.connect reader writer)
;; The core.async channel prepl reads from.
(def in-chan (async/chan))
;; The core.async channel prepl writes to.
(def out-chan (async/chan))
;; Start a prepl client. You have to start it in another thread, because
;; the loop that reads from `reader` is infinite.
(let [port (.getLocalPort server)
;; Write whatever the prepl server returns into the output channel.
out-fn #(async/>!! out-chan %)]
(async/thread
(server/remote-prepl "localhost" port reader out-fn)))
;; Start a loop that reads everything from the input channel and writes it to
;; the specified pipe (PipedWriter).
;;
;; Everything this loop writes to the pipe is readable by other programs (or
;; threads).
(async/go-loop
[]
(when-let [input (async/<! in-chan)]
(.write writer input)
(.flush writer)
(recur)))
;; Put a code string to evaluate on the input channel
;; and read the response from the output channel.
(async/go
(async/>! in-chan "(select-keys {:a 1 :b 2 :c 3} [:a :c])")
(prn (async/<! out-chan)))
(.close reader)
(.close writer)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment