Liking cljdoc? Tell your friends :D

The new namespace called virtuoso.v2 brings some functions and macros that already present in the virtuoso.core namespace. To prevent things from breaking, I decided to put them into a new namespace. Personally I find v2 more convenient for usage but this is up to you.

The v2 API provides utilities named after their Clojure counterparts, e.g. map, pvalues and so on. But under the hood, they use a global virtual executor. This executor gets closed on JVM shutdown.

Import the namespace:

(ns some.project
  (:require
   [virtuoso.v2 :as v]))

The future macro acts like a regular future but is served using a global virtual executor service:

(def -fut
  (v/future
    (let [a 1 b 2] (+ a b))))

-fut
#object[java.util... 0x1f3aa45f "java.util.concurrent...@1f3aa45f[Completed normally]"]

@-fut
3

The macro accepts an arbitrary block of code that gets executed into a future.

The pvalues macro accepts a number of forms and runs each into a future. The result is a lazy sequence of dereferenced values:

(def -items
    (v/pvalues (+ 3 4)
               (Thread/sleep 1000)
               (let [a 3]
                 (* a a))))

-items
(7 nil 9)

Pay attention all the futures get run immediately but the process of dereferencing is lazy. They get deref-ed one by one as you iterate the result. Thus, you can easily spot an exception should it pop up:

(def -items
    (v/pvalues (/ 3 2)
               (/ 3 1)
               (/ 3 0)))

(first -items)
3/2

(second -items)
3

(last -items) ;; only now throws
;; Execution error (ArithmeticException) at virtuoso.v2/fn...
;; Divide by zero

The map function is similar to the standard map but performs each function call in a virtual future. All the steps are fired without chunking. The result is a lazy sequence of dereferenced values.

(def -items
    (v/map (fn [x] (/ 10 x)) [5 4 3 2 1 0]))

;; don't touch the last item
(take 5 -items)
;; (2 5/2 10/3 5 10)

;; touch it
(last -items)
;; Execution error (ArithmeticException) at virtuoso.v2/fn...
;; Divide by zero

The for macro acts like for but wraps each body expression into a future. All the futures are fired at once with no chunking. The result is a lazy sequence of dereferenced values. You can use :let, :when, and other nested forms:

(def -items
    (v/for [a [:a :b :c]
        b [1 2 3 4 5]
        :when (and (not= a :b) (not= b 3))]
    {:a a :b b}))

-items
({:a :a, :b 1}
 {:a :a, :b 2}
 {:a :a, :b 4}
 {:a :a, :b 5}
 {:a :c, :b 1}
 {:a :c, :b 2}
 {:a :c, :b 4}
 {:a :c, :b 5})

The thread macro just creates and starts a new virtual thread out from a block of code. Useful if you'd like to deal with Thead instances:

(let [t1 (v/thread (Thread/sleep 1000) (+ 1 2))
      t2 (v/thread (Thread/sleep 2000) (* 3 2))]
    (.join t1)
    (.join t2)
    (println "both are done"))

The v2 namespace, when loaded, adds its own JVM shutdown hook as follows:

(defonce ^Thread -shutdown-hook
  (new Thread (fn []
                (.close -EXECUTOR))))

(defonce ___
  (-> (Runtime/getRuntime)
      (.addShutdownHook -shutdown-hook)))

The global executor will be closed on JVM shutdown.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close