GithubHelp home page GithubHelp logo

at-el's Introduction

@, another object system for Emacs Lisp

@ is a library providing a domain-specific language for multiple-inheritance prototype-based objects in Emacs Lisp. The goal is to provide a platform for elegant object-oriented Emacs Lisp.

The root object of the @ object system is @. New objects are created with the @extend function, by extending existing objects. Given no objects to extend, new objects will implicitly extend @. Keyword arguments provided to @extend are assigned as properties on the new object.

Properties are looked up using eq, so stick to keywords, symbols, and integers as property keys. The parent prototypes of an object, used in property lookups, are listed in the :proto property of the object. This can be modified at any time to change the prototype chain.

See also: Prototype-based Elisp Objects with @

Prototype Library

Practical example @ prototypes can be found under lib/. When prototypes are stored in global variables following the @ naming convention, as demonstrated in the examples, their methods can be looked up with describe-@ (C-h @), similar to describe-function (C-h f).

Feature Demonstration

Here's a hands-on example of @'s features.

Property Access

(require '@)

;; Create a rectangle prototype, extending the root object @.
;; Convention: prefix "class" variable names with @.
(defvar @rectangle (@extend :width nil :height nil))

;; The @ function is used to access properties of an object, following
;; the prototype chain breadth-first as necessary. An error is thrown
;; if the property has not been defined.
(@ @rectangle :width) ; => nil

;; The @ function is setf-able. Assignment *always* happens on the
;; immediate object, never on a parent prototype.
(setf (@ @rectangle :width) 0)
(setf (@ @rectangle :height) 0)

;; Define the method :area on @rectangle.
;; The first argument is this/self. Convention: call it @@.
(setf (@ @rectangle :area) (lambda (@@) (* (@ @@ :width) (@ @@ :height))))

;; Convenience macro def@ for writing methods. Symbols like @: will be
;; replaced by lookups on @@. The following is equivalent to the above
;; definition.
(def@ @rectangle :area ()
  (* @:width @:height))

Multiple Inheritance

;; Create a color mix-in prototype
(defvar @colored (@extend :color (list)))

;; The @: variables are setf-able, too.
(def@ @colored :mix (color)
  (push color @:color))

;; Create a colored rectangle from the prototypes.
(defvar foo (@extend @colored @rectangle :width 10 :height 4))

;; @! is used to call methods. The object itself is passed as the
;; first argument to the function stored on that prototype's property.
(@! foo :area)  ; => 40
(@! foo :mix :red)
(@! foo :mix :blue)
(@ foo :color)  ; => (:blue :red)

;; @: variables are turned into method calls when in function position.
(def@ foo :describe ()
  (format "{color: %s, area: %d}" @:color (@:area)))

(@! foo :describe)  ; => "{color: (:blue :red), area: 40}"

Constructors and Super Methods

;; By convention, constructors are the :init method. The @^:
;; "variables" are used to access super methods, including :init. Use
;; this to chain constructors and methods up the prototype chain (like
;; CLOS's `call-next-method').
(def@ @rectangle :init (width height)
  (@^:init)
  (setf @:width width @:height height))

;; The :new method on @ extends @@ with a new object and calls :init
;; on it with the provided arguments.
(@! (@! @rectangle :new 13.2 2.1) :area) ; => 27.72

Dynamic Property Getters and Setters

;; If a property is not found in the prototype chain, the :get method
;; is used to determine the value.
(let ((obj (@extend)))
  (def@ obj :get (property)
    (format "got %s" property))
  (@ obj :foo))
; => "got :foo"

;; The :get method on @, the default getter, produces an error if a
;; property is unbound. If you would rather unbound properties return
;; nil mix in @soft-get, which provides an alternate default :get
;; method.
(@ (@extend @rectangle @soft-get) :foo) ; => nil

;; Properties are assigned using the :set method. The :set method on @
;; does standard property assignment as would be expected. This can be
;; overridden for custom assignment behavior.
(let ((foo-only (@extend)))
  (def@ foo-only :set (property value)
    (if (string-match-p "^:foo" (prin1-to-string property))
        (@^:set property value)  ; supermethod
      (error "Only :foo* properties allowed!")))
  (setf (@ foo-only :foo-bar) 'a)  ; ok
  (setf (@ foo-only :bar) 'b))     ; ERROR

;; Using this, an @immutable prototype mixin is provided that
;; disallows all property assignments.
(setf (@ (@extend @immutable) :foo) 'a)  ; ERROR

The @vector prototype under lib/ shows how these can be useful for providing pseudo-properties.

;; Built on :set, a @watchable mixin is provided for observing all of
;; the changes to any object.
(let ((history ())
      (obj (@extend @watchable)))
  (@! obj :watch (lambda (obj prop new) (push (list property new) history)))
  (setf (@ obj :foo) 0)
  (setf (@ obj :foo) 1)
  (setf (@ obj :bar) 'a)
  (setf (@ obj :bar) 'b)
  history)
; => ((:bar b) (:bar a) (:foo 1) (:foo 0))

Reflection

;; @is is the classical "instanceof" operator. It works on any type of
;; object in both positions.
(@is foo @colored)   ; => t
(@is foo @rectangle) ; => t
(@is foo foo)        ; => t
(@is foo @)          ; => t
(@is foo (@extend))  ; => nil
(@is [1 2 3] @)      ; => nil

;; The :is method on @ can also be used for this.
(@! @colored :is @)    ; => t
(@! foo :is @colored)  ; => t

;; The :keys method on @ can be used to list the keys on an object.
(@! foo :keys)  ; => (:proto :width :height :color)

Syntax Highlighting

The library provides syntax highlighting for 'def@' and '@:' variables in emacs-lisp-mode, so the above @ uses will look more official in an Emacs buffer.

at-el's People

Contributors

purcell avatar sharad avatar skeeto avatar swsnr avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.