BackgroundI am authoring pulley.objective, an Open Source library that provides an object system inspired by Python's object model.  I have some features implemented, and would appreciate feedback before I continue.Ultimate goals include:Support for calculated propertiesAllow methods to be attached to an object and invokedA method differs from a function in that a method has access to the object on which the method was invokedObjects are (mostly) compatible with Clojure's persistent map objects.Inheritance, with Python-style MRO and access to superThe first two are currently implemented, and the third (map compatibility) is partially implemented.  The rest (inheritance, etc.) will be implemented in the future.CodeThe code here is from revision 4ff7e05d1ed3dcd9c3d2642bd13f506809259a3b.  Although I'm only posting the code here, feel free to comment on anything in the repository, such as documentation.All the code is contained in src/com/positronic_solutions/pulley/objective.clj.PreliminariesHere's the namespace declaration and a few preliminary declarations:(ns com.positronic-solutions.pulley.objective)(declare object-         property-get-)PersistentObjectThe PersistentObject type is responsible for implementing objects (I've elided most of the invoke variants here, since they're practically the same):(deftype PersistentObject [attrs]  clojure.lang.Associative  (assoc [self k v]    (object- (assoc attrs k v)))  (cons [self entry]    (let [[k v] entry]      (assoc self k v)))  (containsKey [self k]    (contains? attrs k))  (empty [self]    (new PersistentObject (empty attrs)))  (entryAt [self k]    (. attrs (entryAt k)))  (equiv [self other]    (. attrs (equiv other)))  (seq [self]    (seq attrs))  (valAt [self k]    (get attrs k))  (valAt [self k default]    (get attrs k default))  clojure.lang.Counted  (count [self]    (count attrs))  clojure.lang.IFn  (invoke [self k]    (if (contains? self k)      (-> (get self k)          (property-get- self))      (throw (new IllegalArgumentException (str Attribute not found:  k)))))  (invoke [self k arg]    ((self k) arg))  (invoke [self k arg1 arg2]    ((self k) arg1 arg2))  (invoke [self k arg1 arg2 arg3]    ((self k) arg1 arg2 arg3))        .        .        .  (invoke [self k arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 arg12 arg13 arg14 arg15 arg16 arg17 arg18 arg19]    ((self k) arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 arg12 arg13 arg14 arg15 arg16 arg17 arg18 arg19))  (invoke [self k arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 arg12 arg13 arg14 arg15 arg16 arg17 arg18 arg19 args]    (apply (self k) arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 arg12 arg13 arg14 arg15 arg16 arg17 arg18 arg19 args))  (applyTo [self args]    (clojure.lang.AFn/applyToHelper self args)))The object- function provides a private constructor for PersistentObject objects:(defn- object- [attrs]  (new PersistentObject attrs))map->object provides a public constructor:(defn map->object [attrs]  (object- attrs))PropertiesThe IProperty protocol is used to support properties.  Only getting properties is supported in this version.(defprotocol ^:private IProperty  (property-get- [self instance]))By default, attributes can be seen as properties where the getter is the identityfunction.  So we extend IProperty to Object as:(extend-protocol IProperty  Object  (property-get- [self instance]    self))This implementation will be used when a non-property attribute is accessed.The property function is used to implement a property:(defn property  ([& {getter :get}]    (reify IProperty      (property-get- [self instance]        (getter instance)))))MethodsMethods are implemented using properties.  The method* function wraps a function f as a property which binds f to the instance the property is on.  As with Python methods, f is expected to take an explicit self argument.(defn method*  ([f]    (property :get (fn [instance]                     (fn [& args]                       (apply f instance args))))))The method macro is provides some syntactic sugar for defining methods:(defmacro method  ([& forms]    `(method* (fn ~@forms))))Example UseHere's a simple example of creating and using an object.  Additional examples can be found in the project's Readme:First, import the library:(require '[com.positronic-solutions.pulley.objective :as objective])Now, create an object:(def foo (objective/map->object {;; Normal attribute                                 ::x 5                                 ;; Property that increments ::x                                 ::inc (objective/property :get (fn [self]                                                                  (update self ::x inc)))                                 ;; Example method                                 ;; Computes sum of xs + self's ::x                                 ::sum (objective/method [self & xs]                                         (reduce + (self ::x) xs))}))Now we can use foo as follows:(foo ::x) ;; => 5((foo ::inc) ::x) ;; => 6(foo ::inc ::x) ;; => 6(foo ::inc ::inc ::x) ;; => 7(foo ::sum 1 2 3) ;; => 11(foo ::inc ::sum 1 2 3) ;; => 12(foo ::inc ::inc ::sum 1 2 3) ;; => 13
Python-style object library for Clojure
python;object oriented;clojure;framework