UNleash the REPL

I spent some hammock time (ski lift time actually), brooding over unrepl.

What's ok

  • cleanly multiplexed output (out, err, eval, exception etc.)
  • elisions (which can provide all sort of browsing navigation)
  • attachments
  • :bye
View demo.clj
;; Clojure 1.6.0
=> *clojure-version*
{:major 1, :minor 6, :incremental 0, :qualifier nil}
=> (def env1 (create-clojure-env))
=> (env1 *in* *out*)
clojure.core=> *clojure-version*
{:major 1, :minor 8, :incremental 0, :qualifier nil}
View sql.clj
(ns powderkeg.sql
(:require [clojure.spec :as s]
[clojure.edn :as edn]
[powderkeg.core :as keg]
[net.cgrand.xforms :as x])
[org.apache.spark.sql functions Column Row RowFactory DataFrame]
[org.apache.spark.sql.types StructType StructField ArrayType DataType DataTypes Metadata MetadataBuilder]))
(defmulti expr first)

So I have a full working prototype (at but I haven’t wrote the spec yet for the input part.

Clojure 1.9.0-alpha14
user=> (require 'unrepl.repl)
user=> (unrepl.repl/start)


View REPL the ultimate

REPL the Ultimate Protocol: Notes for a hopefully better "tooling over repl" approach for Clojure

The single point of agreement between the server and the client should be that: there exists a stream-based plain repl running and connected (socket or pipes etc. is not relevant).

From there one can upgrade to a richer repl. The interesting fact is that since the upgrade is triggered by the client there can't be any mismatch between capabilities. Even if your editor scripting power is limited (coughvimcough) you can treat the clojure snippet required for upgrade as a blob.

View ednrepl.clj
;; some sample interaction with a clojure socket repl whose output stream consists only of valid edn forms.
;; the edn stream is not meant for the end user but for the client UI.
;; it demoes several features:
;; • prompt, out, err, eval and exceptions are demultiplexed; unstructred ones (err & out) are just text but the others give data.
;; • elided content (because data too deep or wide) is replaced by a tagged literal (which optionally contains the expr to evaluate to get more items)
;; this last point makes possible navigation
;; lines are prefixed by > or < to tell what is sent to the repl (>) and what is received from it (<)
View retrospec.clj
=> (ns user (:require [powderkeg.sql :as sql] [clojure.spec :as s]))
=> (s/def ::age int?)(s/def ::name string?) (s/def ::friends (s/* ::name))
=> (sql/df [{:name "Gaël" :age 6 :friends ["Alix"]}
{:name "Estelle" :age 4 :friends ["Léana"]}
{:name "Lilly" :age 1 :friends ["Tigger" "Cuddly"]}]
(s/keys :req-un [::name ::age ::friends]))
View dataframes.clj
;; first, define some specs
=> (s/def ::name string?)
(s/def ::age int?)
(s/def ::friends (s/* ::name))
;; second create a dataframe from a RDD and a spec
=> (sql/df (rdd [{:name "Gaël" :age 6 :friends ["Alix"]}
View spec.clj
(ns powderkeg.spec
(:require [clojure.spec :as s])
(:import [org.apache.spark.sql.types DataType DataTypes]))
(defmulti expr first)
(def ^:private regexp-repeat
(s/cat :tag any? :type ::datatype)
View partial vs JIT.clj
=> (class #(* 10 %))
=> (class #(* 10 %))
=> (class #(* 10 %))
=> (class #(* 10 %))
=> (class #(* 10 %))