Skip to content

Instantly share code, notes, and snippets.

@just-sultanov
Created April 25, 2022 12:40
Show Gist options
  • Save just-sultanov/840f694a2b2a1eadf841beb22a07aab5 to your computer and use it in GitHub Desktop.
Save just-sultanov/840f694a2b2a1eadf841beb22a07aab5 to your computer and use it in GitHub Desktop.
Experiment
(ns tenet.experiment
(:require
[clojure.string :as str])
(:import
(clojure.lang
Associative
Counted
IFn
ILookup
IObj
IPersistentCollection
IPersistentVector
ISeq
Indexed
Keyword)
(java.io
Writer)
(java.util
Map$Entry)))
(def *registry
(atom
#{:busy
:conflict
:error
:forbidden
:incorrect
:interrupted
:not-found
:unauthorized
:unavailable
:unsupported}))
(defprotocol IAnomaly
(anomaly? [this]))
(defprotocol IResponseBuilder
(as-response [this category]))
(defprotocol IResponse
(response? [this])
(category [this])
(value [this]))
(extend-protocol IAnomaly
nil
(anomaly? [_] false)
Object
(anomaly? [_] false)
Exception
(anomaly? [_] true)
Keyword
(anomaly? [x]
(or (boolean (@*registry x))
(isa? x ::error))))
(deftype Response
[$category $value $meta]
IResponse
(response? [_] true)
(category [_] $category)
(value [_] $value)
IAnomaly
(anomaly? [_] (anomaly? $category))
IFn
(invoke [_] ($value))
(invoke [_ a] ($value a))
(invoke [_ a b] ($value a b))
(invoke [_ a b c] ($value a b c))
(invoke [_ a b c d] ($value a b c d))
(invoke [_ a b c d e] ($value a b c d e))
;; add invoke and applyTo
IObj
(meta [_] $meta)
(withMeta [_ new-meta] (Response. $category $value (merge $meta new-meta)))
Counted
(count [_] (count $value))
ISeq
(first [_] (first $value))
(next [_] (next $value))
(seq [_] (seq $value))
(cons [_ o] (cons o $value))
Indexed
(nth [_ idx] (nth $value idx))
(nth [_ idx not-found] (nth $value idx not-found))
ILookup
(valAt [_ key] (.valAt $value key))
(valAt [_ key not-found] (.valAt $value key not-found))
Associative
(containsKey [_ key] (.containsKey $value key))
(entryAt [_ key] (.entryAt $value key))
(assoc [_ key new-value] (.assoc $value key new-value))
Map$Entry
(getKey [_] (.getKey $value))
(getValue [_] (.getValue $value))
IPersistentCollection
(equiv [_ other] (= $value other))
Object
(toString [_] (str $value))
(equals [_ other] (= $value other))
(hashCode [_] (.hashCode $value)))
(defmethod print-method Response [^Response response ^Writer writer]
(print-method (.$value response) writer))
(defmethod print-dup Response [^Response response ^Writer writer]
(print-dup (.$value response) writer))
(defn vec->response
[[category value :as coll]]
(with-meta (as-response value category) (meta coll)))
(defn map->response
[{:keys [category value] :as map}]
(with-meta (as-response value category) (meta map)))
(extend-type nil
IResponseBuilder
(as-response [_ type]
(Response. type nil nil))
IResponse
(response? [_] false))
(extend-type Object
IResponseBuilder
(as-response [x category]
(Response. category x (assoc (meta x) :type (type x))))
IResponse
(response? [_] false))
(comment
(require '[criterium.core :as bench])
;; vector
(def x:vector
(with-meta
(as-response [1 2 3 4 5] :error)
{:foo :bar}))
x:vector ;; => [1 2 3 4 5]
(type x:vector) ;; => clojure.lang.PersistentVector
(class x:vector) ;; => tenet.experiment.Response
(anomaly? x:vector) ;; => true
(response? x:vector) ;; => true
(category x:vector) ;; => :error
(meta x:vector) ;; => {:type clojure.lang.PersistentVector, :foo :bar}
(count x:vector) ;; => 5
(seq x:vector) ;; => (1 2 3 4 5)
(map inc x:vector) ;; => (2 3 4 5 6)
(nth x:vector 2) ;; => 3
(get x:vector 2) ;; => 3
(contains? x:vector 3) ;; => true
(contains? x:vector 6) ;; => false
(= x:vector [1 2 3 4 5]) ;; => true
(str x:vector) ;; => "[1 2 3 4 5]"
(pr-str x:vector) ;; => "[1 2 3 4 5]"
;; list
(def x:list
(with-meta
(as-response '(1 2 3 4 5) :error)
{:foo :bar}))
;; fn
(def x:keyword (as-response :foo :error))
(def x:+ (as-response + :error))
(x:keyword {:foo :bar}) ;; => :bar
(x:+ 1 2 3) ;; => 6
;; map
(def x:map (as-response {:foo {:bar 42}} :error))
(get-in x:map [:foo :bar]) ;; => 42
(update-in x:map [:foo :bar] inc) ;; => {:foo {:bar 43}}
;; string
(def x:string (as-response "foo" :error))
(str/upper-case x:string) ;; => "FOO"
(str/capitalize x:string) ;; => "Foo"
;; problems
(def x:num (as-response 42 :error))
(+ x:num 42) ;; => class tenet.experiment.Response cannot be cast to class java.lang.Number
(+ (value x:num) 42) ;; => 84
(conj x:vector 40) ;; => (40 1 2 3 4 5)
(conj (value x:vector) 40) ;; => [1 2 3 4 5 40]
(conj x:list 40) ;; => (40 1 2 3 4 5)
(conj (value x:list) 40) ;; => (40 1 2 3 4 5)
;; perf tests
(bench/quick-bench
(:foo (meta x:vector))) ;; => :bar
;; 13.485809 ns
(def plain-map
(with-meta {:foo :bar} {:foo :bar}))
(bench/quick-bench
(:foo plain-map)) ;; => :bar
;; 8.541336 ns
(bench/quick-bench
(:foo (meta plain-map))) ;; => :bar
;; 79.708892 ns
(bench/quick-bench
(str/upper-case x:string)) ;; => "FOO"
;; 28.614984 ns
(bench/quick-bench
(str/upper-case "foo")) ;; => "FOO"
;; 22.902562 ns
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment