method.scm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Generic procedure package
  3. ; This is written in fairly portable Scheme. It needs:
  4. ; Scheme 48 low-level macros (explicit renaming), in one small place.
  5. ; (CALL-ERROR message proc arg ...) - signal an error.
  6. ; Record package and DEFINE-RECORD-TYPES macro.
  7. ; An object :RECORD-TYPE which is the record type descriptor for
  8. ; record type descriptors (record types are assumed to be records).
  9. ; This wouldn't be difficult to change.
  10. ; A RECORD? predicate (not essential - only for defining a DISCLOSE
  11. ; method for records).
  12. ; --------------------
  13. ; Simple types.
  14. ; More specific types have higher priorities. The priorities are used
  15. ; to establish the ordinary in which type predicates are called.
  16. (define-record-type simple-type :simple-type
  17. (really-make-simple-type supers predicate priority id)
  18. simple-type?
  19. (supers simple-type-superiors)
  20. (predicate simple-type-predicate)
  21. (priority simple-type-priority)
  22. (id simple-type-id)
  23. (more)) ;if needed later
  24. (define-record-discloser :simple-type
  25. (lambda (c) `(simple-type ,(simple-type-id c))))
  26. (define (make-simple-type supers predicate id)
  27. (make-immutable!
  28. (really-make-simple-type supers
  29. predicate
  30. (compute-priority supers)
  31. id)))
  32. (define (compute-priority supers)
  33. (if (null? supers)
  34. 0
  35. (+ (apply max (map %type-priority supers))
  36. *increment*)))
  37. (define *increment* 10)
  38. ; These two procedures will become generic later, but must exist early
  39. ; in order to be able to bootstrap the method definition mechanism.
  40. (define (%type-priority type)
  41. (cond ((simple-type? type)
  42. (simple-type-priority type))
  43. ((record-type? type)
  44. (record-type-priority type))
  45. (else (type-priority type)))) ;generic
  46. (define (%type-predicate type)
  47. (cond ((simple-type? type)
  48. (simple-type-predicate type))
  49. ((record-type? type)
  50. (record-predicate type))
  51. (else (type-predicate type)))) ;generic
  52. (define (%same-type? t1 t2)
  53. (or (eq? t1 t2)
  54. (if (simple-type? t1)
  55. #f
  56. (if (record-type? t1)
  57. #f
  58. (same-type? t1 t2)))))
  59. (define-syntax define-simple-type
  60. (syntax-rules ()
  61. ((define-simple-type ?name (?super ...) ?pred)
  62. (define ?name (make-simple-type (list ?super ...) ?pred '?name)))))
  63. ; --------------------
  64. ; Built-in Scheme types
  65. (define-simple-type :syntax () #f)
  66. (define-simple-type :values () #f) ;any number of values
  67. (define (value? x) #t)
  68. (define-simple-type :value (:values) value?)
  69. (define-simple-type :zero (:values) (lambda (x) #f))
  70. (define-simple-type :number (:value) number?)
  71. (define-simple-type :complex (:number) complex?)
  72. (define-simple-type :real (:complex) real?)
  73. (define-simple-type :rational (:real) rational?)
  74. (define-simple-type :integer (:rational) integer?)
  75. (define-simple-type :exact-integer (:integer)
  76. (lambda (n) (and (integer? n) (exact? n))))
  77. (define-simple-type :boolean (:value) boolean?)
  78. (define-simple-type :symbol (:value) symbol?)
  79. (define-simple-type :char (:value) char?)
  80. (define-simple-type :null (:value) null?)
  81. (define-simple-type :pair (:value) pair?)
  82. (define-simple-type :vector (:value) vector?)
  83. (define-simple-type :string (:value) string?)
  84. (define-simple-type :procedure (:value) procedure?)
  85. (define-simple-type :input-port (:value) input-port?)
  86. (define-simple-type :output-port (:value) output-port?)
  87. (define-simple-type :eof-object (:value) eof-object?)
  88. ; If there is no RECORD? predicate, do
  89. ; (define-simple-type :record (:value) value?)
  90. ; and change the DISCLOSE method for records to
  91. ; (or (disclose-record obj) (next-method)).
  92. (define-simple-type :record (:value) record?)
  93. ; If record types are not records, un-comment the following line.
  94. ; (define-simple-type :record-type (:value) record-type?)
  95. ; Given a record type, RECORD-TYPE-PRIORITY returns its priority.
  96. ; Here we establish that every record type is a direct subtype of the
  97. ; :RECORD type.
  98. (define record-type-priority
  99. (let ((r-priority
  100. (simple-type-priority (make-simple-type (list :record) #f #f))))
  101. (lambda (rt) r-priority)))
  102. ; --------------------
  103. ; Method-info records are triples <type-list, n-ary?, proc>.
  104. (define-record-type method-info :method-info
  105. (really-make-method-info types n-ary? proc)
  106. method-info?
  107. (types method-info-types)
  108. (n-ary? method-info-n-ary?)
  109. (proc method-info-proc))
  110. (define (make-method-info types n-ary? proc)
  111. (make-immutable! (really-make-method-info types n-ary? proc)))
  112. (define-record-discloser :method-info
  113. (lambda (info)
  114. `(method-info ,(method-info-types info) ,(method-info-n-ary? info))))
  115. ; --------------------
  116. ; Method lists
  117. ; A method list is a list of method-info records, sorted in order from
  118. ; most specific to least specific.
  119. (define (empty-method-list) '())
  120. ; insert-method inserts an entry into a method list so that the most
  121. ; specific methods come earliest in the list. The last method should
  122. ; be a default method or error signal(l)er.
  123. (define (insert-method info ms)
  124. (let recur ((ms ms))
  125. (if (null? ms)
  126. (cons info ms)
  127. (if (more-specific? (car ms) info)
  128. (cons (car ms) (recur (cdr ms)))
  129. (cons info
  130. (if (same-applicability? (car ms) info)
  131. (cdr ms)
  132. ms))))))
  133. ; Replace an existing method with identical domain.
  134. (define (same-applicability? info1 info2)
  135. (and (every2 %same-type?
  136. (method-info-types info1)
  137. (method-info-types info2))
  138. (eq? (method-info-n-ary? info1) (method-info-n-ary? info2))))
  139. (define (every2 pred l1 l2)
  140. (if (null? l1)
  141. (null? l2)
  142. (if (null? l2)
  143. #f
  144. (and (pred (car l1) (car l2)) (every2 pred (cdr l1) (cdr l2))))))
  145. ; This interacts with methods->perform, below.
  146. ; In this version, it's supposed to be a total order.
  147. (define (more-specific? info1 info2)
  148. (let ((t1 (method-info-types info1))
  149. (t2 (method-info-types info2)))
  150. (let ((l1 (length t1))
  151. (l2 (length t2))
  152. (foo? (and (not (method-info-n-ary? info1))
  153. (method-info-n-ary? info2))))
  154. (if (= l1 l2)
  155. (or foo?
  156. (let loop ((l1 t1)
  157. (l2 t2))
  158. (if (null? l2)
  159. #f
  160. (or (more-specific-type? (car l1) (car l2))
  161. (and (%same-type? (car l1) (car l2))
  162. (loop (cdr l1) (cdr l2)))))))
  163. (and (> l1 l2)
  164. foo?)))))
  165. (define (more-specific-type? t1 t2)
  166. (> (%type-priority t1) (%type-priority t2)))
  167. ; --------------------
  168. ; A method table is a cell that contains a method list.
  169. ; Note that the method table is not reachable from the generic
  170. ; procedure. This means good things for the GC.
  171. (define-record-type method-table :method-table
  172. (really-make-method-table methods prototype
  173. generic get-perform set-perform! id)
  174. method-table?
  175. (methods method-table-methods set-method-table-methods!)
  176. (prototype method-table-prototype)
  177. (generic make-generic)
  178. (get-perform method-table-get-perform)
  179. (set-perform! method-table-set-perform!)
  180. (id method-table-id))
  181. (define-record-discloser :method-table
  182. (lambda (t) `(method-table ,(method-table-id t))))
  183. (define (make-method-table id . option)
  184. (let* ((prototype (if (null? option)
  185. (make-method-info '() #t #f)
  186. (car option)))
  187. (mtable (call-with-values make-cell-for-generic
  188. (lambda (generic get-perform set-perform!)
  189. (really-make-method-table '()
  190. prototype
  191. generic
  192. get-perform
  193. set-perform!
  194. id)))))
  195. (set-final-method!
  196. mtable
  197. (lambda (next-method . args)
  198. (apply call-error "invalid or unimplemented operation"
  199. id args)))
  200. mtable))
  201. (define (make-cell-for-generic)
  202. (let ((perform #f))
  203. ;; PERFORM always caches (METHODS->PERFORM method-list prototype).
  204. (values (lambda args (perform args)) ;Generic proc
  205. (lambda () perform)
  206. (lambda (new) (set! perform new)))))
  207. (define (add-to-method-table! mtable info)
  208. (let ((l (insert-method info (method-table-methods mtable))))
  209. (set-method-table-methods! mtable l)
  210. ((method-table-set-perform! mtable)
  211. (methods->perform l (method-table-prototype mtable)))))
  212. (define (set-final-method! mtable proc)
  213. (add-to-method-table! mtable
  214. (make-method-info '()
  215. #t
  216. proc)))
  217. (define (apply-generic mtable args)
  218. ;; (apply (make-generic mtable) args)
  219. (((method-table-get-perform mtable)) args)) ;+++
  220. ; DEFINE-GENERIC
  221. (define-syntax define-generic
  222. (syntax-rules ()
  223. ((define-generic ?name ?mtable-name)
  224. (begin (define ?mtable-name (make-method-table '?name))
  225. (define ?name (make-generic ?mtable-name))))
  226. ((define-generic ?name ?mtable-name (?spec . ?specs))
  227. (begin (define ?mtable-name
  228. (make-method-table '?name
  229. (method-info ?name ("next" next-method
  230. ?spec . ?specs)
  231. (next-method))))
  232. (define ?name (make-generic ?mtable-name))))))
  233. ; --------------------
  234. ; Method combination.
  235. ; Here is the specification:
  236. ;(define (apply-generic mtable args)
  237. ; (let loop ((ms (method-table-methods mtable)))
  238. ; (let ((next-method (lambda () (loop (cdr ms)))))
  239. ; (if (let test ((ts (method-info-types (car ms)))
  240. ; (args args))
  241. ; (if (null? ts)
  242. ; (or (null? args)
  243. ; (method-info-n-ary? (car ms)))
  244. ; (and ((%type-predicate (car ts)) (car args))
  245. ; (test (cdr ts) (cdr args)))))
  246. ; (apply (method-info-proc (car ms))
  247. ; next-method
  248. ; args)
  249. ; (next-method)))))
  250. ; (perform arg-list)
  251. ; (apply proc next-method-thunk arg-list)
  252. ; This version of METHODS->PERFORM simply marches through all the
  253. ; methods, looking for one that handles the operation.
  254. ; The prototype is currently ignored, but it could be put to good use.
  255. (define (methods->perform l prototype)
  256. (let recur ((l l))
  257. (let* ((info (car l))
  258. (proc (method-info-proc info)))
  259. (if (null? (cdr l))
  260. (last-action proc)
  261. (one-action (argument-sequence-predicate info)
  262. proc
  263. (recur (cdr l)))))))
  264. (define (last-action proc)
  265. (lambda (args)
  266. (apply proc #f args)))
  267. (define (one-action pred proc perform-next)
  268. (lambda (args)
  269. (if (pred args)
  270. (apply proc
  271. (lambda () (perform-next args)) ; next-method
  272. args)
  273. (perform-next args))))
  274. (define (argument-sequence-predicate info)
  275. (let recur ((types (method-info-types info)))
  276. (if (null? types)
  277. (if (method-info-n-ary? info) value? null?)
  278. (let ((pred (%type-predicate (car types)))
  279. (check-rest (recur (cdr types))))
  280. (if (eq? pred value?)
  281. (check-for-next check-rest) ;+++
  282. (check-next pred check-rest))))))
  283. (define (check-for-next check-rest)
  284. (lambda (args)
  285. (if (null? args)
  286. #f
  287. (check-rest (cdr args)))))
  288. (define (check-next pred check-rest)
  289. (lambda (args)
  290. (if (null? args)
  291. #f
  292. (if (pred (car args))
  293. (check-rest (cdr args))
  294. #f))))
  295. ; --------------------
  296. ; METHOD-INFO macro.
  297. ; Returns a method-info record.
  298. ; You can specify the name of the next-method parameter by saying
  299. ; (method-info my-name (x y "next" n) body ...)
  300. ; Otherwise, the next-method parameter will be named next-method.
  301. ; Just pretend it's Dylan and that #next reads as "next".
  302. (define-syntax method-info
  303. (syntax-rules ()
  304. ((method-info ?id ?formals ?body ...)
  305. (method-internal ?formals () () #f ?id ?body ...))))
  306. (define-syntax method-internal
  307. (syntax-rules ()
  308. ((method-internal ((?formal1 ?type1) . ?specs)
  309. (?formal ...) (?type ...) ?next
  310. . ?rest)
  311. (method-internal ?specs
  312. (?formal ... ?formal1) (?type ... ?type1) ?next
  313. . ?rest))
  314. ((method-internal ("next" ?next . ?specs)
  315. (?formal ...) (?type ...) ?ignore
  316. . ?rest)
  317. (method-internal ?specs
  318. (?formal ...) (?type ...) ?next
  319. . ?rest))
  320. ((method-internal (?spec . ?specs)
  321. (?formal ...) (?type ...) ?next
  322. . ?rest)
  323. (method-internal ?specs
  324. (?formal ... ?spec) (?type ... :value) ?next
  325. . ?rest))
  326. ((method-internal ?rest
  327. (?formal ...) (?type ...) ?next
  328. ?id ?body ...)
  329. (make-method-info (list ?type ...)
  330. (not (null? '?rest))
  331. (let ((?id (with-next-method ?next (?formal ... . ?rest)
  332. ?body ...)))
  333. ;; The (let ...) is a hack for the Scheme 48
  334. ;; byte code compiler, which will remember
  335. ;; ?id as the procedure's name. This should
  336. ;; aid debugging a little bit since the name
  337. ;; shows up in backtraces and the inspector.
  338. ?id)))))
  339. ; Non-hygienic, a la Dylan
  340. (define-syntax with-next-method
  341. (cons (lambda (e r c)
  342. (let ((next (or (cadr e) 'next-method)))
  343. `(,(r 'lambda) (,next ,@(caddr e))
  344. ,@(cdddr e))))
  345. '(lambda)))
  346. ; DEFINE-METHOD macro.
  347. (define-syntax define-method
  348. (syntax-rules ()
  349. ((define-method ?mtable ?formals ?body ...)
  350. (add-method! ?mtable
  351. (method-info ?mtable ?formals ?body ...)))))
  352. (define-generic add-method! &add-method! (mtable info))
  353. (let ((info
  354. (method-info add-method! ((mtable :method-table) (info :method-info))
  355. (add-to-method-table! mtable info))))
  356. (add-to-method-table! &add-method! info))
  357. ; --------------------
  358. ; Generic functions on types: sort of a meta-object protocol, huh?
  359. (define-generic type-predicate &type-predicate (t))
  360. (define-method &type-predicate ((t :record-type)) (record-predicate t))
  361. (define-method &type-predicate ((t :simple-type)) (simple-type-predicate t))
  362. (define-generic type-priority &type-priority (t))
  363. (define-method &type-priority ((t :record-type)) (record-type-priority t))
  364. (define-method &type-priority ((t :simple-type)) (simple-type-priority t))
  365. (define-generic type-superiors &type-superiors (t))
  366. (define-method &type-superiors ((t :record-type)) (list :record))
  367. (define-method &type-superiors ((t :simple-type)) (simple-type-superiors t))
  368. ; Type equivalence
  369. (define-generic same-type? &same-type? (t1 t2))
  370. (define-method &same-type? (t1 t2) (eq? t1 t2))
  371. (define-method &same-type? ((t1 :simple-type) (t2 :simple-type))
  372. (and (eq? (simple-type-predicate t1) (simple-type-predicate t2))
  373. (eq? (simple-type-id t1) (simple-type-id t2)))) ;?
  374. ; --------------------
  375. ; Singleton types.
  376. (define-record-type singleton :singleton
  377. (singleton value)
  378. (value singleton-value))
  379. (define-record-discloser :singleton
  380. (lambda (s) `(singleton ,(singleton-value s))))
  381. (define (compare-to val)
  382. (lambda (x) (eqv? x val)))
  383. (define-method &type-predicate ((s :singleton))
  384. (compare-to (singleton-value s)))
  385. (define-method &type-priority ((s :singleton)) 1000000)
  386. (define-method &same-type? ((s1 :singleton) (s2 :singleton))
  387. (eqv? (singleton-value s1) (singleton-value s2)))
  388. ; --------------------
  389. ; DISCLOSE
  390. ; A generic procedure for producing printed representations.
  391. ; Should return one of
  392. ; - A list (symbol info ...), to be printed as #{Symbol info ...}
  393. ; - #f, meaning no information available on how to print.
  394. ; This is intended to be used not only by write and display, but also by
  395. ; the pretty printer.
  396. (define-generic disclose &disclose (x))
  397. (define-method &disclose (obj) #f)
  398. (define-method &disclose ((obj :record))
  399. (or (disclose-record obj)
  400. '(record)))
  401. (define-method &add-method! ((d (singleton &disclose)) info)
  402. (let ((t (car (method-info-types info))))
  403. (if (record-type? t)
  404. (define-record-discloser t (proc->discloser (method-info-proc info)))
  405. (next-method))))
  406. (define (proc->discloser proc)
  407. (lambda (arg)
  408. (proc (lambda () #f) arg)))
  409. ;(define-method &disclose ((s :singleton))
  410. ; `(singleton ,(singleton-value s)))