/usr/share/racket/pkgs/swindle/tiny-clos.rkt is in racket-common 6.3-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 | ;;; Heavily hacked by Eli Barzilay: Maze is Life! (eli@barzilay.org)
;;> This module is the core object system. It is a heavily hacked version
;;> of the original Tiny-CLOS code from Xerox, but it has been fitted to
;;> Racket, optimized and extended. See the source file for a lot of
;;> details about how the CLOS magic is created.
;;>
;;> [There is one difference between Swindle and Tiny-CLOS: the meta object
;;> hierarchy is assumed to be using only single inheritance, or if there is
;;> multiple inheritance then the built in meta objects should come first to
;;> make the slots allocated in the same place. This should not be a
;;> problem in realistic situations.]
;;; Original copyright:
;;; ***************************************************************************
;;; Copyright (c) 1992 Xerox Corporation. All Rights Reserved.
;;;
;;; Use, reproduction, and preparation of derivative works are permitted. Any
;;; copy of this software or of any derivative work must include the above
;;; copyright notice of Xerox Corporation, this paragraph and the one after it.
;;; Any distribution of this software or derivative works must comply with all
;;; applicable United States export control laws.
;;; This software is made available AS IS, and XEROX CORPORATION DISCLAIMS ALL
;;; WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND
;;; NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY LIABILITY FOR
;;; DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS EXPRESSLY DISCLAIMED,
;;; WHETHER ARISING IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT
;;; LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED OF THE POSSIBILITY OF SUCH
;;; DAMAGES.
;;; ***************************************************************************
#lang s-exp swindle/base
;;; A very simple CLOS-like language, embedded in Scheme, with a simple MOP.
;;; The features of the default base language are:
;;; * Classes, with instance slots, but no slot options.
;;; * Multiple-inheritance.
;;; * Generic functions with multi-methods and class specializers only.
;;; * Primary methods and call-next-method; no other method combination.
;;; * Uses Scheme's lexical scoping facilities as the class and generic
;;; function naming mechanism. Another way of saying this is that class,
;;; generic function and methods are first-class (meta)objects.
;;;
;;; While the MOP is simple, it is essentially equal in power to both MOPs in
;;; AMOP. This implementation is not at all optimized, but the MOP is designed
;;; so that it can be optimized. In fact, this MOP allows better optimization
;;; of slot access extenstions than those in AMOP.
;;;
;;; In addition to calling a generic, the entry points to the default base
;;; language are:
;;;
;;; (MAKE-CLASS list-of-superclasses list-of-slot-names)
;;; (MAKE-GENERIC-FUNCTION)
;;; (MAKE-METHOD list-of-specializers procedure)
;;; (ADD-METHOD generic method)
;;;
;;; (MAKE class . initargs)
;;; (INITIALIZE instance initargs) ; Add methods to this, dont call directly.
;;;
;;; (SLOT-REF object slot-name)
;;; (SLOT-SET! object slot-name new-value)
;;; (SLOT-BOUND? object slot-name)
;;;
;;; So, for example, one might do:
;;; (define <position> (make-class (list <object>) (list 'x 'y)))
;;; (add-method initialize
;;; (make-method (list <position>)
;;; (lambda (call-next-method pos initargs)
;;; (for-each (lambda (initarg-name slot-name)
;;; (slot-set! pos slot-name
;;; (getarg initargs initarg-name 0)))
;;; '(x y)
;;; '(x y)))))
;;; (set! p1 (make <position> 'x 1 'y 3))
;;;
;;; NOTE! Do not use EQUAL? to compare objects! Use EQ? or some hand written
;;; procedure. Objects have a pointer to their class, and classes are
;;; circular structures, and...
;;;
;;; The introspective part of the MOP looks like the following. Note that
;;; these are ordinary procedures, not generics.
;;; * CLASS-OF
;;; INSTANCE-OF?
;;; SUBCLASS?
;;; * CLASS-DIRECT-SUPERS
;;; CLASS-DIRECT-SLOTS
;;; CLASS-CPL
;;; CLASS-SLOTS
;;; CLASS-NAME
;;; * GENERIC-METHODS
;;; GENERIC-ARITY
;;; GENERIC-NAME
;;; GENERIC-COMBINATION
;;; * METHOD-SPECIALIZERS
;;; METHOD-PROCEDURE
;;; METHOD-NAME
;;;
;;; The intercessory protocol looks like (generics in uppercase):
;;; ELI: All of these are generic functions now!
;;; MAKE
;;; ALLOCATE-INSTANCE
;;; INITIALIZE (really a base-level generic)
;;; class initialization
;;; COMPUTE-CPL
;;; COMPUTE-SLOTS
;;; COMPUTE-GETTER-AND-SETTER
;;; method initialization
;;; COMPUTE-APPLY-METHOD
;;; ADD-METHOD (Notice this is not a generic!) [eli: yes!]
;;; COMPUTE-APPLY-GENERIC
;;; COMPUTE-METHODS
;;; COMPUTE-METHOD-MORE-SPECIFIC?
;;; COMPUTE-APPLY-METHODS
;;; OK, now let's get going. But, as usual, before we can do anything
;;; interesting, we have to muck around for a bit first. First, we need to
;;; load the support library. [-- replaced with a module.]
(require swindle/misc
racket/undefined)
;; This is a convenient function for raising exceptions
(define (raise* exn-maker fmt . args)
(let ([sym (and (symbol? fmt)
(begin0 fmt
(when (null? args) (error 'raise* "got too few arguments"))
(set! fmt (car args)) (set! args (cdr args))))]
[fmt-num (- (length args) (procedure-arity exn-maker) -2)])
(when (< fmt-num 0)
(error 'raise* "got too few arguments"))
(let loop ([fmt-args '()] [args args] [a fmt-num])
(if (zero? a)
(raise (exn-maker
(if sym
(apply format (concat "~s: " fmt) sym (reverse fmt-args))
(apply format fmt (reverse fmt-args)))
(current-continuation-marks) . args))
(loop (cons (car args) fmt-args) (cdr args) (sub1 a))))))
;; A simple topological sort.
;; It's in this file so that both TinyClos and Objects can use it.
;; This is a fairly modified version of code I originally got from Anurag
;; Mendhekar <anurag@moose.cs.indiana.edu>.
(define (compute-std-cpl c get-direct-supers)
(top-sort (build-transitive-closure get-direct-supers c)
(build-constraints get-direct-supers c)
(std-tie-breaker get-direct-supers)))
(define (top-sort elements constraints tie-breaker)
(let loop ([elements elements] [constraints constraints] [result '()])
(if (null? elements)
result
(let ([can-go-in-now
(filter (lambda (x)
(every (lambda (constraint)
(or (not (eq? (cadr constraint) x))
(memq (car constraint) result)))
constraints))
elements)])
(if (null? can-go-in-now)
(error 'top-sort "invalid constraints")
(let ([choice (if (null? (cdr can-go-in-now))
(car can-go-in-now)
(tie-breaker result can-go-in-now))])
(loop (filter (lambda (x) (not (eq? x choice))) elements)
constraints (append result (list choice)))))))))
(define (std-tie-breaker get-supers)
(lambda (partial-cpl min-elts)
(let loop ([pcpl (reverse partial-cpl)])
(let* ([current-elt (car pcpl)]
[ds-of-ce (get-supers current-elt)]
[common (filter (lambda (x) (memq x ds-of-ce)) min-elts)])
(if (null? common)
(if (null? (cdr pcpl))
(error 'std-tie-breaker "nothing valid") (loop (cdr pcpl)))
(car common))))))
(define (build-transitive-closure get-follow-ons x)
(let track ([result '()] [pending (list x)])
(if (null? pending)
result
(let ([next (car pending)])
(if (memq next result)
(track result (cdr pending))
(track (cons next result)
(append (get-follow-ons next) (cdr pending))))))))
(define (build-constraints get-follow-ons x)
(let loop ([elements (build-transitive-closure get-follow-ons x)]
[this-one '()]
[result '()])
(if (or (null? this-one) (null? (cdr this-one)))
(if (null? elements)
result
(loop (cdr elements)
(cons (car elements) (get-follow-ons (car elements)))
result))
(loop elements
(cdr this-one)
(cons (list (car this-one) (cadr this-one)) result)))))
;;; Then, we need to build what, in a more real implementation, would be the
;;; interface to the memory subsystem: instances and entities. The former are
;;; used for instances of instances of <class>; the latter are used for
;;; instances of instances of <entity-class>. In this MOP, none of this is
;;; visible to base- or MOP-level programmers.
;;; A few things to note, that have influenced the way all this is done:
;;; - R4RS doesn't provide a mechanism for specializing the
;;; behavior of the printer for certain objects.
;;; - Some Scheme implementations bomb when printing circular structures --
;;; that is, arrays and/or lists that somehow point back to themselves.
;;; So, the natural implementation of instances -- vectors whose first field
;;; point to the class -- is straight on out. Instead, we use a procedure to
;;; `encapsulate' that natural representation.
;;; Having gone that far, it makes things simpler to unify the way normal
;;; instances and entities are handled, at least in the lower levels of the
;;; system. Don't get faked out by this -- the user shouldn't think of normal
;;; instances as being procedures, they aren't. (At least not in this
;;; language.) If you are using this to teach, you probably want to hide the
;;; implementation of instances and entities from people.
;;>> ???
;;> This is Racket's `unspecified' value which is used as the default
;;> value for unbound slots. It is provided so you can check if a slot is
;;> unbound.
(define* ??? undefined)
(define unspecified-initializer (lambda args ???))
(define false-func (lambda args #f))
;; Basic allocation follows, all was in a single let, but this is not needed
;; with Racket's modules. Also modified to use simple structs for
;; everything, including entities since PLT has applicable struct objects.
(define-values (struct:instance make-instance instance? inst-ref inst-set!)
;; slots: applicable, class, function, slots-vector
(make-struct-type 'swindleobj #f 3 0 #f '() (current-inspector)
(lambda (o . args) (apply (instance-proc o) args))))
(defsubst (instance-class x) (inst-ref x 0))
(defsubst (instance-proc x) (inst-ref x 1))
(defsubst (instance-slots x) (inst-ref x 2))
(defsubst (set-instance-class! x c) (inst-set! x 0 c))
(defsubst (set-instance-proc! x p) (inst-set! x 1 p))
(defsubst (set-instance-slots! x s) (inst-set! x 2 s))
(defsubst (%instance-ref o f) (vector-ref (instance-slots o) f))
(defsubst (%instance-set! o f n) (vector-set! (instance-slots o) f n))
(define (%allocate-instance class nfields)
(make-instance class
(lambda args
(error 'instance
"an instance isn't a procedure -- can't apply it"))
(make-vector nfields ???)))
(define (%allocate-entity class nfields)
(letrec ([o (make-instance
class
(lambda args
(error 'entity
"tried to call an entity before its proc is set"))
(make-vector nfields ???))])
o))
;; This is used only once as part of bootstrapping the braid.
(define (set-instance-class-to-self! class)
(set-instance-class! class class))
;;>>...
;;> *** Low level functionality
;;> (These functions should be used with caution, since they make shooting
;;> legs in exotic ways extremely easy.)
;;>> (change-class! object new-class initargs ...)
;;> This operation changes the class of the given `object' to the given
;;> `new-class'. The way this is done is by creating a fresh instance of
;;> `new-class', then copying all slot values from `object' to the new
;;> instance for all shared slot names. Finally, the new instance's set
;;> of slots is used for the original object with the new class, so it
;;> preserves its identity.
(define* (change-class! obj new-class . initargs)
(let ([new (make new-class . initargs)]
[new-slots (%class-slots new-class)])
(dolist [slot (%class-slots (class-of obj))]
(when (and (not (eq? :class (getarg (cdr slot) :allocation :instance)))
(assq (car slot) new-slots))
(slot-set! new (car slot) (slot-ref obj (car slot)))))
(set-instance-slots! obj (instance-slots new))
(set-instance-class! obj new-class)))
;; This might be cute for some ugly hacks but not needed for now.
;; Copies the contents of source to target, making it an "alias" object. This
;; is no re-provided by clos.rkt, but maybe it will in the future...
;; (define* (copy-object-contents! target source)
;; (set-instance-class! target (instance-class source))
;; (set-instance-proc! target (instance-proc source))
;; (set-instance-slots! target (instance-slots source)))
;;>> (set-instance-proc! object proc)
;;> This function sets the procedure of an entity object. It is useful
;;> only for making new entity classes.
(provide set-instance-proc!) ; dangerous!
;; Basic allocation ends here.
;;>>...
;;> *** Basic functionality
;;>> (instance? x)
;;>> (object? x)
;;> These two are synonyms: a predicate that returns #t for objects that
;;> are allocated and managed by Swindle.
(provide instance?)
(define* object? instance?)
;;>> (class-of x)
;;> Return the class object of `x'. This will either be a Swindle class
;;> for objects, or a built-in class for other Scheme values.
;;; %allocate-instance, %allocate-entity, %instance-ref, %instance-set! and
;;; class-of are the normal interface, from the rest of the code, to the
;;; low-level memory system. One thing to take note of is that the protocol
;;; does not allow the user to add low-level instance representations. I have
;;; never seen a way to make that work.
;;; Note that this implementation of class-of assumes the name of a the
;;; primitive classes that are set up later.
(define* (class-of x)
;; This is an early version that will be modified when built-in types are
;; introduced later.
(if (instance? x) (instance-class x) <top>))
;;; Now we can get down to business. First, we initialize the braid.
;;; For Bootstrapping, we define an early version of MAKE. It will be changed
;;; to the real version later on.
(define* (make class . initargs)
(cond [(or (eq? class <class>) (eq? class <entity-class>))
(let* ([new (%allocate-instance class
(length the-slots-of-a-class))]
[dsupers (getarg initargs :direct-supers '())]
[dslots (map list (getarg initargs :direct-slots '()))]
[cpl (let loop ([sups dsupers] [so-far (list new)])
(if (null? sups)
(reverse so-far)
(loop (append (cdr sups)
(%class-direct-supers (car sups)))
(if (memq (car sups) so-far)
so-far
(cons (car sups) so-far)))))]
[slots
(apply append dslots (map %class-direct-slots (cdr cpl)))]
[nfields 0]
[name (or (getarg initargs :name) '-anonymous-)]
[field-initializers '()]
;; this is a temporary allocator version, kept as the original
;; one in tiny-clos. the permanent version below is modified.
[allocator
(lambda (init)
(let ([f nfields])
(set! nfields (+ nfields 1))
(set! field-initializers (cons init field-initializers))
(mcons (lambda (o) (%instance-ref o f))
(lambda (o n) (%instance-set! o f n)))))]
[getters-n-setters
(map (lambda (s)
(cons (car s) (allocator unspecified-initializer)))
slots)])
(%set-class-direct-supers! new dsupers)
(%set-class-direct-slots! new dslots)
(%set-class-cpl! new cpl)
(%set-class-slots! new slots)
(%set-class-nfields! new nfields)
(%set-class-field-initializers! new (reverse field-initializers))
(%set-class-getters-n-setters! new getters-n-setters)
(%set-class-name! new name)
(%set-class-initializers! new '()) ; no class inits now
(%set-class-valid-initargs! new #f) ; no initargs now
new)]
[(eq? class <generic>)
(let ([new (%allocate-entity class (length (%class-slots class)))]
[arity (getarg initargs :arity #f)]
[name (or (getarg initargs :name) '-anonymous-)])
(%set-generic-methods! new '())
(%set-generic-arity! new arity)
(%set-generic-name! new name)
(%set-generic-combination! new #f)
new)]
[(eq? class <method>)
(let ([new (%allocate-entity class (length (%class-slots class)))]
[name (or (getarg initargs :name) '-anonymous-)])
(%set-method-specializers! new (getarg initargs :specializers))
(%set-method-procedure! new (getarg initargs :procedure))
(%set-method-qualifier! new (or (getarg initargs :qualifier)
:primary))
(%set-method-name! new name)
(set-instance-proc! new (method:compute-apply-method #f new))
new)]))
;;; These are the real versions of slot-ref and slot-set!. Because of the way
;;; the new slot access protocol works, with no generic call in line, they can
;;; be defined up front like this. Cool eh?
;;>> (slot-ref obj slot)
;;> Pull out the contents of the slot named `slot' in the given `obj'.
;;> Note that slot names are usually symbols, but can be other values as
;;> well.
(define* (slot-ref object slot-name)
((lookup-slot-info (class-of object) slot-name mcar) object))
(defsubst (%slot-ref object slot-name)
((lookup-slot-info (class-of object) slot-name mcar) object))
;;>> (slot-set! obj slot new)
;;> Change the contents of the `slot' slot of `obj' to the given `new'
;;> value.
(define* (slot-set! object slot-name new-value)
((lookup-slot-info (class-of object) slot-name mcdr) object new-value))
(defsubst (%slot-set! object slot-name new-value)
((lookup-slot-info (class-of object) slot-name mcdr) object new-value))
;;>> (set-slot-ref! obj slot new)
;;> An alias for `slot-set!', to enable using `setf!' on it.
(define* set-slot-ref! slot-set!)
;; This is a utility that is used to make locked slots
(define (make-setter-locked! g+s key error)
(let ([setter (mcdr g+s)])
(set-mcdr! g+s
(lambda (o n)
(cond [(and (pair? n) (eq? key (car n)) (not (eq? key #t)))
(setter o (cdr n))]
[(eq? ??? ((mcar g+s) o)) (setter o n)]
[else (error)])))))
;;>> (slot-bound? object slot)
;;> Checks if the given `slot' is bound in `object'. See also `???'
;;> above.
(define* (slot-bound? object slot-name)
(not (eq? ??? (%slot-ref object slot-name))))
(define (lookup-slot-info class slot-name selector)
(selector (cdr (or (assq slot-name
;; no need to ground slot-ref any more! -- see below
;; (if (eq? class <class>)
;; ;;* This grounds out the slot-ref tower
;; getters-n-setters-for-class
;; (%class-getters-n-setters class))
(%class-getters-n-setters class))
(raise* make-exn:fail:contract
"slot-ref: no slot `~.s' in ~.s"
slot-name class)))))
;;; These are for optimizations - works only for single inheritance!
(define (%slot-getter class slot-name)
(lookup-slot-info class slot-name mcar))
(define (%slot-setter class slot-name)
(lookup-slot-info class slot-name mcdr))
;;>>... Singleton and Struct Specifiers
;;; Singleton class. A hash-table is used so it is still possible to compare
;;; classes with eq?.
(define singleton-classes (make-hash-table 'weak))
;;>> (singleton x)
;;> Returns a singleton specification. Singletons can be used as type
;;> specifications that have only one element in them so you can
;;> specialize methods on unique objects.
;;>
;;> This is actually just a list with the symbol `singleton' in its head
;;> and the value, but this function uses a hash table to always return
;;> the same object for the same value. For example:
;;> => (singleton 1)
;;> (singleton 1)
;;> => (eq? (singleton 1) (singleton 1))
;;> #t
;;> but if the input objects are not `eq?', the result isn't either:
;;> => (eq? (singleton "1") (singleton "1"))
;;> #f
;;> Only `eq?' is used to compare objects.
(define* (singleton x)
(or (hash-table-get singleton-classes x false-func)
(let ([c (list 'singleton x)])
(hash-table-put! singleton-classes x c)
c)))
;;>> (singleton? x)
;;> Determines if something is a singleton specification (which is any
;;> list with a head containing the symbol `singleton').
(define* (singleton? x)
(and (pair? x) (eq? (car x) 'singleton)))
(defsubst (%singleton? x)
(and (pair? x) (eq? (car x) 'singleton)))
;;>> (singleton-value x)
;;> Pulls out the value of a singleton specification.
(define* singleton-value cadr)
;;>>...
;;> Also note that Racket struct types are converted to appropriate
;;> Swindle classes. This way, it is possible to have Swindle generic
;;> functions that work with struct type specializers.
;;>> (struct-type->class struct-type)
;;> This function is used to convert a struct-type to a corresponding
;;> Swindle subclass of `<struct>'. See the Racket manual for details
;;> on struct types.
(define struct-to-class-table (make-hash-table))
(define* (struct-type->class stype)
(hash-table-get
struct-to-class-table stype
(thunk
(let-values ([(name init-field-k auto-field-k accessor mutator
immutable-k-list super skipped?)
(struct-type-info stype)])
(let* ([supers (list (cond [super (struct-type->class super)]
[skipped? <opaque-struct>]
[else <struct>]))]
[proc? (procedure-struct-type? stype)]
[supers (if proc? (cons <primitive-procedure> supers) supers)]
[this (parameterize ([*default-object-class* #f])
(make (if proc? <procedure-class> <primitive-class>)
:name name :direct-supers supers))])
(hash-table-put! struct-to-class-table stype this)
this)))))
;;>>...
;;> *** Common accessors
;;; Given that the early version of MAKE is allowed to call accessors on class
;;; metaobjects, the definitions for them come here, before the actual class
;;; definitions, which are coming up right afterwards.
;;>> (class-direct-slots class)
;;>> (class-direct-supers class)
;;>> (class-slots class)
;;>> (class-cpl class)
;;>> (class-name class)
;;>> (class-initializers class)
;;> Accessors for class objects (look better than using `slot-ref').
(define* (class-direct-slots c) (%slot-ref c 'direct-slots))
(define* (class-direct-supers c) (%slot-ref c 'direct-supers))
(define* (class-slots c) (%slot-ref c 'slots))
(define (class-nfields c) (%slot-ref c 'nfields))
(define (class-field-initializers c) (%slot-ref c 'field-initializers))
(define (class-getters-n-setters c) (%slot-ref c 'getters-n-setters))
(define* (class-cpl c) (%slot-ref c 'cpl))
(define* (class-name c) (%slot-ref c 'name))
(define* (class-initializers c) (%slot-ref c 'initializers))
(define (class-valid-initargs c) (%slot-ref c 'valid-initargs))
;;>> (generic-methods generic)
;;>> (generic-arity generic)
;;>> (generic-name generic)
;;>> (generic-combination generic)
;;> Accessors for generic function objects.
(define* (generic-methods g) (%slot-ref g 'methods))
(define* (generic-arity g) (%slot-ref g 'arity))
(define* (generic-name g) (%slot-ref g 'name))
(define* (generic-combination g) (%slot-ref g 'combination))
;;>> (method-specializers method)
;;>> (method-procedure method)
;;>> (method-qualifier method)
;;>> (method-name method)
;;>> (method-arity method)
;;> Accessors for method objects. `method-arity' is not really an
;;> accessor, it is deduced from the arity of the procedure (minus one for
;;> the `call-next-method' argument).
(define* (method-specializers m) (%slot-ref m 'specializers))
(define* (method-procedure m) (%slot-ref m 'procedure))
(define* (method-qualifier m) (%slot-ref m 'qualifier))
(define* (method-name m) (%slot-ref m 'name))
(define* (method-arity m)
(let ([a (procedure-arity (%method-procedure m))])
(cond [(integer? a) (sub1 a)]
[(arity-at-least? a)
(make-arity-at-least (sub1 (arity-at-least-value a)))]
[else (error 'method-arity "the procedure in ~.s has bad arity ~e"
m a)])))
;;; These versions will be optimized later.
(define %class-direct-slots class-direct-slots)
(define %class-direct-supers class-direct-supers)
(define %class-slots class-slots)
(define %class-nfields class-nfields)
(define %class-field-initializers class-field-initializers)
(define %class-getters-n-setters class-getters-n-setters)
(define %class-cpl class-cpl)
(define %class-name class-name)
(define %class-initializers class-initializers)
(define %class-valid-initargs class-valid-initargs)
(define %generic-methods generic-methods)
(define %generic-arity generic-arity)
(define %generic-name generic-name)
(define %generic-combination generic-combination)
(define %method-specializers method-specializers)
(define %method-procedure method-procedure)
(define %method-qualifier method-qualifier)
(define %method-name method-name)
(define (%set-class-direct-slots! c x) (%slot-set! c 'direct-slots x))
(define (%set-class-direct-supers! c x) (%slot-set! c 'direct-supers x))
(define (%set-class-slots! c x) (%slot-set! c 'slots x))
(define (%set-class-nfields! c x) (%slot-set! c 'nfields x))
(define (%set-class-field-initializers! c x)
(%slot-set! c 'field-initializers x))
(define (%set-class-getters-n-setters! c x)
(%slot-set! c 'getters-n-setters x))
(define (%set-class-cpl! c x) (%slot-set! c 'cpl x))
(define (%set-class-name! c x) (%slot-set! c 'name x))
(define (%set-class-initializers! c x) (%slot-set! c 'initializers x))
(define (%set-class-valid-initargs! c x) (%slot-set! c 'valid-initargs x))
(define (%set-generic-methods! g x) (%slot-set! g 'methods x))
(define (%set-generic-arity! g x) (%slot-set! g 'arity x))
(define (%set-generic-name! g x) (%slot-set! g 'name x))
(define (%set-generic-combination! g x) (%slot-set! g 'combination x))
(define (%set-method-specializers! m x) (%slot-set! m 'specializers x))
(define (%set-method-procedure! m x) (%slot-set! m 'procedure x))
(define (%set-method-qualifier! m x) (%slot-set! m 'qualifier x))
(define (%set-method-name! m x) (%slot-set! m 'name x))
;;; These are used to access the two slots that optimize generic invocations.
(define (%generic-app-cache g ) (%slot-ref g 'app-cache))
(define (%generic-singletons-list g ) (%slot-ref g 'singletons-list))
(define (%set-generic-app-cache! g x) (%slot-set! g 'app-cache x))
(define (%set-generic-singletons-list! g x) (%slot-set! g 'singletons-list x))
;;; The next 7 clusters define the 6 initial classes. It takes 7 to 6 because
;;; the first and fourth both contribute to <class>.
(define the-slots-of-a-class
'(direct-supers ; (class ...)
direct-slots ; ((name . options) ...)
cpl ; (class ...)
slots ; ((name . options) ...)
nfields ; an integer
field-initializers ; (proc ...)
getters-n-setters ; ((slot-name getter setter) ...)
name ; a symbol
initializers ; (proc ...)
valid-initargs)) ; (initarg ...) or #f
(define getters-n-setters-for-class ; see lookup-slot-info
(map (lambda (s)
(let ([f (position-of s the-slots-of-a-class)])
(cons s (mcons (lambda (o) (%instance-ref o f))
(lambda (o n) (%instance-set! o f n))))))
the-slots-of-a-class))
;;>>...
;;> *** Basic classes
;;>> <class>
;;> This is the "mother of all classes": every Swindle class is an
;;> instance of `<class>'.
;;> Slots:
;;> * direct-supers: direct superclasses
;;> * direct-slots: direct slots, each a list of a name and options
;;> * cpl: class precedence list (classes list this to <top>)
;;> * slots: all slots (like direct slots)
;;> * nfields: number of fields
;;> * field-initializers: a list of functions to initialize slots
;;> * getters-n-setters: an alist of slot-names, getters, and setters
;;> * name: class name (usually the defined identifier)
;;> * initializers: procedure list that perform additional initializing
;;> See the `clos' documentation for available class and slot keyword
;;> arguments and their effect.
(define* <class> (%allocate-instance #f (length the-slots-of-a-class)))
(set-instance-class-to-self! <class>)
;; In the original tiny-clos, this block used to just set the getters-n-setters
;; slot of a class to '() since it wasn't used anyway. In Swindle the MOP
;; accessors are all optimized to directly get the vector element because the
;; meta hierarchy is assumed to be single-inheritance only (allocation of more
;; slots always come after the built in ones), so what I do here is set the
;; slot value properly, and since `%class-getters-n-setters' accesses the
;; vector directly it doesn't go through slot-ref, which means that the
;; slot-ref definition above is fine. So,
;; (%set-class-getters-n-setters! <class> getters-n-setters-for-class)
;; translates into this:
((mcdr (cdr (assq 'getters-n-setters getters-n-setters-for-class)))
<class> getters-n-setters-for-class)
;; and now the direct `%class-getters-n-setters' version:
(set! %class-getters-n-setters
;; and (lookup-slot-info <class> 'getters-n-setters mcar) translates to:
(mcar (cdr (assq 'getters-n-setters getters-n-setters-for-class))))
;;>> <top>
;;> This is the "mother of all values": every value is an instance of
;;> `<top>' (including standard Scheme values).
(define* <top> (make <class> :direct-supers '()
:direct-slots '()
:name '<top>))
;;>> <object>
;;> This is the "mother of all objects": every Swindle object is an
;;> instance of `<object>'.
(define* <object> (make <class> :direct-supers (list <top>)
:direct-slots '()
:name '<object>))
;;; This cluster, together with the first cluster above that defines <class>
;;; and sets its class, have the effect of:
;;; (define <class>
;;; (make <class> :direct-supers (list <object>)
;;; :direct-slots '(direct-supers ...)
;;; :name '<class>))
(%set-class-direct-supers! <class> (list <object>))
(%set-class-cpl! <class> (list <class> <object> <top>))
(%set-class-direct-slots! <class> (map list the-slots-of-a-class))
(%set-class-slots! <class> (map list the-slots-of-a-class))
(%set-class-nfields! <class> (length the-slots-of-a-class))
(%set-class-field-initializers! <class> (map (lambda (s)
unspecified-initializer)
the-slots-of-a-class))
(%set-class-name! <class> '<class>)
(%set-class-initializers! <class> '())
(%set-class-valid-initargs! <class> #f)
;;>> <procedure-class>
;;> The class of all procedures classes, both standard Scheme procedures
;;> classes and entity (Swindle procedure objects) classes. (Note that
;;> this is a class of *classes*).
(define* <procedure-class>
(make <class> :direct-supers (list <class>)
:direct-slots '()
:name '<procedure-class>))
;;>> <entity-class>
;;> The class of entity classes -- generic functions and methods. An
;;> entity is a procedural Swindle object, something that you can apply as
;;> a function but it is still a Swindle object. Note that this is the
;;> class of entity *classes* not of entities themselves.
(define* <entity-class>
(make <class> :direct-supers (list <procedure-class>)
:direct-slots '()
:name '<entity-class>))
;;>> <function>
;;> The class of all applicable values: methods, generic functions, and
;;> standard closures.
(define* <function>
(make <class> :direct-supers (list <top>)
:direct-slots '()
:name '<function>))
;;; The two extra slots below (app-cache and singletons-list) are used to
;;; optimize generic invocations: app-cache holds an 'equal hash-table that
;;; maps a list of classes to the lambda expression that holds the method call
;;; (it used to be an l-hash-table, but 'equal is ok since we can't compare
;;; swindleobj instances recursively -- which is also why tool.rkt needs to
;;; redefine the `render-value/format' method). The contents of this slot is
;;; reset whenever a method is added to the generic. Two problems make things
;;; a little more complicated. First, if add-method is used to modify any of
;;; the generic-invocation-generics then all of these caches should be flushed,
;;; this is achieved by setting *generic-app-cache-tag* to a new [list] object
;;; and the value of app-cache is a cons of that value and the actual hash
;;; table - if we see that the car is not eq? to the current tag, then we flush
;;; the cache. Second, singleton values might screw things up, so we hold in
;;; singletons-list a list that has the same length as all method specializer
;;; lists, each element contains a hash table with all singleton values that
;;; appear in that place matched to #t, then when we try to see if we have a
;;; cached function for a generic application, we scan the argument list
;;; against this list, and any value that has a singleton with that value at
;;; some method, is left in place for the app-cache lookup (it is used itself
;;; rather than its class). This whole thing is a bit complicated but leads to
;;; dramatic run-time improvement.
;;>> <generic>
;;> The class of generic functions: objects that contain method objects
;;> and calls the appropriate ones when applied.
;;> Slots:
;;> * methods: a list of <method> objects
;;> * arity: the generic arity (same for all of its methods)
;;> * name: generic name
;;> * combination: a method combination function or #f, see
;;> `make-generic-combination' below for details
(define* <generic>
(make <entity-class> :direct-supers (list <object> <function>)
:direct-slots '(methods arity name combination
app-cache singletons-list) ; see above
:name '<generic>))
;;>> <method>
;;> The class of methods: objects that are similar to Scheme closures,
;;> except that they have type specifiers attached. Note that in contrast
;;> to Tiny CLOS, methods are applicable objects in Swindle -- they check
;;> supplied argument types when applied.
;;> Slots:
;;> * specializers: a list of class (and singleton) specializers
;;> * procedure: the function (never call directly!)
;;> * qualifier: some qualifier tag, used when applying a generic
;;> * name: method name
(define* <method>
(make <entity-class> :direct-supers (list <object> <function>)
:direct-slots '(specializers procedure qualifier name)
:name '<method>))
;; Do this since compute-apply-method relies on them not changing, as well as a
;; zillion other places. A method should be very similar to a lambda.
(dolist [slot '(specializers procedure qualifier)]
(make-setter-locked! (lookup-slot-info <method> slot values) #t
(lambda ()
(raise* make-exn:fail:contract
"slot-set!: slot `~.s' in <method> is locked" slot))))
;;>>...
;;> *** Convenience functions
;;>
;;> These are some convenience functions -- no new syntax, just function
;;> wrappers for `make' with some class and some slot values. See `clos'
;;> for a more sophisticated (and convenient) approach.
;;; These are the convenient syntax we expose to the base-level user.
;;>> (make-class direct-supers direct slots)
;;> Creates a class object -- an instance of <class>.
(define* (make-class direct-supers direct-slots)
(make <class> :direct-supers direct-supers
:direct-slots direct-slots))
;;>> (make-generic-function [name/arity])
;;> Creates a generic function object -- an instance of <generic>. The
;;> argument can specify name and/or arguments number.
(define* (make-generic-function . name/arity)
(cond
[(null? name/arity) (make <generic>)]
[(null? (cdr name/arity))
(let ([n/a (car name/arity)])
(if (integer? n/a)
(make <generic> :arity n/a) (make <generic> :name n/a)))]
[else (make <generic> :name (car name/arity) :arity (cadr name/arity))]))
;;>> (make-method specializers procedure)
;;> Creates a method object -- an instance of <method>, using the given
;;> specializer list and procedure. The procedure should have a first
;;> argument which is being used to access a `call-next-method' call.
(define* (make-method specializers procedure)
(make <method> :specializers specializers
:procedure procedure))
;;>> (no-next-method generic method [args ...])
;;>> (no-applicable-method generic [args ...])
;;> These two generic functions are equivalents to the ones in CL. The
;;> first one is applied on a generic and a method in case there was no
;;> next method and `call-next-method' was used. The second is used when
;;> a generic was called but no matching primary methods were found. The
;;> only difference is that in Swindle methods can be applied directly,
;;> and if `call-next-method' is used, then `no-next-method' gets `#f' for
;;> the generic argument.
(define* no-next-method (make-generic-function 'no-next-method))
(define* no-applicable-method (make-generic-function 'no-applicable-method))
;;; Add possibility of generic-independent method application - this is the
;;; instance-proc of methods, which is activated when you apply the object (in
;;; the original, methods could not be applied). This is defined using this
;;; name and arguments because it is later used directly by the generic
;;; function (cannot use the generic in the initial make since methods need to
;;; be created when the generics are constructed).
(define (method:compute-apply-method call-next-method method)
(let* ([specializers (%method-specializers method)]
[*no-next-method* ; see the *no-next-method* trick below
(lambda args (no-next-method #f method . args))]
[proc (%method-procedure method)]
[arity (method-arity method)]
[exact? (integer? arity)]
[required ((if exact? identity arity-at-least-value) arity)])
(when (and exact? (> (length specializers) required))
(error 'compute-apply-method
"got ~e specializers for ~s - too much for procedure arity ~a"
(length specializers) (%method-name method) required))
(lambda args
(cond [(if exact?
(not (= (length args) required)) (< (length args) required))
(raise* make-exn:fail:contract:arity
"method ~a: expects ~a~e argument~a, given ~e~a"
(%method-name method)
(if exact? "" "at least ") required
(if (= 1 required) "" "s") (length args)
(if (null? args) "" (format ": ~e" args)))]
[(not (every instance-of? args specializers))
(let loop ([args args] [specs specializers])
(if (instance-of? (car args) (car specs))
(loop (cdr args) (cdr specs))
(raise* make-exn:fail:contract
"method ~a: expects argument of type ~a; given ~e"
(%method-name method) (%class-name (car specs))
(car args))))]
[else (proc *no-next-method* . args)]))))
;;>>... Generics in the instance initialization protocol
;;> The following generic functions are used as part of the protocol of
;;> instantiating an instance, and some are used specifically to instantiate
;;> class objects.
;;; The instance structure protocol.
;;>> (allocate-instance class initargs)
;;> This generic function is called to allocate an instance of a class.
;;> It is applied on the class object, and is expected to return the new
;;> instance object of that class.
(define* allocate-instance
(make-generic-function 'allocate-instance))
;;>> (initialize instance initargs)
;;> This generic is called to initialize an instance. It is applied on
;;> the newly allocated object and the given initargs, and is not expected
;;> to return any meaningful value -- only do some side effects on the
;;> instance to initialize it. When overriding this for a some class, it
;;> is not a good idea to skip `call-next-method' since it is responsible
;;> for initializing slot values.
(define* initialize
(make-generic-function 'initialize))
;;>> (compute-getter-and-setter class slot allocator)
;;> This generic is used to get a getter and setter functions for a given
;;> slot. It is passed the class object, the slot information (a list of
;;> a slot name and options), and an allocator function. The allocator is
;;> a function that gets an initializer function and returns an index
;;> position for the new slot. The return value should be a list of two
;;> elements -- a getter and a setter functions.
(define* compute-getter-and-setter
(make-generic-function 'compute-getter-and-setter))
;;; The class initialization protocol.
;;>> (compute-cpl class)
;;> This generic is used to get the class-precedence-list for a class
;;> object. The standard <class> object uses the `compute-std-cpl' (see
;;> in the code) which flattens the class ancestors using a topological
;;> sort that resolve ambiguities left-to-right.
(define* compute-cpl
(make-generic-function 'compute-cpl))
;;>> (compute-slots class)
;;> This generic is used to compute all slot information for a given
;;> class, after its precedence list has been computed. The standard
;;> <class> collects information from all preceding classes.
(define* compute-slots
(make-generic-function 'compute-slots))
;;>> (compute-apply-method method)
;;> This generic is used to compute the procedure that will get executed
;;> when a method is applied directly.
(define* compute-apply-method
(make-generic-function 'compute-apply-method))
;;>>... Generics in the generic invocation protocol
;;> These generics are used for invocation of generic functions. See the
;;> code to see how this circularity is achieved.
;;>> ((compute-apply-generic generic) args ...)
;;> This generic is used to compute the object (a closure) that is
;;> actually applied to execute the generic call. The standard version
;;> uses `compute-method' and `compute-apply-methods' below, and caches
;;> the result.
(define* compute-apply-generic
(make-generic-function 'compute-apply-generic))
;;>> (compute-methods generic args)
;;> Computes the methods that should be applied for this generic
;;> invocation with args. The standard code filters applicable methods
;;> and sorts them according to their specificness. The return value is
;;> expected to depend only on the types of the arguments (and values if
;;> there are singleton specializers).
(define* compute-methods
(make-generic-function 'compute-methods))
;;>> ((compute-method-more-specific? generic) mthd1 mthd2 args)
;;> Get a generic and return a function that gets two methods and a list
;;> of arguments and decide which of the two methods is more specific.
;;> This decision should only be based on the argument types, or values
;;> only in case of singletons.
(define* compute-method-more-specific?
(make-generic-function 'compute-method-more-specific?))
;;>> ((compute-apply-methods generic methods) args ...)
;;> Gets a generic and returns a function that gets the given arguments
;;> for this call. This function which it returns is the combination of
;;> all given methods. The standard one arranges them by default using
;;> the `call-next-method' argument that methods have. Swindle extends
;;> this with qualified methods and applies `before', `after', and
;;> `around' methods in a similar way to CLOS: first the `around' methods
;;> are applied (and they usually call their `call-next-method' to
;;> continue but can return a different value), then all the `before'
;;> methods are applied (with no `call-next-method'), then all `primary'
;;> methods as usual (remembering the return value), and finally the
;;> `after' methods (similar to the `before', but in reverse specificness
;;> order). If the generic has a `combination' slot value, then it is a
;;> procedure that is used to combine the primary methods, but the
;;> auxiliary ones are still applied in the same way. This is unlike CLOS
;;> where the standard combinations run only `around' methods, and there
;;> is generally more control with method combinations, but in Swindle
;;> `compute-apply-methods' should be overridden for this. See
;;> `make-generic-combination' for details about method combinations.
(define* compute-apply-methods
(make-generic-function 'compute-apply-methods))
;;; The next thing to do is bootstrap generic functions.
(define generic-invocation-generics
(list compute-apply-generic compute-methods
compute-method-more-specific? compute-apply-methods))
;;; This is used to signal whenever all method caches are to be reset - so when
;;; a method is added to generic-invocation-generics, this is set to some value
;;; which is not eq? to the current one.
(define *generic-app-cache-tag* #t)
;;>> (add-method generic method)
;;> This generic function is called to add a method to a generic function
;;> object. This is an other change from the original Tiny CLOS where it
;;> was a normal function.
(define* (add-method generic method)
;; add singleton specializer value (if any) to the corresponding hash table
;; in singletons-list.
(define (add-to-singletons-list specs tables)
(cond
[(null? specs) null]
[(%singleton? (car specs))
(let ([ht (or (car tables)
(make-hash-table 'weak))])
(hash-table-put! ht (singleton-value (car specs)) #t)
(cons ht (add-to-singletons-list (cdr specs) (cdr tables))))]
[else
(cons (car tables)
(add-to-singletons-list (cdr specs) (cdr tables)))]))
(define (n-falses n)
(let loop ([n n] [r '()]) (if (zero? n) r (loop (sub1 n) (cons #f r)))))
(let ([tables (%generic-singletons-list generic)]
[specs (%method-specializers method)]
[qualifier (%method-qualifier method)])
;; make sure that tables always contain enough hash tables (or #f's)
(cond [(eq? tables ???)
(set! tables (n-falses (length specs)))]
[(< (length tables) (length specs))
(set! tables (append
tables
(n-falses (- (length specs) (length tables)))))])
(set! tables (add-to-singletons-list specs tables))
(%set-generic-singletons-list! generic tables)
(if (memq generic generic-invocation-generics)
;; reset all caches by changing the value of *generic-app-cache-tag*
(set! *generic-app-cache-tag* (list #f))
;; reset this generic app-cache
(%set-generic-app-cache! generic ???))
(%set-generic-methods!
generic
(cons method
(filter (lambda (m)
(not (and (every eq? (method-specializers m) specs)
(eq? (%method-qualifier m) qualifier))))
(%generic-methods generic))))
(set-instance-proc! generic (compute-apply-generic generic))))
;;; Adding a method calls COMPUTE-APPLY-GENERIC, the result of which calls the
;;; other generics in the generic invocation protocol. Two, related, problems
;;; come up. A chicken and egg problem and a infinite regress problem.
;;; In order to add our first method to COMPUTE-APPLY-GENERIC, we need
;;; something sitting there, so it can be called. The first definition below
;;; does that.
;;; Then, the second definition solves both the infinite regress and the not
;;; having enough of the protocol around to build itself problem the same way:
;;; it special cases invocation of generics in the invocation protocol.
(set-instance-proc! compute-apply-generic
(lambda (generic)
((%method-procedure (car (%generic-methods generic))) '() generic)))
(add-method compute-apply-generic
(make-method (list <generic>)
(named-lambda method:compute-apply-generic (call-next-method generic)
#| The code below is the original, then comes the optimized version below
;; see the definition of the <generic> class above.
(lambda args
(if (and (memq generic generic-invocation-generics) ;* Ground case
(memq (car args) generic-invocation-generics))
(apply (%method-procedure (last (%generic-methods generic))) #f args)
((compute-apply-methods generic)
(compute-methods generic args) . args)))
|#
;; This function converts the list of arguments to a list of keys to look
;; for in the cache - use the argument's class except when there is a
;; corresponding singleton with the same value at the same position.
(define (get-keys args tables)
(let loop ([args args] [tables tables] [ks '()])
(if (or (null? tables) (null? args))
(reverse ks)
(loop (cdr args) (cdr tables)
(cons (if (and (car tables)
(hash-table-get
(car tables) (car args) false-func))
(car args)
(class-of (car args)))
ks)))))
;; This is the main function that brings the correct value from the
;; cache, or generates one and store it if there is no entry, or the
;; cache was reset. Finally, it is applied to the arguments as usual.
;; NOTE: This code is delicate! Handle with extreme care!
(lambda args
(let ([app-cache (%generic-app-cache generic)]
[arity (%generic-arity generic)]
[keys (get-keys args (%generic-singletons-list generic))]
[ground? (and ;* Ground case
(memq generic generic-invocation-generics)
(pair? args)
(memq (car args) generic-invocation-generics))])
;; This function creates the cached closure -- the assumption is that
;; `keys' contain a specification that will identify all calls that
;; will have this exact same list.
(define (compute-callable)
(let ([c (if ground?
(let ([m (%method-procedure
(last (%generic-methods generic)))])
(lambda args (apply m #f args)))
(compute-apply-methods
generic (compute-methods generic args)))])
(hash-table-put! (cdr app-cache) keys c)
c))
(when (cond [(not arity) #f]
[(integer? arity) (not (= (length args) arity))]
[else (< (length args) (arity-at-least-value arity))])
(let ([least (and (arity-at-least? arity)
(arity-at-least-value arity))])
(raise* make-exn:fail:contract:arity
"generic ~a: expects ~a~e argument~a, given ~e~a"
(%generic-name generic)
(if least "at least " "") (or least arity)
(if (= 1 (or least arity)) "" "s") (length args)
(if (null? args) "" (format ": ~e" args)))))
(when (or (eq? app-cache ???)
(not (eq? (car app-cache) *generic-app-cache-tag*)))
(set! app-cache (cons *generic-app-cache-tag*
(make-hash-table 'weak 'equal)))
(%set-generic-app-cache! generic app-cache))
((hash-table-get (cdr app-cache) keys compute-callable)
. args))))))
(add-method compute-methods
(make-method (list <generic>)
(named-lambda method:compute-methods (call-next-method generic args)
(let ([more-specific? (compute-method-more-specific? generic)])
(sort (filter
(lambda (m)
;; Note that every only goes as far as the shortest list
(every instance-of? args (%method-specializers m)))
(%generic-methods generic))
(lambda (m1 m2) (more-specific? m1 m2 args)))))))
(add-method compute-method-more-specific?
(make-method (list <generic>)
(named-lambda method:compute-method-more-specific?
(call-next-method generic)
(lambda (m1 m2 args)
(let loop ([specls1 (%method-specializers m1)]
[specls2 (%method-specializers m2)]
[args args])
(cond [(and (null? specls1) (null? specls2))
(if (eq? (%method-qualifier m1) (%method-qualifier m2))
(error 'generic
"two methods are equally specific in ~e" generic)
#f)]
;; some methods in this file have less specializers than
;; others, for things like args -- so remove this, leave the
;; args check but treat the missing as if it's <top>
;; ((or (null? specls1) (null? specls2))
;; (error 'generic
;; "two methods have different number of ~
;; specializers in ~e" generic))
[(null? args) ; shouldn't happen
(error 'generic
"fewer arguments than specializers for ~e" generic)]
[(null? specls1) ; see above -> treat this like <top>
(if (eq? <top> (car specls2))
(loop specls1 (cdr specls2) (cdr args))
#f)]
[(null? specls2) ; see above -> treat this like <top>
(if (eq? <top> (car specls1))
(loop (cdr specls1) specls2 (cdr args))
#t)]
[else (let ([c1 (car specls1)] [c2 (car specls2)])
(if (eq? c1 c2)
(loop (cdr specls1) (cdr specls2) (cdr args))
(more-specific? c1 c2 (car args))))]))))))
(add-method compute-apply-methods
(make-method (list <generic>)
(named-lambda method:compute-apply-methods
(call-next-method generic methods)
(let ([primaries '()] [arounds '()] [befores '()] [afters '()]
[combination (%generic-combination generic)])
;; *** Trick: this (and in <method> above) is the only code that is
;; supposed to ever apply a method procedure. So, the closure that
;; will invoke `no-next-method' is named `*no-next-method*' so it is
;; identifiable. The only way to break this would be to call the
;; method-procedure directly on an object with such a name.
(define one-step
(if combination
(combination generic)
(lambda (tail args)
(lambda newargs
;; tail is never null: (null? (cdr tail)) below, and the fact
;; that this function is applied on the primaries which are
;; never null
(let ([args (if (null? newargs) args newargs)])
((cdar tail)
(if (null? (cdr tail))
(named-lambda *no-next-method* args
(no-next-method generic (caar tail) . args))
(one-step (cdr tail) args))
. args))))))
(define ((apply-before/after-method args) method)
((cdr method)
(named-lambda *no-next-method* args
(no-next-method generic (car method) . args))
. args))
(define ((call-before-primary-after args) . newargs)
;; could supply newargs below, but change before calling befores
(let ([args (if (null? newargs) args newargs)])
(for-each (apply-before/after-method args) befores)
(begin0 ((one-step primaries args))
(for-each (apply-before/after-method args) afters))))
(define (one-around-step tail args)
(if (null? tail)
(call-before-primary-after args)
(lambda newargs
(let ([args (if (null? newargs) args newargs)])
((cdar tail) (one-around-step (cdr tail) args) . args)))))
;; first sort by qualifier and pull out method-procedures
(let loop ([ms methods])
(unless (null? ms)
(letsubst ([(push! p)
(set! p (cons (cons (car ms)
(%method-procedure (car ms)))
p))])
(case (%method-qualifier (car ms))
[(:primary) (push! primaries)]
[(:around) (push! arounds)]
[(:before) (push! befores)]
[(:after) (push! afters)]
;; ignore other qualifiers
;; [else (error 'compute-apply-methods
;; "a method ~e has an unexpected qualifier `~e'"
;; (car methods)
;; (%method-qualifier (car methods)))]
)
(loop (cdr ms)))))
(set! primaries (reverse primaries))
(set! arounds (reverse arounds))
(set! befores (reverse befores))
;; no reverse for afters
(cond [(null? primaries)
(lambda args (no-applicable-method generic . args))]
;; optimize common case of only primaries
[(and (null? befores) (null? afters) (null? arounds))
;; args is initialized to () since if it is a generic of no
;; arguments then it will always stay so, otherwise, the first
;; call will have the real arguments anyway
(one-step primaries '())]
[else (one-around-step arounds '())])))))
;;>> (((make-generic-combination keys...) generic) tail args)
;;> This function can be used to construct simple method combinations that
;;> can be used with the `combination' slot of generic functions. The
;;> combination itself is a function that gets a generic and returns a
;;> function that gets a list of method/procedure pairs (for optimization
;;> the method-procedures are pre taken) and the arguments and performs
;;> the call -- but this is only interesting if there's any need to
;;> implement a method combination directly, otherwise, the
;;> `make-generic-combination' interface should allow enough freedom.
;;> Note that when a method combination is used, `around', `before', and
;;> `after' are called around the primary call as usual, but the primaries
;;> are never called with a valid `call-next-method' argument.
;;>
;;> The keyword arguments that can be taken determine the behavior of this
;;> combination. Overall, it is roughly like a customizable version of a
;;> fold operation on the method calls.
;;> * :init
;;> - The initial value for this computation. Defaults to null.
;;> * :combine
;;> - A function to be called on a method call result and the old value,
;;> and produces a new value. The default is `cons', which with an
;;> initial null value will collect the results into a reversed list.
;;> * :process-methods
;;> - A function that can be called on the initial list of
;;> method/procedure pairs to change it -- for example, it can be
;;> reversed to apply the methods from the least specific to the most
;;> specific. No default.
;;> * :process-result
;;> - A function that can be called on the final resulting value to
;;> produce the actual return value. For example, it can reverse back
;;> a list of accumulated values. No default.
;;> * :control
;;> - If this parameter is specified, then the `:combine' argument is
;;> ignored. The value given to `:control' should be a function of
;;> four arguments:
;;> 1. a `loop' function that should be called on some new value and
;;> some new tail;
;;> 2. a `val' argument that gets the current accumulated value;
;;> 3. a `this' thunk that can be called to apply the current method
;;> and return its result;
;;> 4. a `tail' value that holds the rest of the method/procedure list
;;> which can be sent to `loop'.
;;> It should be clear now, that a `:control' argument can have a lot
;;> of control on the computation, it can abort, change arbitrary
;;> values and skip calling methods. Note that if it won't call
;;> `loop' with an empty list, then a `:process-result' function will
;;> not be used as well. See the pre-defined combinations in the
;;> source code to see examples of using this function.
(define* (make-generic-combination
&key [init '()] [combine cons]
process-methods process-result control)
(lambda (generic)
(lambda (tail dummy-args)
(let ([tail (if process-methods (process-methods tail) tail)])
(lambda args
(let loop ([res init] [tail tail])
;; see *no-next-method* trick above
(let ([*no-next-method*
(lambda args (no-next-method generic (caar tail) . args))])
(if (null? tail)
(if process-result (process-result res) res)
(if control
(control loop res
(lambda () ((cdar tail) *no-next-method* . args))
(cdr tail))
(loop (combine ((cdar tail) *no-next-method* . args) res)
(cdr tail)))))))))))
;;>> generic-+-combination
;;>> generic-list-combination
;;>> generic-min-combination
;;>> generic-max-combination
;;>> generic-append-combination
;;>> generic-append!-combination
;;>> generic-begin-combination
;;>> generic-and-combination
;;>> generic-or-combination
;;> These are all functions that can be used as a `combination' value for
;;> a generic function. They work in the same way as the standard method
;;> combinations of CL. Most of them do the obvious thing based on some
;;> function to combine the result. The `begin' combination simply
;;> executes all methods one by one and returns the last value, the `and'
;;> and `or' combinations will call them one by one until a false or true
;;> result is returned. The source of these can be used as templates for
;;> defining more combinations.
(define* generic-+-combination
(make-generic-combination :init 0 :combine +))
(define* generic-list-combination
(make-generic-combination :process-result reverse))
(define* generic-min-combination
(make-generic-combination :process-result (lambda (r) (apply min r))))
(define* generic-max-combination
(make-generic-combination :process-result (lambda (r) (apply max r))))
(define* generic-append-combination
(make-generic-combination
:process-result (lambda (r) (apply append (reverse r)))))
(define* generic-append!-combination
(make-generic-combination
:process-result (lambda (r) (apply append (reverse r)))))
(define* generic-begin-combination
(make-generic-combination :init #f :combine (lambda (x y) x)))
(define* generic-and-combination
(make-generic-combination
:init #t
:control (lambda (loop val this tail) (and val (loop (this) tail)))))
(define* generic-or-combination
(make-generic-combination
:init #f
:control (lambda (loop val this tail) (or (this) (loop #f tail)))))
;;>>...
;;> *** More class functionality
;;> (In the following, a `class' can be a class, a singleton specifier, or a
;;> struct type.)
;; optimized helper
(defsubst (%struct->class c)
(if (struct-type? c) (struct-type->class c) c))
;;>> (subclass? class1 class2)
;;> Is `class1' a subclass of `class2'?
(define* (subclass? c1 c2)
(if (%singleton? c1)
(if (%singleton? c2)
(eq? (singleton-value c1) (singleton-value c2))
(instance-of? (singleton-value c1) (%struct->class c2)))
(memq (%struct->class c2) (%class-cpl (%struct->class c1)))))
;;>> (instance-of? x class)
;;> Checks if `x' is an instance of `class' (or one of its subclasses).
(define* (instance-of? x c)
;; efficiency: many cases use <top> (all untyped arguments)
(or (eq? c <top>)
(if (%singleton? c)
;; efficiency: similar to `subclass?' above
(eq? (singleton-value c) x)
(memq (%struct->class c) (%class-cpl (%struct->class (class-of x)))))))
;;>> (class? x)
;;> Determines whether `x' is a class.
(define* (class? x) (instance-of? x <class>))
(defsubst (%class? x) (instance-of? x <class>))
;;>> (specializer? x)
;;> Determines whether `x' is a class, a singleton, or a struct-type.
(define* (specializer? x) (or (class? x) (%singleton? x) (struct-type? x)))
;;>> (more-specific? class1 class2 x)
;;> Is `class1' more specific than `class2' for the given value?
(define* (more-specific? c1 c2 arg)
(if (%singleton? c1)
(and (eq? (singleton-value c1) arg)
(not (and (%singleton? c2) (eq? (singleton-value c1) arg))))
(let ([cc1 (memq (%struct->class c1) (%class-cpl (class-of arg)))])
(and cc1 (memq (%struct->class c2) (cdr cc1))))))
(add-method initialize
(make-method (list <top>)
(named-lambda method:initialize (call-next-method object initargs)
(error 'initialize "can't initialize an instance of ~e"
(class-of object)))))
(add-method initialize
(make-method (list <object>)
(named-lambda method:initialize (call-next-method object initargs)
(let* ([class (class-of object)]
[field-initializers (%class-field-initializers class)])
(for-each (lambda (init) (init . initargs))
(%class-initializers class))
(let loop ([n 0] [inits field-initializers])
(when (pair? inits)
(%instance-set! object n ((car inits) . initargs))
(loop (+ n 1) (cdr inits))))))))
(add-method initialize
(make-method (list <class>)
(named-lambda method:initialize (call-next-method class initargs)
(call-next-method)
(%set-class-direct-supers!
class
(let ([default (*default-object-class*)]
[supers (getarg initargs :direct-supers)])
;; check valid supers, and always have an object class
(cond
[(not default) supers] ; check disabled
[(or (not supers) (null? supers)) (list default)]
[(not (list? supers)) (error 'class "bad superclasses: ~e" supers)]
[else (let ([c (find-if
(lambda (c)
(not (and (%class? c) (subclass? c default))))
supers)])
(if c
(error 'class "cannot inherit from a ~a, ~e"
(if (%class? c) "non-object class" "non-class") c)
supers))])))
(%set-class-direct-slots!
class
(let ([autoinitargs (getarg initargs :autoinitargs)])
(map (lambda (s)
(if (pair? s)
(if (or (not autoinitargs)
(getarg (cdr s) :initarg)
(not (symbol? (car s))))
s
(list* (car s) :initarg (string->symbol
(string-append
":" (symbol->string (car s))))
(cdr s)))
(list s)))
(getarg initargs :direct-slots '()))))
(%set-class-cpl! class (compute-cpl class))
(%set-class-slots! class (compute-slots class))
(%set-class-name! class (or (getarg initargs :name) '-anonymous-))
(let* ([nfields 0]
[field-initializers '()]
;; allocator: give me an initializer function, get a slot number
[allocator (lambda (init)
(let ([f nfields])
(set! nfields (+ nfields 1))
(set! field-initializers
(cons init field-initializers))
f))]
[getters-n-setters (map (lambda (slot)
(cons (car slot)
(compute-getter-and-setter
class slot allocator)))
(%class-slots class))])
(%set-class-nfields! class nfields)
(%set-class-field-initializers! class (reverse field-initializers))
(%set-class-getters-n-setters! class getters-n-setters))
(%set-class-initializers!
class (reverse
(mappend
(lambda (c)
(if (instance-of? c <class>) (%class-initializers c) '()))
(cdr (%class-cpl class)))))
(%set-class-valid-initargs! ; for sanity checks
class (getarg initargs :valid-initargs
(thunk (mappend (lambda (slot)
(getargs (cdr slot) :initarg))
(%class-slots class))))))))
(add-method initialize
(make-method (list <generic>)
(named-lambda method:initialize (call-next-method generic initargs)
(call-next-method)
(%set-generic-methods! generic '())
(%set-generic-arity! generic (getarg initargs :arity #f))
(%set-generic-name! generic (or (getarg initargs :name) '-anonymous-))
(%set-generic-combination! generic (getarg initargs :combination))
(set-instance-proc! generic
(lambda args
(raise* make-exn:fail:contract
"~s: no methods added yet"
(%generic-name generic)))))))
(add-method initialize
(make-method (list <method>)
(named-lambda method:initialize (call-next-method method initargs)
(call-next-method)
(%set-method-specializers! method
(map (lambda (c) (%struct->class c))
(getarg initargs :specializers)))
(%set-method-procedure! method (getarg initargs :procedure))
(%set-method-qualifier! method (or (getarg initargs :qualifier)
:primary))
(%set-method-name! method (or (getarg initargs :name)
'-anonymous-))
(set-instance-proc! method (compute-apply-method method)))))
(add-method allocate-instance
(make-method (list <class>)
(named-lambda method:allocate-instance (call-next-method class initargs)
(%allocate-instance class (length (%class-field-initializers class))))))
(add-method allocate-instance
(make-method (list <entity-class>)
(named-lambda method:allocate-instance (call-next-method class initargs)
(%allocate-entity class (length (%class-field-initializers class))))))
(add-method compute-cpl
(make-method (list <class>)
(named-lambda method:compute-cpl (call-next-method class)
(compute-std-cpl class %class-direct-supers))))
(add-method compute-slots
(make-method (list <class>)
(named-lambda method:compute-slots (call-next-method class)
(let ([all-slots (map %class-direct-slots (%class-cpl class))]
[final-slots #f])
(let collect ([to-process (apply append all-slots)]
[result '()])
(if (null? to-process)
(set! final-slots result)
(let* ([name (caar to-process)]
[others '()]
[remaining-to-process
(filter (lambda (o)
(if (eq? (car o) name)
(begin (set! others (cons (cdr o) others)) #f)
#t))
to-process)])
(collect remaining-to-process
(cons (cons name (apply append (reverse others)))
result)))))
;; Sort the slots by order of appearance in cpl, makes them stay in the
;; same index, allowing optimizations for single-inheritance
(let collect ([to-process (apply append (reverse all-slots))]
[result '()])
(cond [(null? to-process) (reverse result)]
[(assq (caar to-process) result)
(collect (cdr to-process) result)]
[else (collect (cdr to-process)
(cons (assq (caar to-process) final-slots)
result))]))))))
(add-method compute-getter-and-setter
(make-method (list <class>)
(letrec ([nothing "nothing"]
[l-getarg
;; apply getarg on a list of names until get a value
(lambda (args initargs)
;; give priority to first initargs
(if (null? initargs)
nothing
(let ([x (getarg args (car initargs) nothing)])
(if (eq? x nothing) (l-getarg args (cdr initargs)) x))))])
(named-lambda method:compute-getter-and-setter
(call-next-method class slot allocator)
(let ([initargs (getargs (cdr slot) :initarg)]
[initializer (getarg (cdr slot) :initializer)]
[initvalue (getarg (cdr slot) :initvalue ???)]
[type (getarg (cdr slot) :type #f)]
[allocation (getarg (cdr slot) :allocation :instance)]
[lock (getarg (cdr slot) :lock #f)])
(define init
(if initializer
(if (eq? 0 (procedure-arity initializer))
(lambda args (initializer)) initializer)
(lambda args initvalue)))
(define (init-slot . args)
(let ([result (l-getarg args initargs)])
(when (eq? result nothing)
(set! result (apply init args)))
(when (and type (not (eq? result ???))
(not (instance-of? result type)))
(error 'class
"bad initial value type for slot ~e in ~e (~e not a ~e)"
(car slot) class result type))
result))
(when (and type (not (specializer? type)))
(error 'class "bad type specifier for ~e: ~e" (car slot) type))
(case allocation
[(:instance)
(let* ([f (allocator init-slot)]
[g+s (mcons (lambda (o) (%instance-ref o f))
(if (and type (not (eq? <top> type)))
(lambda (o n)
(if (instance-of? n type)
(%instance-set! o f n)
(raise* make-exn:fail:contract
"slot-set!: wrong type for slot ~
`~.s' in ~e (~e not in ~e)"
(car slot) class n type)))
(lambda (o n) (%instance-set! o f n))))])
(when lock
(make-setter-locked! g+s lock
(lambda ()
(raise* make-exn:fail:contract
"slot-set!: slot `~.s' in ~.s is locked"
(car slot) (%class-name class)))))
g+s)]
[(:class)
(unless (null? initargs)
(let ([setter #f])
(%set-class-initializers!
class
(cons (lambda args
(let ([result (l-getarg args initargs)])
;; cache the setter
(unless setter
(set! setter
(mcdr (cdr (assq (car slot)
(%class-getters-n-setters
class))))))
(unless (eq? result nothing)
(setter #f result))))
(%class-initializers class)))))
(if (and (assq (car slot) (%class-direct-slots class))
(getarg (cdr (assq (car slot)
(%class-direct-slots class)))
:allocation #f))
;; the slot was declared as :class here
(let* ([cell (init)] ; default value - no arguments
[g+s (mcons (lambda (o) cell)
(lambda (o n)
(if (and type (not (instance-of? n type)))
(raise*
make-exn:fail:contract
"slot-set!: wrong type for shared slot ~
`~.s' in ~e (~e not in ~e)"
(car slot) class n type)
(set! cell n))))])
(when lock
(make-setter-locked! (car slot) g+s lock
(lambda ()
(raise* make-exn:fail:contract
"slot-set!: slot `~.s' in ~.s is locked"
(car slot) (%class-name class)))))
g+s)
;; the slot was inherited as :class - fetch its getters/setters
(let loop ([cpl (cdr (%class-cpl class))])
(cond [(assq (car slot) (%class-getters-n-setters (car cpl)))
=> cdr]
[else (loop (cdr cpl))])))]
[else
(error 'class
"allocation for `~.s' must be :class or :instance, got ~e"
(car slot) allocation)]))))))
;;; Use the previous function when populating this generic.
(add-method compute-apply-method
(make-method (list <method>) method:compute-apply-method))
(add-method no-next-method
(make-method (list <generic> <method>)
(lambda (call-next-method generic method . args)
(raise* make-exn:fail:contract
(concat "~s: no applicable next method to call"
(case (%method-qualifier method)
[(:before) " in a `before' method"]
[(:after) " in an `after' method"]
[else ""])
" with arguments: ~e")
(%generic-name generic) args))))
(add-method no-next-method
(make-method (list (singleton #f) <method>)
(lambda (call-next-method generic method . args)
(raise* make-exn:fail:contract
(concat "~s: no applicable next method in a direct method call"
" with arguments: ~e")
(%method-name method) args))))
(add-method no-applicable-method
(make-method (list <generic>)
(lambda (call-next-method generic . args)
(raise* make-exn:fail:contract
"~s: no applicable primary methods for arguments ~e, of types ~e"
(%generic-name generic) args (map class-of args)))))
;;; ---------------------------------------------------------------------------
;;; Customization variables
;;>>... Swindle Customization Parameters
;;>> *default-method-class*
;;>> *default-generic-class*
;;>> *default-class-class*
;;>> *default-entityclass-class*
;;> These parameters specify default classes for the many constructor
;;> macros in `clos'.
(define* *default-method-class* (make-parameter <method>))
(define* *default-generic-class* (make-parameter <generic>))
(define* *default-class-class* (make-parameter <class>))
(define* *default-entityclass-class* (make-parameter <entity-class>))
;; an automatic superclass for all classes -- turned off for the builtins below
;;>> *default-object-class*
;;> This parameter contains a value which is automatically made a
;;> superclass for all classes. Defaults to `<object>'.
(define* *default-object-class* (make-parameter #f))
;;>> *make-safely*
;;> Setting this parameter to #t will make Swindle perform sanity checks
;;> on given initargs for creating an object. This will make things
;;> easier for debugging, but also slower. Defaults to `#f'. Note that
;;> the sanity checks are done in `initialize'.
;; This could be in `make', but `defclass' will call that with no slots to make
;; the object and then call `initialize' with all arguments to actually create
;; the class.
(define* *make-safely* (make-parameter #f))
(define (check-initargs class initargs)
;; sanity check - verify sensible keywords given
(let ([valid-initargs (%class-valid-initargs class)])
(or (not valid-initargs)
(let loop ([args initargs])
(cond [(null? args) #t]
[(not (and (pair? args) (pair? (cdr args))))
(error 'make "error in initargs for ~e; arg list not balanced"
class)]
[(not (symbol? (car args)))
(error 'make "error in initargs for ~e; ~e is not a keyword"
class (car args))]
[(not (memq (car args) valid-initargs))
(error 'make "error in initargs for ~e; unknown keyword: ~e"
class (car args))]
[else (loop (cddr args))])))))
;;; ---------------------------------------------------------------------------
;;; Make `make' a generic function
;;>>... Creating Instances
;;; Now everything works, both generic functions and classes, so we can turn on
;;; the real MAKE.
;;; ELI: This is turned into a generic function - do this carefully - first
;;; create the generic function and the method instances, then change make.
;;>> (make class initargs ...)
;;> Create an instance of `class', which can be any Swindle class (except
;;> for some special top-level classes and built-in classes).
;;>
;;> See the `Object Initialization Protocol' below for a description of
;;> generic functions that are involved in creating a Swindle object.
(let ([m (make-method (list <class>)
(named-lambda method:make (call-next-method class . initargs)
(let ([instance (allocate-instance class initargs)])
(when (*make-safely*) (check-initargs class initargs))
(initialize instance initargs)
instance)))]
[g (make-generic-function 'make)])
(add-method g m)
(set! make g))
;; The clean concept behind this is due to Joe Marshall.
;;>> (rec-make (name class arg ...) ...)
;;> This is similar to:
;;>
;;> (letrec ([name (make class arg ...)] ...)
;;> (values name ...))
;;>
;;> except that the names are first bound to allocated instances with no
;;> initargs, and then they are initialized with all these bindings. This
;;> is useful for situations where creating some instances needs other
;;> instances as values. One sample usage is the way `defclass' makes the
;;> class binding available for slot specifications like `:type'. Note
;;> that this is a special form, which invokes `allocate-instance' and
;;> `initialize' directly, so specializing `make' on some input will not
;;> change the way `rec-make' works.
(defsubst* (rec-make (name class arg ...) ...)
(let ([name (allocate-instance class (list arg ...))] ...)
(when (*make-safely*) (check-initargs class (list arg ...)) ...)
(initialize name (list arg ...)) ...
(values name ...)))
;;; ---------------------------------------------------------------------------
;;; Make `add-method' a generic function
;;; Use this to compute a name for the method. specs is a list of classes or
;;; class-names (in case of unnamed-methods in clos.rkt).
(define (compute-method-name specs generic-name)
(define (spec-string spec)
(cond [(%singleton? spec) (format "{~.s}" (singleton-value spec))]
[(%class? spec) (symbol->string
(%class-name (%struct->class spec)))]
[else "???"]))
(string->symbol
(apply string-append
(symbol->string generic-name) ":"
(if (null? specs)
'("()")
(cons (spec-string (car specs))
(map (lambda (c) (string-append "," (spec-string c)))
(cdr specs)))))))
(let ([old-add-method add-method])
(set! add-method (make <generic> :name 'add-method :arity 2))
(old-add-method add-method
(make-method (list <generic> <method>)
(named-lambda method:add-method (call-next-method generic method)
(let ([method-arity (method-arity method)]
[generic-arity (%generic-arity generic)])
(cond
[(not generic-arity)
(%set-generic-arity! generic method-arity)]
;; note: equal? works on arity-at-least structs
[(not (equal? generic-arity method-arity))
(error 'add-method
"wrong arity for `~.s', expects ~a; given a method with ~a"
(%generic-name generic)
(if (integer? generic-arity)
generic-arity
(format "at-least-~a"
(arity-at-least-value generic-arity)))
(if (integer? method-arity)
method-arity
(format "at-least-~a"
(arity-at-least-value method-arity))))])
;; set a name for the method if none (when attached to a generic)
(let ([n (%method-name method)])
(unless (and n (not (eq? n '-anonymous-)))
(%set-method-name!
method
(let* ([psym (object-name (%method-procedure method))]
[pstr (and psym (symbol->string psym))])
(if (or (not pstr) (regexp-match? #rx":[0-9]*:[0-9]*$" pstr))
(compute-method-name (%method-specializers method)
(%generic-name generic))
psym)))))
(old-add-method generic method))))))
;;; Optimized frequently used accessors:
;;; This is possible because of the ordering of the slots in compute-slots,
;;; works only for single-inheritance. Note that there is no type checking -
;;; it is unsafe, but makes things around 5-6 times faster!
(set! %class-direct-slots (%slot-getter <class> 'direct-slots))
(set! %class-direct-supers (%slot-getter <class> 'direct-supers))
(set! %class-slots (%slot-getter <class> 'slots))
(set! %class-nfields (%slot-getter <class> 'nfields))
(set! %class-field-initializers (%slot-getter <class> 'field-initializers))
(set! %class-getters-n-setters (%slot-getter <class> 'getters-n-setters))
(set! %class-cpl (%slot-getter <class> 'cpl))
(set! %class-name (%slot-getter <class> 'name))
(set! %class-initializers (%slot-getter <class> 'initializers))
(set! %class-valid-initargs (%slot-getter <class> 'valid-initargs))
(set! %generic-methods (%slot-getter <generic> 'methods))
(set! %generic-arity (%slot-getter <generic> 'arity))
(set! %generic-name (%slot-getter <generic> 'name))
(set! %generic-combination (%slot-getter <generic> 'combination))
(set! %method-specializers (%slot-getter <method> 'specializers))
(set! %method-procedure (%slot-getter <method> 'procedure))
(set! %method-qualifier (%slot-getter <method> 'qualifier))
(set! %method-name (%slot-getter <method> 'name))
(set! %set-class-direct-slots! (%slot-setter <class> 'direct-slots))
(set! %set-class-direct-supers! (%slot-setter <class> 'direct-supers))
(set! %set-class-slots! (%slot-setter <class> 'slots))
(set! %set-class-nfields! (%slot-setter <class> 'nfields))
(set! %set-class-field-initializers!(%slot-setter <class> 'field-initializers))
(set! %set-class-getters-n-setters! (%slot-setter <class> 'getters-n-setters))
(set! %set-class-cpl! (%slot-setter <class> 'cpl))
(set! %set-class-name! (%slot-setter <class> 'name))
(set! %set-class-initializers! (%slot-setter <class> 'initializers))
(set! %set-class-valid-initargs! (%slot-setter <class> 'valid-initargs))
(set! %set-generic-methods! (%slot-setter <generic> 'methods))
(set! %set-generic-arity! (%slot-setter <generic> 'arity))
(set! %set-generic-name! (%slot-setter <generic> 'name))
(set! %set-generic-combination! (%slot-setter <generic> 'combination))
(set! %set-method-specializers! (%slot-setter <method> 'specializers))
(set! %set-method-procedure! (%slot-setter <method> 'procedure))
(set! %set-method-qualifier! (%slot-setter <method> 'qualifier))
(set! %set-method-name! (%slot-setter <method> 'name))
;; Optimize these internal ones as well.
(set! %generic-app-cache (%slot-getter <generic> 'app-cache))
(set! %generic-singletons-list (%slot-getter <generic> 'singletons-list))
(set! %set-generic-app-cache! (%slot-setter <generic> 'app-cache))
(set! %set-generic-singletons-list! (%slot-setter <generic> 'singletons-list))
;;; ---------------------------------------------------------------------------
;;; Built-in classes.
;;>>... Built-in Classes
;;>> <primitive-class>
;;> The class of all built-on classes.
(define* <primitive-class>
(make <class> :direct-supers (list <class>)
:direct-slots '()
:name '<primitive-class>
;; needed so structs can turn to classes even if *make-safely*
:valid-initargs #f))
;; Normally, can't allocate these.
(add-method allocate-instance
(make-method (list <primitive-class>)
(named-lambda method:allocate-instance (call-next-method class initargs)
(error 'allocate-instance "can't instantiate a primitive class ~e"
class))))
;;>> <builtin>
;;> The superclass of all built-in classes.
(define* <builtin>
(make <class> :direct-supers (list <top>)
:direct-slots '()
:name '<builtin>))
(defsubst (defprimclass primclass) (defprimclass primclass <builtin>)
(_ primclass supers ...) (define* primclass
(make <primitive-class>
:name 'primclass
:direct-supers (list supers ...)
:direct-slots '())))
;;>> <sequence>
;;>> <mutable>
;;>> <immutable>
;;>> <pair>
;;>> <mutable-pair>
;;>> <mpair>
;;>> <immutable-pair>
;;>> <list>
;;>> <nonempty-list>
;;>> <null>
;;>> <vector>
;;>> <char>
;;>> <string-like>
;;>> <mutable-string-like>
;;>> <immutable-string-like>
;;>> <string>
;;>> <mutable-string>
;;>> <immutable-string>
;;>> <bytes>
;;>> <mutable-bytes>
;;>> <immutable-bytes>
;;>> <path>
;;>> <symbol>
;;>> <keyword>
;;>> <real-keyword>
;;>> <boolean>
;;>> <number>
;;>> <exact>
;;>> <inexact>
;;>> <complex>
;;>> <real>
;;>> <rational>
;;>> <integer>
;;>> <exact-complex>
;;>> <inexact-complex>
;;>> <exact-real>
;;>> <inexact-real>
;;>> <exact-rational>
;;>> <inexact-rational>
;;>> <exact-integer>
;;>> <inexact-integer>
;;>> <end-of-file>
;;>> <port>
;;>> <input-port>
;;>> <output-port>
;;>> <stream-port>
;;>> <input-stream-port>
;;>> <output-stream-port>
;;>> <void>
;;>> <box>
;;>> <weak-box>
;;>> <regexp>
;;>> <byte-regexp>
;;>> <parameter>
;;>> <promise>
;;>> <exn>
;;>> <exn:fail>
;;>> <exn:break>
;;>> <semaphore>
;;>> <hash-table>
;;>> <subprocess>
;;>> <thread>
;;>> <syntax>
;;>> <identifier-syntax>
;;>> <namespace>
;;>> <custodian>
;;>> <tcp-listener>
;;>> <security-guard>
;;>> <will-executor>
;;>> <struct-type>
;;>> <inspector>
;;>> <pseudo-random-generator>
;;>> <compiled-expression>
;;>> <unknown-primitive>
;;> These classes represent built-in objects. See the class hierarchy
;;> below for a complete description of the relations between these
;;> classes.
;;>> <struct>
;;>> <opaque-struct>
;;> These are also classes for built-in objects, but they are classes for
;;> Racket structs -- which can be used like Swindle classes since they
;;> will get converted to appropriate Swindle subclasses of `<struct>'.
;;> `<opaque-struct>' is a class of structs that are hidden -- see the
;;> documentation for `struct-info' and the `skipped?' result. Note that
;;> structs can be used as long as they can be inspected -- otherwise, we
;;> can't even know that they are structs with `struct?' (this means that
;;> <opaque-struct> can only appear in the cpl of a struct class that
;;> inherits from a struct which is not under the current inspector).
(defprimclass <sequence>)
(defprimclass <mutable>)
(defprimclass <immutable>)
(defprimclass <pair> <sequence>)
(defprimclass <mutable-pair> <pair> <mutable>)
(define* <mpair> <mutable-pair>) ; alias
(defprimclass <immutable-pair> <pair> <immutable>)
(defprimclass <list> <sequence>)
(defprimclass <nonempty-list> <pair> <list> <immutable>)
(defprimclass <null> <list>)
(defprimclass <vector> <sequence> <mutable>)
(defprimclass <char>)
(defprimclass <string-like> <sequence>)
(defprimclass <mutable-string-like> <string-like> <mutable>)
(defprimclass <immutable-string-like> <string-like> <immutable>)
(defprimclass <string> <string-like>)
(defprimclass <mutable-string> <string> <mutable-string-like>)
(defprimclass <immutable-string> <string> <immutable-string-like>)
(defprimclass <bytes> <string-like>)
(defprimclass <mutable-bytes> <bytes> <mutable-string-like>)
(defprimclass <immutable-bytes> <bytes> <immutable-string-like>)
(defprimclass <path> <immutable-string-like>)
(defprimclass <symbol>)
(defprimclass <keyword> <symbol>)
(defprimclass <real-keyword>)
(defprimclass <boolean>)
;; Have all possible number combinations in any case
(defprimclass <number>)
(defprimclass <exact> <number>)
(defprimclass <inexact> <number>)
(defprimclass <complex> <number>)
(defprimclass <real> <complex>)
(defprimclass <rational> <real>)
(defprimclass <integer> <rational>)
(defprimclass <exact-complex> <complex> <exact>)
(defprimclass <inexact-complex> <complex> <inexact>)
(defprimclass <exact-real> <real> <exact-complex>)
(defprimclass <inexact-real> <real> <inexact-complex>)
(defprimclass <exact-rational> <rational> <exact-real>)
(defprimclass <inexact-rational> <rational> <inexact-real>)
(defprimclass <exact-integer> <integer> <exact-rational>)
(defprimclass <inexact-integer> <integer> <inexact-rational>)
(defprimclass <end-of-file>)
(defprimclass <port>)
(defprimclass <input-port> <port>)
(defprimclass <output-port> <port>)
(defprimclass <stream-port> <port>)
;; Racket stuff
(defprimclass <input-stream-port> <input-port> <stream-port>)
(defprimclass <output-stream-port> <output-port> <stream-port>)
(defprimclass <void>)
(defprimclass <box> <mutable>)
(defprimclass <weak-box> <box>)
(defprimclass <regexp>)
(defprimclass <byte-regexp>)
(defprimclass <parameter>)
(defprimclass <promise>)
(defprimclass <exn>)
(defprimclass <exn:fail> <exn>)
(defprimclass <exn:break> <exn>)
;; make these classes used when we see exn structs
(let ([set-exn-class
(lambda (class make-exn . xs)
(hash-table-put! struct-to-class-table
(let-values ([(e _)
(struct-info
(apply make-exn "foo"
(current-continuation-marks)
xs))])
e)
class))])
(set-exn-class <exn> make-exn)
(set-exn-class <exn:fail> make-exn:fail)
(set-exn-class <exn:break> make-exn:break (let/ec e e)))
(defprimclass <semaphore>)
(defprimclass <hash-table>)
(defprimclass <subprocess>)
(defprimclass <thread>)
(defprimclass <syntax>)
(defprimclass <identifier-syntax> <syntax>)
(defprimclass <namespace>)
(defprimclass <custodian>)
(defprimclass <tcp-listener>)
(defprimclass <security-guard>)
(defprimclass <will-executor>)
(defprimclass <struct-type>)
(defprimclass <inspector>)
(defprimclass <pseudo-random-generator>)
(defprimclass <compiled-expression>)
(defprimclass <unknown-primitive>)
(defprimclass <struct>)
(defprimclass <opaque-struct> <struct>)
;;>> <procedure>
;;> The class of all Scheme procedures.
(define* <procedure>
(make <procedure-class> :name '<procedure>
:direct-supers (list <builtin> <function>)
:direct-slots '()))
;;>> <primitive-procedure>
;;> The class of all primitive Racket procedures.
(define* <primitive-procedure>
(make <procedure-class>
:name '<primitive-procedure>
:direct-supers (list <procedure>)
:direct-slots '()))
(*default-object-class* <object>) ; turn auto-superclass back on
(set! class-of
(lambda (x)
;; If all Schemes were IEEE compliant, the order of these wouldn't
;; matter?
;; ELI: changed the order so it fits better the expected results.
(cond [(instance? x) (instance-class x)]
[(struct? x)
(let-values ([(type _) (struct-info x)])
(if type (struct-type->class type) <opaque-struct>))]
[(procedure? x) (cond [(parameter? x) <parameter>]
[(primitive? x) <primitive-procedure>]
[else <procedure>])]
[(string? x) (if (immutable? x) <immutable-string> <string>)]
[(pair? x) (if (list? x) <nonempty-list> <immutable-pair>)]
[(null? x) <null>]
[(symbol? x) (if (keyword? x) <keyword> <symbol>)]
[(number? x)
(if (exact? x)
(cond [(integer? x) <exact-integer>]
[(rational? x) <exact-rational>]
[(real? x) <exact-real>]
[(complex? x) <exact-complex>]
[else <exact>]) ; should not happen
(cond [(integer? x) <inexact-integer>]
[(rational? x) <inexact-rational>]
[(real? x) <inexact-real>]
[(complex? x) <inexact-complex>]
[else <inexact>]))] ; should not happen
[(boolean? x) <boolean>]
[(char? x) <char>]
[(bytes? x) (if (immutable? x) <immutable-bytes> <bytes>)]
[(path? x) <path>]
[(vector? x) <vector>]
[(mpair? x) <mutable-pair>]
[(eof-object? x) <end-of-file>]
[(input-port? x)
(if (file-stream-port? x) <input-stream-port> <input-port>)]
[(output-port? x)
(if (file-stream-port? x) <output-stream-port> <output-port>)]
[(void? x) <void>]
[(box? x) <box>]
[(weak-box? x) <weak-box>]
[(regexp? x) <regexp>]
[(byte-regexp? x) <byte-regexp>]
[(promise? x) <promise>]
[(real-keyword? x) <real-keyword>]
[(semaphore? x) <semaphore>]
[(hash-table? x) <hash-table>]
[(thread? x) <thread>]
[(subprocess? x) <subprocess>]
[(syntax? x)
(if (identifier? x) <identifier-syntax> <syntax>)]
[(namespace? x) <namespace>]
[(custodian? x) <custodian>]
[(tcp-listener? x) <tcp-listener>]
[(security-guard? x) <security-guard>]
[(will-executor? x) <will-executor>]
[(struct-type? x) <struct-type>]
[(inspector? x) <inspector>]
[(pseudo-random-generator? x) <pseudo-random-generator>]
[(compiled-expression? x) <compiled-expression>]
[else <unknown-primitive>])))
;;; ---------------------------------------------------------------------------
;;; Some useful predicates.
;;>> (builtin? x)
;;>> (function? x)
;;>> (generic? x)
;;>> (method? x)
;;> Predicates for instances of <builtin>, <function>, <generic>, and
;;> <method>.
(define* (builtin? x) (instance-of? x <builtin>))
(define* (function? x) (instance-of? x <function>))
(define* (generic? x) (instance-of? x <generic>))
(define* (method? x) (instance-of? x <method>))
;;; ---------------------------------------------------------------------------
;;>>... Class Hierarchy
;;>
;;> In the following, every class's class is specified after a colon. Also,
;;> some classes appear in more than once place due to multiple-inheritance.
;;>
;;> <top> : <class>
;;> <object> : <class>
;;> <class> : <class>
;;> <procedure-class> : <class>
;;> <entity-class> : <class>
;;> <primitive-class> : <class>
;;> <generic> : <entity-class>
;;> <method> : <entity-class>
;;> <function> : <class>
;;> <generic> : <entity-class>
;;> <method> : <entity-class>
;;> <procedure> : <procedure-class>
;;> <primitive-procedure> : <procedure-class>
;;> <builtin> : <class>
;;> <sequence> : <primitive-class>
;;> <pair> : <primitive-class>
;;> <mutable-pair> : <primitive-class>
;;> <mpair> : <primitive-class> ; alias for <mutable-pair>
;;> <immutable-pair> : <primitive-class>
;;> <nonempty-list> : <primitive-class>
;;> <list> : <primitive-class>
;;> <nonempty-list> : <primitive-class>
;;> <null> : <primitive-class>
;;> <vector> : <primitive-class>
;;> <string-like> : <primitive-class>
;;> <string> : <primitive-class>
;;> <mutable-string> : <primitive-class>
;;> <immutable-string> : <primitive-class>
;;> <bytes> : <primitive-class>
;;> <mutable-bytes> : <primitive-class>
;;> <immutable-bytes> : <primitive-class>
;;> <path> : <primitive-class>
;;> <mutable> : <primitive-class>
;;> <mutable-pair> : <primitive-class>
;;> <mpair> : <primitive-class> ; alias for <mutable-pair>
;;> <mutable-string-like> : <primitive-class>
;;> <mutable-string> : <primitive-class>
;;> <mutable-bytes> : <primitive-class>
;;> <vector>
;;> <box>
;;> <immutable> : <primitive-class>
;;> <list> : <primitive-class>
;;> <pair> : <primitive-class>
;;> <immutable-string-like> : <primitive-class>
;;> <immutable-string> : <primitive-class>
;;> <immutable-bytes> : <primitive-class>
;;> <path> : <primitive-class>
;;> <char> : <primitive-class>
;;> <symbol> : <primitive-class>
;;> <keyword> : <primitive-class>
;;> <real-keyword> : <primitive-class>
;;> <boolean> : <primitive-class>
;;> <number> : <primitive-class>
;;> <complex> : <primitive-class>
;;> <exact-complex> : <primitive-class>
;;> <inexact-complex> : <primitive-class>
;;> <real> : <primitive-class>
;;> <exact-real> : <primitive-class>
;;> <inexact-real> : <primitive-class>
;;> <rational> : <primitive-class>
;;> <integer> : <primitive-class>
;;> <exact-rational> : <primitive-class>
;;> <inexact-rational> : <primitive-class>
;;> <exact-integer> : <primitive-class>
;;> <inexact-integer> : <primitive-class>
;;> <exact> : <primitive-class>
;;> <exact-complex> : <primitive-class>
;;> <exact-real> : <primitive-class>
;;> <exact-rational> : <primitive-class>
;;> <exact-integer> : <primitive-class>
;;> <inexact> : <primitive-class>
;;> <inexact-complex> : <primitive-class>
;;> <inexact-real> : <primitive-class>
;;> <inexact-rational> : <primitive-class>
;;> <inexact-integer> : <primitive-class>
;;> <end-of-file> : <primitive-class>
;;> <port> : <primitive-class>
;;> <input-port> : <primitive-class>
;;> <input-stream-port> : <primitive-class>
;;> <output-port> : <primitive-class>
;;> <output-stream-port> : <primitive-class>
;;> <stream-port> : <primitive-class>
;;> <input-stream-port> : <primitive-class>
;;> <output-stream-port> : <primitive-class>
;;> <void> : <primitive-class>
;;> <box> : <primitive-class>
;;> <weak-box> : <primitive-class>
;;> <regexp> : <primitive-class>
;;> <byte-regexp> : <primitive-class>
;;> <parameter> : <primitive-class>
;;> <promise> : <primitive-class>
;;> <exn> : <primitive-class>
;;> <exn:fail> : <primitive-class>
;;> <exn:break> : <primitive-class>
;;> <semaphore> : <primitive-class>
;;> <hash-table> : <primitive-class>
;;> <subprocess> : <primitive-class>
;;> <thread> : <primitive-class>
;;> <syntax> : <primitive-class>
;;> <identifier-syntax> : <primitive-class>
;;> <namespace> : <primitive-class>
;;> <custodian> : <primitive-class>
;;> <tcp-listener> : <primitive-class>
;;> <security-guard> : <primitive-class>
;;> <will-executor> : <primitive-class>
;;> <inspector> : <primitive-class>
;;> <pseudo-random-generator> : <primitive-class>
;;> <compiled-expression> : <primitive-class>
;;> <unknown-primitive> : <primitive-class>
;;> <procedure> : <procedure-class>
;;> <primitive-procedure> : <procedure-class>
;;> <struct> : <primitive-class>
;;> <opaque-struct> : <primitive-class>
;;> ... struct type classes ...
;;>>... Object Initialization Protocol
;;> This is the initialization protocol. All of these are generic
;;> functions (unlike the original Tiny CLOS). See the individual
;;> descriptions above for more details.
;;>
;;> make
;;> allocate-instance
;;> initialize
;;> class initialization only:
;;> compute-cpl
;;> compute-slots
;;> compute-getter-and-setter
;;> method initialization only:
;;> compute-apply-method
;;> add-method
;;> compute-apply-generic
;;> compute-methods
;;> compute-method-more-specific?
;;> compute-apply-methods
|