LINUX.ORG.RU

История изменений

Исправление quasimoto, (текущая версия) :

(or a b)

http://en.wikipedia.org/wiki/Tagged_union.

В CL это значит, что суммы нужно заворачивать в структуры:

(defstruct my-variant
  (variant nil :type (or number string)))

(defmacro with-my-variant ((var my-variant) for-number for-string)
  `(let ((,var (my-variant-variant ,my-variant)))
     (etypecase ,var
       (number ,for-number)
       (string ,for-string))))

(defmethod map-as-number ((my-variant my-variant) fn)
  (with-my-variant (var my-variant) (funcall fn var) var))

(defmethod map-as-string ((my-variant my-variant) fn)
  (with-my-variant (this my-variant) this (funcall fn this)))

(map-as-number (make-my-variant :variant 123) #'1+) ; => 124
(map-as-number (make-my-variant :variant "123") #'1+) ; => "123"
(map-as-string (make-my-variant :variant 123) #'length) ; => 123
(map-as-string (make-my-variant :variant "123") #'length) ; => 3

Следующим шагом будет написание макроса defunion так, чтобы

(defunion number-or-string number string)

раскрывался в код структуры и обобщённых методов работы с данным вариантом (которые потом может неявно использовать, например, паттерн-матчинг).

ломает всю красивую картину ООП и делает диспетчеризацию по произвольным типам принципиально невозможной

Чем сишный union или boost::variant или std.variant из D ломает ООП? Если говорить про «наследники класса как варианты его типа», то sealed class hierarchy - и тоже ничего не ломает.

Исправление quasimoto, :

(or a b)

http://en.wikipedia.org/wiki/Tagged_union.

В CL это значит, что суммы нужно заворачивать в структуры:

(defstruct my-variant
  (variant nil :type (or number string)))

(defmacro with-my-variant ((var my-variant) for-number for-string)
  `(let ((,var (my-variant-variant ,my-variant)))
     (etypecase ,var
       (number ,for-number)
       (string ,for-string))))

(defmethod map-as-number ((my-variant my-variant) fn)
  (with-my-variant (var my-variant) (funcall fn var) var))

(defmethod map-as-string ((my-variant my-variant) fn)
  (with-my-variant (this my-variant) this (funcall fn this)))

(map-as-number (make-my-variant :variant 123) #'1+) ; => 124
(map-as-number (make-my-variant :variant "123") #'1+) ; => "123"
(map-as-string (make-my-variant :variant 123) #'length) ; => 123
(map-as-string (make-my-variant :variant "123") #'length) ; => 3

Следующим шагом будет написание макроса defunion так, чтобы

(defunion number-or-string number string)

раскрывался в код структуры и обобщённых методов работы с данным вариантом (которые потом может неявно использовать, например, паттерн-матчинг).

ломает всю красивую картину ООП и делает диспетчеризацию по произвольным типам принципиально невозможной

Чем сишный union или boost::variant или std.variant из D ломает ООП? Если говорить про «наследники класса как варианты его типа», то sealed/final для базового класса - и тоже ничего не ломает.

Исходная версия quasimoto, :

(or a b)

http://en.wikipedia.org/wiki/Tagged_union.

В CL это значит, что суммы нужно заворачивать в структуры:

(defstruct my-variant
  (variant nil :type (or number string)))

(defmacro with-my-variant ((var my-variant) for-number for-string)
  `(let ((,var (my-variant-variant ,my-variant)))
     (etypecase ,var
       (number ,for-number)
       (string ,for-string))))

(defmethod map-as-number ((my-variant my-variant) fn)
  (with-my-variant (var my-variant) (funcall fn var) var))

(defmethod map-as-string ((my-variant my-variant) fn)
  (with-my-variant (this my-variant) this (funcall fn this)))

(map-as-number (make-my-variant :variant 123) #'1+) ; => 124
(map-as-number (make-my-variant :variant "123") #'1+) ; => "123"
(map-as-string (make-my-variant :variant 123) #'1+) ; => 123
(map-as-string (make-my-variant :variant "123") #'length) ; => 3

Следующим шагом будет написание макроса defunion так, чтобы

(defunion number-or-string number string)

раскрывался в код структуры и обобщённых методов работы с данным вариантом (которые потом может неявно использовать, например, паттерн-матчинг).

ломает всю красивую картину ООП и делает диспетчеризацию по произвольным типам принципиально невозможной

Чем сишный union или boost::variant или std.variant из D ломает ООП? Если говорить про «наследники класса как варианты его типа», то sealed/final для базового класса - и тоже ничего не ломают.