Object oriented programming¶
OOP in Antilisp relies on two independant abstractions: generics and classes
Generics are polymorphic functions with runtime multiple dispatch over the types of their arguments. The dispatched definitions of a generic are called methods.
Classes allow the user to define their own types and inherit methods and attributes defined on parent classes.
Generics¶
Generics are defined with (defgeneric)
, and implemented with (defmethod)
:
(defgeneric (to-list x))
# default implementation: raise an error
(defmethod (to-list x)
(error "undefined-method"
(+ "to-list is not defined for " (repr x))))
# define to-list for strings
# the last definition has higher priority over the previous ones
(defmethod (to-list (x string))
# convert the string to a list of characters
# ...
'())
Methods are stored in the method-store global dictionary.
Classes¶
Antilisp provides primitive OOP constructs to manipulate objects:
(make-class parent-classes ...attributes) # create and return a class
(make-instance class) # create and return an instance of class
(get-attr object 'attribute)
(set-attr object 'attribute value)
(is-instance object class)
# reflection
(is-subclass class-1 class-2)
(get-class-attributes class)
(get-class-env class)
(get-type object)
However, the recommended way of using objects is with the (defclass)
macro:
; the defclass macro automatically defines a constructor (make-vector), a type predicate (is-vector) and a generic type cast (to-vector)
(defclass vector ()
"a 2D vector class"
x y)
(let v (make-vector 1 2)
(set (x v) 3)
(print (x v))) ; prints 3
(defmethod (length (x vector))
; TODO
0)
(defmethod (to-vector (x cell))
(make-vector (car x) (car (cdr x))))
(let v (make-vector 1 2)
(print (is-vector v)))
; the class docstring is parsed by defclass to set the docstring
(print (get-doc vector)) ; "a 2D vector class"
Generic casts and type predicates have been defined for builtin types as well:
(to-symbol (+ "to-" (to-string 'vector))) # 'to-vector
(is-integer 0)
(is-string "string")
(is-symbol 'variable)
Many builtins, like (repr)
, (at)
or (iter)
, are implemented as generics. A special case is (+)
, which can accept any number of arguments, and internally uses the (add)
generic to add with type dispatch.