This is Common Lisp, which I am not an expert in:
;;; Stupid CLOS example.
(defgeneric x (point)) ; make X a method
(defgeneric y (point)) ; make Y a method
(defclass rect-point ()
((x :accessor x :initarg :x)
(y :accessor y :initarg :y)))
(defclass polar-point ()
((radius :accessor radius :initarg :radius)
(angle :accessor angle :initarg :angle)))
(defmethod x ((p polar-point))
(* (radius p) (cos (angle p))))
(defmethod y ((p polar-point))
(* (radius p) (sin (angle p))))
(defgeneric move-by (point Δx Δy))
(defmethod move-by ((p rect-point) Δx Δy)
(incf (x p) Δx)
(incf (y p) Δy))
(defmethod move-by ((p polar-point) Δx Δy)
(let ((x (+ (x p) Δx)) (y (+ (y p) Δy)))
(setf (radius p) (sqrt (+ (* x x) (* y y)))
(angle p) (atan y x))))
(defmethod print-object ((p polar-point) stream) ; standard method print-object
(format stream "@~a<~a" (radius p) (angle p)))
(defvar p1 (make-instance 'rect-point :x 3 :y 4))
(defvar p2 (make-instance 'polar-point :radius 1 :angle 1.047))
;; prints (3, 4) → (4, 5)
(format t "(~a, ~a) → " (x p1) (y p1))
(move-by p1 1 1)
(format t "(~a, ~a)~%" (x p1) (y p1))
;; prints @1<1.047 (0.500171, 0.8659266) → @1.9318848<0.7853087 (1.366171, 1.3659266)
(format t "~a (~a, ~a) → " p2 (x p2) (y p2))
(move-by p2 .866 .5)
(format t "~a (~a, ~a)~%" p2 (x p2) (y p2))
Here's a similar program in OCaml, which I am also not an expert in, using pattern-matching functions instead of methods, and avoiding mutation: (* Stupid OCaml example. *)
type point = Rect of float * float | Polar of float * float
let x = function
| Rect(x, y) -> x
| Polar(r, theta) -> r *. Float.cos theta
let y = function
| Rect(x, y) -> y
| Polar(r, theta) -> r *. Float.sin theta
let moved_by = fun dx dy ->
function
| Rect(x, y) -> Rect(x +. dx, y +. dy)
| p ->
let x = dx +. x p and y = dy +. y p in
Polar(Float.sqrt(x *. x +. y *. y),
Float.atan2 y x)
let string_of_point = function
| Rect(x, y) -> Printf.sprintf "Rect(%f, %f)" x y
| Polar(r, theta) -> Printf.sprintf "Polar(%f, %f)" r theta
;;
print_endline(string_of_point(moved_by 1. 2. (Rect(3., 4.)))) ;
print_endline(string_of_point(moved_by 0.866 0.5 (Polar(1., 1.047))))I think this more function-focused approach is more elegant, but also more extensible and possibly less verbose when dealing with multiple classes that share the same interface.
> the same as in Python except that you also have to define the generic function itself before you can define the methods. As a side note, though it's not terribly important, the "defgeneric" can be omitted if you don't care to specify docstring or any special behavior.
How would you classify Ruby? You can reopen a class and add more methods to it at any time.