Klipse for Clojure

Klipse: The features

All languages:

  • code is evaluated as you type (configurable)
  • environment is shared between code snippets
  • hidden code snippets
  • snippet preamble
  • printing
  • clojurescript transpilation
  • code evaluation in a loop
  • load code from a github gist
  • interacting with the DOM
  • the CodeMirror editor is configurable: options and CSS

Only for Clojure:

  • caching of external libs: core.async
  • custom external libraries
  • limited size printing
  • infinite loop partial prevention
  • reagent snippets
  • output is beautified and strings are beautified

More details:

Join the ride

qr code

Klipse: the REPL

Usage:

  • have fun with clojure stuff without a clojure env available
  • share your discoveries on twitter
  • ask for help on slack/google group
  • what else?

Interactive documentation

Klipse: code is evaluated as you type

Zero-delay between the trial and the result is key for creativity.





[1,2,3].map(x => x + 1)

$("#container-1")
.css({color: 'white',
       backgroundColor: 'blue',
      padding: '10px',
      fontSize: '50px'})
.text("Hello World")

Running code in a loop

Let’s create a Klipse snippet that is evaluated again and again in intervals.

Setting data-loop-msec to the time in msec between the evaluations.

For example, this is how we display a random number each second (Check the page source!):

<pre><code class="lang-eval-clojure" data-loop-msec="1000">
(rand)
</code></pre>

This snippet is evaluated each second - without any user interaction:


(rand)

Hiding some details from the reader

Less is more

You don’t want to confuse your readers with technical details that are not revelant to the main idea of your article.

But you need this (irrelevant) code to let the other parts of the code run properly.

Two mechanisms for hiding code from the reader but not from Klipse:

  • A hidden Klipse snippet
  • data-preamble

You can hide a snippet using CSS

<pre class="hidden"><code class="clj">
(def irrelevant-constant
42)
</code></pre>

There is a hidden Klipse snippet just above me

We can use the hidden code in subsequent Klipse snippets:

irrelevant-constant

We can hide part of the Klipse snippet’s code using data-preamble.

<pre><code class="clj" data-preamble="(def random-num  (rand))">
random-num
</code></pre>

random-num

Klipse: environment is shared between Klipse snippets

A blog post is a story.

The different elements of the stories need to be connected together.

Let’s see how to write a Hello World function in Clojure:

(defn hello [name]
  (str "hello " name "\n"))

And now, let’s see how to use this function:

(hello "Klipse")

☕You can embed a jsfiddle or a codepen in a blog post with an <iframe>.
But you cannot share neither code nor data between the iframes.

Caching of clojure libs

clojure libs cached in Klipse:

  • clojure.walk
  • clojure.set
  • clojure.data
  • clojure.zip
  • core.async
  • core.match
  • clojure.spec
  • clojure.math.combinatorics
  • reagent
  • reagent-froms
  • clojure.test.check
  • net.cgrand.macrovich

core.async

Let’s require core.async

(require '[clojure.core.async :refer [chan <! timeout put!]])
(require-macros '[cljs.core.async.macros :refer [go]])

Let’s create a communication channel

(def msg-chan (chan))

A consumer:


(let [container js/klipse-container]
  (go
    (let [div (js/document.createElement "div")]
      (.appendChild container div)
      (set! (.-innerText div)
            (<! msg-chan)))))

A publisher

(put! msg-chan "Hello")

Check this amazing demo by the author of core.async.

Clojure.libs - math.combinatorics and core.match

Let’s generate all the permutations of an array

(require '[clojure.math.combinatorics :refer [permutations]])
(permutations [1 2 3])

Core.match - pattern matching - will simplify a lot your code and make it much more elegant.

(require '[cljs.core.match :refer-macros [match]])
(let [x 1 y 2]
  (match [x y]
         [1 b] b
         [a 2] a
         :else nil))

External libs

You can also include code from an external lib, that is not cached in Klipse.

Klipse can load code dynamically from any source or code repository e.g. github.

data-external-libs is like the class path.

For instance, let’s play with a clojure implementation of McCarthy’s LISP.

<pre><code class="clj" data-external-libs="https://raw.githubusercontent.com/viebel/original-lisp/48badc99b6e7cfba7b86742d2a0b9ccc5db18953/src">
(require '[original-lisp.core :refer [l-eval]])
</code></pre>

(require '[original-lisp.core :refer [l-eval]])
(l-eval '(car '(10 2)) '())

Load code from a github gist

Sometimes, you don’t want to keep your code in the source of your blog but rather have it in a github gist.
It will allow you to modify the code of the snippet without having to republish your gitbook.

Here is a gist that defines a function read-several-exps for reading several expressions from a string.
(read-string reads only a single expression)

To kliplisfy the gist, we use data-gist-id with the id of the gist - including the username.

Here is how it is included in the current presentation (check the page source!):

<pre><code class="clj" data-gist-id="viebel/8cb19d258fea39a64146721ce50603d0">
</code></pre>

Here is the klipsified gist:


Reagent snippets

Reagent is one of the most elegant lib for creating a react components.

Judge by yourself

Let’s require reagent

(require '[reagent.core :as r])

Let’s start the show!

A simple reagent component

(defn hello [name]
  [:p (str "Hello " name "!")])

[hello "Klipse"]

Let’s leverage the power of expression of Clojure

[:div
 (for [name ["World" "Meetup" "Klipse"]]
   [hello name])]

Reagent (cont.)

A BMI calculator

(def bmi-data (r/atom {:height 180 :weight 80}))

(defn calc-bmi []
  (let [{:keys [height weight bmi] :as data} @bmi-data
        h (/ height 100)]
    (if (nil? bmi)
      (assoc data :bmi (/ weight (* h h)))
      (assoc data :weight (* bmi h h)))))

(defn slider [param value min max]
  [:input {:type "range" :value value :min min :max max
           :style {:width "100%"}
           :on-change (fn [e]
                        (swap! bmi-data assoc param (.-target.value e))
                        (when (not= param :bmi)
                          (swap! bmi-data assoc :bmi nil)))}])

(defn bmi-component []
  (let [{:keys [weight height bmi]} (calc-bmi)
        [color diagnose] (cond
                           (< bmi 18.5) ["orange" "underweight"]
                           (< bmi 25) ["inherit" "normal"]
                           (< bmi 30) ["orange" "overweight"]
                           :else ["red" "obese"])]
    [:div
     [:h3 "BMI calculator"]
     [:div
      "Height: " (int height) "cm"
      [slider :height height 100 220]]
     [:div
      "Weight: " (int weight) "kg"
      [slider :weight weight 30 150]]
     [:div
      "BMI: " (int bmi) " "
      [:span {:style {:color color}} diagnose]
      [slider :bmi bmi 10 50]]]))

Clojurescript transpilation

Understand how clojurescript overcomes the limitations of javascript:

  • no question marks and hyphens in symbols
  • truth definition is weird
(defn is-it-true? [x]
  (let [a x
        a x]
    (if a true false)))

Learn more about Truth in clojurescript.

Klipse: the challenges

  • Clojurescript is not clojure: threads, types, core.async…​
  • Self-Host Clojurescript is not Clojurescript: many libs are not yet ported (core.logic, core.matrix…​)
  • Security issues for blogging platform
  • Infinite loops

What’s in it for you?

  • Ask from bloggers to klipsify their articles
  • Give a star to Klipse on Github
  • Share the world about code interactivty with your friends
  • Write an interactive blog post: It’s the best way to learn a topic and It’s really fun!
  • Klipsify a tutorial, a scientific paper, a blog post…​
  • Klipsify your library documentation

Questions

questions

powered by Klipse /