preface

In projects, we usually define domain data models for real problem domains. For example, when developing a VDOM, it is natural to define a VNode data type for packaging, storing, and manipulating related data. CLJ/CLJS not only provides built-in data structures such as List, Vector, Set, and Map, but also provides defType and defRecord so that we can customize data structures to meet our actual development needs.

Defining Data structures starts with Data Type and Record

When it comes to data structures, it’s natural to think of structs in C, where only fields define no methods, and this is the basic gameplay of defType and defRecord. The sample

(deftype VNode1 [tag props])
(defrecord VNode2 [tag props])

(def vnode1
  (VNode1. "DIV" {:textContent "Hello world!"}))
;; 或 (->VNode1 "DIV" {:textContent "Hello world!"})

(def vnode2
  (VNode2. "DIV" {:textContent "Hello world!"}))
;; 或 (->VNode2 "DIV" {:textContent "Hello world!"})
;; 或 (map->VNode2 {:tag "DIV", :props {:textContent "Hello world!"}})Copy the code

There seems to be no difference between the two. In fact, the difference lies in the operation of members

;; Deftype Specifies the member value (.-tag vnode1). => DIV ;; Defrecord takes the member value (:tag vnode2); => DIV ;; Deftype Modifies the member value (set! (.-tag vnode1) "SPAN") ;; (aset vnode1 "tag" "SPAN") (.-tag vnode1); => SPAN ;; Def vnode3 (assoc vnode2 :tag "SPAN") (:tag vnode2); => DIV (:tag vnode3) ;; => SPANCopy the code

From the above we can see that the data structure defined by defRecord can be treated as a Map, whereas defType cannot. But all of that is art, and the bottom line is that in OOP we build two types of data models: 1. Programming domain model; 2. Application domain model. For programming domain models (such as String, etc.), we can define defType to provide specialization; However, for the application domain model, we should abstract it and use existing tools (such as ASsoc,filter, etc.) to process it, and for the application domain model, all attributes should be accessible, there is no need for private, because all attributes are immutable oh.

Protocol

Protocol, like Interface, allows us to implement Interface programming. Above we can customize the data structure with defType and defRecord. In fact, we can extend the power of the data structure by implementing existing Protocol or custom Protocol.

deftypeanddefrecordImplement Protocol at definition time

;; Protocol IA (defprotocol IA (println [this]) (log [this MSG])); Defprotocol IB (print [this] [this MSG]); IA (defRecord VNode [tag props] IA (println [this] (println (:tag this))) (log [this MSG] (println MSG) ":" (:tag this))) IB (print ([this] (print (:tag this))))) ;; Def vnode (vnode. "DIV" {:textContent "Hello!" })) (println vnode) (log vnode "Oh-yeah:") (print vnode)Copy the code

In IB, print is defined as a multi-arity method, so that only one function signature is implemented as a multi-arity method.

(print ([this] (print (:tag this))))Copy the code

Otherwise the Java. Lang. UnsupportedOperationException: NTH not supported on this type: Symbol of anomalies

Implement Protocol to append existing data structures

The power of Protocol is that we can extend the behavior of existing data structures at run time, where multiple protocols can be implemented for a data structure through extend-type, and specified protocols can be implemented for multiple data structures through extend-protocol. 1. Use the extend – type

;; Extend the js/NodeList, Seq (extend-type js/NodeList ISeqable (-seq [this] (let [l (.-length this) v (transient [])] (doseq [I (range L)]) (->> i (aget this) (conj! v))) (persistent! v)))) ;; Use (map # (. - textContent %) (js/document. QuerySelector "div"));. Extend js/RegExp to be used directly as a function (extend-type js/RegExp IFn (-invoke ([this s] (re-matches this s)))); Use (#"s.*" "some"); => someCopy the code

2. Use the extend – protocol

;; Extend js/RegExp and JS /String, Make it directly available as a function (extend-protocol IFn JS /RegExp (-invoke ([this s] (re-matches this s)) js/String (-invoke ([this n]) (clojure.string/join (take n this))))) ;; Use (#"s.*" "some"); => some ("test" 2) ;; => "te"Copy the code

And we can find out about distribution. To check whether an instance of a data type implements the specified Protocol

(satisfies? IFn #"test") ;; => true ;; So for IFn we can just call IFn, right? (Ifn? #"test") ;; =>trueCopy the code

reifyConstruct an attribute-free instance that implements the specified Protocol

(defn user [firstname lastname] (reify IUser (full-name [_] (str firstname lastname)))) ;; Def me (user "John" "Huang") (full-name me); => johnHuangCopy the code

specifyandspecify!Appends the Protocol implementation to the instance

Append the specified Protocol implementation to specify immutable and copyable values. Append to the value of CLJS.

(def a "johnHuang") (def b (specify a IUser (full-name [_] "Full Name"))) (full-name a) ;; => error (full-name b); =>Full NameCopy the code

specify! You can append the specified Protocol implementation to the JS value

(def a #js {}) (specify! a IUser (full-name [_] "Full Name")) (full-name a) ;; => "Full Name"Copy the code

conclusion

CLJS recommends abstraction of data structures, so it provides Seq in addition to List,Map,Set, and Vector; And built-in a series of data operation functions, such as Map,filter,reduce and so on. Deftype and defRecord are used more for object-oriented programming, or as a means of extension in the face of built-in operations that are not sufficient to describe logic. Deftype, defRecord, and defProtocol make it easier to switch from OOP to FP. In addition, deftype, defRecord and protocol also effectively solve Expression Problem. Specific see http://www.ibm.com/developerworks/library/j-clojure-protocols/

Respect the original, reproduced please indicate from: www.cnblogs.com/fsjohnhuang… ^_^ fat John

Scan this article if you find it interesting! Donation mutual encouragement!