2 Kindly
Kindly is a 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]
(:as kind]
[scicloj.kindly.v4.kind :as tc])) [tablecloth.api
2.1 Why
Different tools have had different ways of writing notes. For example:
- Anglican tutorials (source) - written in Gorilla REPL
- thi-ng/geom viz examples (source) - written in Org-babel-clojure
- Clojure2d docs (source1, source2) - written in Codox and Metadoc
- Tablecloth API docs (source) - written in rmarkdown-clojure
- R interop ClojisR examples (source) - written in Notespace v2
- Bayesian optimization tutorial (source) - written in Nextjournal
- scicloj.ml tutorials (source) - written in Notespace v3
- Clojure2d color tutorial (source) - written in Clerk
- Tablecloth documentation (source) - written in Kindly using Clay
- …
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
- implemented a partially working adapter for Portal (kind-portal)
- actively working to support other tools such as Cursive
2.4 Existing book/notebook projects using Kindly
- Tablecloth documentation
- Fastmath 3 documentation
- ClojisR documentation
- Wolframite documentation
- Clay documentation
- Kindly-noted - documenting the ecosystem around Kindly - WIP
- Noj documentation - WIP
- Clojure Tidy Tuesdays data-science explorations
- Clojure Data Tutorials
- Clojure Data Scrapbook
- LLMs tutorial (in spanish) by Kyle Passarelli
- Statistical Computing in Clojure: Functional Approaches to Unsupervised Learning by Jaryt Salvo
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/emmy-viewers
: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/scittle
: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)
You can find more (details and examples of using these kinds)[/kindly-noted/kinds.html] in the Kindly book.
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/mdmeta
:kindly/kind)
:kind/md
2.7.3 Attaching metadata to values - cont.
-> "hello *hello* **hello**"
(
kind/mdmeta
: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/mdmeta)
:kindly{:kind :kind/md, :options nil} #
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"
(
my-library-function-for-big-big-textassoc-in [1 :style]
(:background "#ddccdd"})) {
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]) (
:x (range 3)
(tc/dataset {:y (repeatedly 3 rand)})
_unnamed [3 2]:
:x | :y |
---|---|
0 | 0.03268775 |
1 | 0.79569777 |
2 | 0.05809690 |
2.8 Hiding code
To hide the the code of a given form and only show the output, here are a few options:
Add the metadata
:kindly/hide-code true
to the form (e.g., by preceding it with^:kindly/hide-code
).Add the metadata
:kindly/hide-code true
to the value (e.g., usingvary-meta
).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
(:element/style
(kind/echarts {:width "500px"
{:height "200px"}}))
-> echarts-example
(:element/style
(kind/echarts {:width "500px"
{:height "200px"}})
meta)
:kindly{:kind :kind/echarts,
#:options #:element{:style {:width "500px", :height "200px"}}}
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]
(str "### subsection: " color))
[(kind/md (:div {:style {:background-color color
(kind/hiccup [: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