Created
April 25, 2022 12:40
-
-
Save just-sultanov/840f694a2b2a1eadf841beb22a07aab5 to your computer and use it in GitHub Desktop.
Experiment
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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