2  Kindly

Kindly is a proposed standard for requesting data visualizations in Clojure.

It specifies in what kinds of way Clojure forms and values should be displayed.

(ns kindly
  (:require [scicloj.kindly.v4.api :as kindly]
            [scicloj.kindly.v4.kind :as kind]
            [tablecloth.api :as tc]))

Kindly logo

2.1 Why

2.2 Goal

  • Have a standard way to request data visualizations
  • for blog posts, books, slideshows, reports, dashboards, and interactive analyses,
  • that just will work across different tools,
  • without even mentioning those tools in the code.

2.3 Status

  • Supported by Clay & Claykind

  • Has adapters for Portal (kind-portal) & Clerk (kind-clerk)

  • Ready to explore on other tools

2.4 Existing book/notebook projects using Kindly

2.5 Example

Here is how one may request something of kind/md, which means Markdown:

(kind/md
 "hello *hello* **hello**")

hello hello hello

2.6 The set of kinds

(sort kindly/known-kinds)
(:kind/code
 :kind/cytoscape
 :kind/dataset
 :kind/echarts
 :kind/edn
 :kind/fn
 :kind/fragment
 :kind/hiccup
 :kind/hidden
 :kind/highcharts
 :kind/html
 :kind/htmlwidgets-ggplotly
 :kind/htmlwidgets-plotly
 :kind/image
 :kind/map
 :kind/md
 :kind/observable
 :kind/plotly
 :kind/portal
 :kind/pprint
 :kind/reagent
 :kind/seq
 :kind/set
 :kind/smile-model
 :kind/table
 :kind/test
 :kind/test-last
 :kind/tex
 :kind/var
 :kind/vector
 :kind/vega
 :kind/vega-lite
 :kind/video)

2.7 How to use Kinds?

2.7.1 Attaching metadata to forms

^:kind/md
["hello *hello* **hello**"]

hello hello hello

^kind/md
["hello *hello* **hello**"]

hello hello hello

2.7.2 Attaching metadata to values

(-> ["hello *hello* **hello**"]
    kind/md)

hello hello hello

(-> ["hello *hello* **hello**"]
    kind/md
    meta
    :kindly/kind)
:kind/md

2.7.3 Attaching metadata to values - cont.

(-> "hello *hello* **hello**"
    kind/md
    meta
    :kindly/kind)
:kind/md

Values that cannot have metadata are wrapped in a vector before attaching metadata.

(-> "hello *hello* **hello**"
    kind/md
    kind/pprint)
["hello *hello* **hello**"]
(-> "hello *hello* **hello**"
    kind/md
    meta)
#:kindly{:kind :kind/md}

2.7.4 Using values annotated by libraries

(defn my-library-function-for-big-big-text [text]
  (kind/hiccup
   [:big {:style {:background "#ccddcc"}}
    [:big text]]))
(-> "hello"
    my-library-function-for-big-big-text)
hello
(-> "hello"
    my-library-function-for-big-big-text
    (assoc-in [1 :style]
              {:background "#ddccdd"}))
hello

2.7.5 Automatically-inferred kinds

In certain situations, kinds are inferred without annotation. The kindly-advice library provides the default inference behaviour and an option to extend it.

For example, images:

(def clj-image
  (->  "https://upload.wikimedia.org/wikipedia/commons/e/eb/Ash_Tree_-_geograph.org.uk_-_590710.jpg"
       (java.net.URL.)
       (javax.imageio.ImageIO/read)))
clj-image
(require '[tablecloth.api :as tc])
(tc/dataset {:x (range 3)
             :y (repeatedly 3 rand)})

_unnamed [3 2]:

:x :y
0 0.70567908
1 0.24702570
2 0.34703335

2.8 Hiding code

To hide the the code of a given form and only show the output, here are a few options:

  1. Add the metadata :kindly/hide-code true to the form (e.g., by preceding it with ^:kindly/hide-code).

  2. Add the metadata :kindly/hide-code true to the value (e.g., using vary-meta).

  3. Some tools such as Clay allow the user to globally define certain kinds (e.g., :kind/md, :kind/hiccup) to always hide code

2.9 Passing options

The functions in the kind namespace may recieve an additiona map argument, which is attached at the :kindly/options key of a value’s metadata.

(def echarts-example
  {:title {:text "Echarts Example"}
   :tooltip {}
   :legend {:data ["sales"]}
   :xAxis {:data ["Shirts", "Cardigans", "Chiffons",
                  "Pants", "Heels", "Socks"]}
   :yAxis {}
   :series [{:name "sales"
             :type "bar"
             :data [5 20 36
                    10 10 20]}]})
(kind/echarts
 echarts-example)
(-> echarts-example
    (kind/echarts {:element/style
                   {:width "500px"
                    :height "200px"}}))
(-> echarts-example
    (kind/echarts {:element/style
                   {:width "500px"
                    :height "200px"}})
    meta)
{:source
 "{:title {:text \"Echarts Example\"}\n   :tooltip {}\n   :legend {:data [\"sales\"]}\n   :xAxis {:data [\"Shirts\", \"Cardigans\", \"Chiffons\",\n                  \"Pants\", \"Heels\", \"Socks\"]}\n   :yAxis {}\n   :series [{:name \"sales\"\n             :type \"bar\"\n             :data [5 20 36\n                    10 10 20]}]}",
 :line 150,
 :column 3,
 :end-line 159,
 :end-column 33,
 :kindly/options #:element{:style {:width "500px", :height "200px"}},
 :kindly/kind :kind/echarts}

2.10 Fragments

kind/fragment is a special kind. It expects a sequential value and generates multiple items, of potentially multiple kinds, from its elements.

(->> ["purple" "darkgreen" "goldenrod"]
     (mapcat (fn [color]
               [(kind/md (str "### subsection: " color))
                (kind/hiccup [:div {:style {:background-color color
                                            :color "lightgrey"}}
                              [:big [:p color]]])]))
     kind/fragment)

2.10.1 subsection: purple

purple

2.10.2 subsection: darkgreen

darkgreen

2.10.3 subsection: goldenrod

goldenrod

source: notebooks/kindly.clj