2013-11-19 3 views
3

От чтения лисповских книг, которую я помню, что они показали пример метода диспетчера ООП стиле, основанный на закрытиях:Как работает интеллектуальное завершение кода в схеме?

(defun create-object() 
    (let ((val 0) 
     (get (lambda() val)) 
     (set (lambda (new-val) (setq val new-val))) 
     (inc (lambda() (setq val (+ 1 val))))) 
    (lambda (method) 
     (cond ((eq method 'get) 
      get) 
      ((eq method 'set) 
      set) 
      ((eq method 'inc) 
      inc))))) 

(let ((obj (create-object))) 
    (funcall (obj 'set) 1) 
    (funcall (obj 'inc)) 
    (funcall (obj 'get))) ;; 2 

Поскольку это просто функция строки символа аргумент а, я думаю, код Intel выиграл» Здесь вам будет очень полезной помощь, не заполняя имена методов или их подписи. (Сравните с аналогичным объектом JavaScript.)

Является ли эта проблема в целом решена? Как вы программируете объектную систему в Схеме, чтобы редактор (например, Emacs) мог быть более интеллектуальным с вашим кодом?

P.S. Пример может быть недействительным кодом схемы, но вы должны получить эту идею.

+1

вы могли бы иметь специальную функцию, как в Python '__dir__' который будет возвращать все возможные ключи, может использоваться на объекте. –

+2

Просто потому, что OOP _can_ будет реализован таким образом, не означает, что он обычно _is_. Но даже здесь использование 'eq' в строках обычно не является хорошей идеей, поэтому было бы более распространено сравнивать символы (например,' (eq method 'get) ') и выполнять завершение на основе символов, которые читатель видел просто. –

ответ

3

Я сделал для вас начальный код. Это для Emacs Lisp, но это должно быть очень легко переносимым для Scheme.

Вот ваш пример использования:

(defun create-object() 
    (lexical-let* ((val 0) 
       (get (lambda() val)) 
       (set (lambda(x) (setq val x)))) 
    (generate-dispatch-table get set))) 

(setq obj (create-object)) 
(funcall (funcall obj 'get)) 
;; => 0 
(funcall (funcall obj 'set) 1) 
;; => 1 
(funcall (funcall obj 'get)) 
;; => 1 
(scheme-completions obj) 
;; => (get set) 

А вот как это реализовано:

(defmacro generate-dispatch-table (&rest members) 
    `(lambda (method) 
    (cond ,@(mapcar 
       (lambda (x) `((eq method ',x) ,x)) members)))) 

(defun collect (pred x) 
    (when (and x (listp x)) 
    (let ((y (funcall pred x)) 
      (z (append 
       (collect pred (car x)) 
       (collect pred (cdr x))))) 
     (if y 
      (append (list y) z) 
     z)))) 

(defun scheme-completions (obj) 
    (collect 
    (lambda(x) (and (eq (car x) 'eq) 
       (eq (cadr x) 'method) 
       (eq (caaddr x) 'quote) 
       (cadr (caddr x)))) 
    obj)) 

А вот простой визуальный интерфейс для доработок:

(require 'helm) 
(defun scheme-completions-helm() 
    (interactive) 
    (let ((y (and 
       (looking-back "(funcall \\([^ ]*\\) +") 
       (intern-soft (match-string 1))))) 
    (when y 
     (helm :sources 
      `((name . "members") 
       (candidates . ,(scheme-completions (eval y))) 
       (action . (lambda(x) (insert "'" x)))))))) 
+0

Я принимаю этот ответ, потому что он дает конкретный пример того, как объектная система может сделать редактор осведомленным о системе. Если я могу суммировать, 1) объект должен иметь отдельную подпись для доступа к членам и 2) интерфейс для перечисления * всех * его членов. – katspaugh

3

Я не пользователь Emacs, но использую DrRacket, и у него есть объектная система, и я делаю то, что должен сделать IDE, но я знаю, что Emacs очень настраиваемый, поскольку он использует elisp, чтобы вы могли поддерживать собственный синтаксис как в подсветке синтаксиса, так и в завершении табуляции. Так что вы делаете:

  1. Сделать свой собственный объект системы
  2. Редактировать ваш редактор Emacs, чтобы делать то, что вы хотите

Многие из моих коллег использовать его и они фиксируют их Emacs такими способами.

Другое дело, этот вопрос заставляет меня задуматься о том, где упоминаются разные подходы и даже похожий код на тот, который вы опубликовали, в качестве примера. Это хорошо читать.

+0

Спасибо за ссылку, это действительно интересно! В частности, страница о продвинутых объектных системах. – katspaugh

2

Я хотел бы избежать двойного понятия символов в create-object через obarray. Кроме того, интерфейсом объекта являются все функции. Поэтому используйте fset и избегайте двойного funcall.

(defun create-object() 
    (lexical-let (val 
     (_oa (make-vector 11 0))) 
    (fset (intern "get" _oa) (lambda() val)) 
    (fset (intern "inc" _oa) (lambda() (incf val))) 
    (fset (intern "set" _oa) (lambda (new-val) (setq val new-val))) 
    (lambda (method &rest args) 
     (apply 'funcall (intern (symbol-name method) _oa) args)))) 


(fset 'obj1 (create-object)) 
(fset 'obj2 (create-object)) 

(obj1 'set 1) 
(obj2 'set 2) 

(obj1 'inc) 
(obj2 'inc) 
(obj2 'inc) 

(obj2 'get) 
(obj1 'get) 

Пример для наследования:

(defun create-object() 
    (lexical-let (val 
     (_oa (make-vector 11 0))) 
    (fset (intern "get" _oa) (lambda() val)) 
    (fset (intern "inc" _oa) (lambda() (incf val))) 
    (fset (intern "set" _oa) (lambda (new-val) (setq val new-val))) 
    (lambda (method &rest args) 
     (apply 'funcall (or (intern-soft (symbol-name method) _oa) 
       (error "Undefined function: %s" method)) 
     args)))) 


(defun create-object-add10() 
    (lexical-let ((base (create-object)) 
     (_oa (make-vector 11 0))) 
    (fset (intern "inc" _oa) (lambda() (funcall base 'set (+ (funcall base 'get) 10)))) 
    (lambda (method &rest args) 
     (let ((call (intern-soft (symbol-name method) _oa))) 
    (if call 
     (apply 'funcall call args) 
     (apply 'funcall base method args)))))) 

(fset 'obj1 (create-object)) 
(fset 'obj2 (create-object-add10)) 

(obj1 'set 1) 
(obj2 'set 2) 

(obj1 'inc) 
(obj2 'inc) 
(obj2 'inc) 

(obj2 'get) 
(obj1 'get) 

Определение create-object -как методов дополнительно должны быть поддержаны через макросы. Это не делается здесь.

Для больше функций, обратите внимание, есть CLOS совместимый с объектно-ориентированной системы в Emacs:

https://www.gnu.org/software/emacs/manual/html_node/eieio/index.html

+0

Спасибо, это хорошо! Я вижу, как obarray можно сравнить с объектом JavaScript и сделать функцию завершения (например, в ответе @ abo-abo) более простой для записи. – katspaugh

+1

Да и обрыв изменяет линейный доступ времени к постоянному доступу времени. Он приближается к внутреннему методу поиска. Это может иметь значение для больших объектов. – Tobias

+0

Я думаю, что закрытие - это не лучший способ реализовать объекты. Списки obarrays лучше, поскольку они позволяют наследовать. – Tobias