Added tests using define/generic in both #:defaults and #:fallbacks, both inside
modules and at the top level. Changed the implementation of define-generics so
that defaults and fallbacks come after method definitions so that define/generic
works properly. Changed internal names to be created with format-id and
syntax-local-introduce rather than generate-temporaries so that they work at the
top level.
The `mzlib/compile' module re-exports the moved function.
Also, fix `make-directory*' so that it never fails on an existing
directory. Remove `unstable/file', since it was just the same
clean-up of `make-directory*'.
The #:fallbacks option specifies a set of method definitions. Each definition
is used for any value that does not support the specific method. Like
allows define/generic and absent method definitions. If neither a specific
implementation or the #:fallbacks clause defines a given method, the normal
runtime error is raised. The #:defined-table option reports whether a value's
specific implementation supports a method; the presence of a #:fallbacks
implementation for a method does not change this result.
Specifically, in the test, the bound name and the method name have marks on them
that are different from each other and from the original method names in the
enclosing generics group.
Specifically, with the reorganization of racket/private/generic, different
method signatures (sets of required and optional, positional and keyword
arguments) exercise different paths in the code, at phases 0 and 1. The tests
therefore include a variety of different method signatures.
Specifically, implementations for the #:defaults keyword in define-generics can
now use define/generic to get at the generic implementation of a method for
which a specific implementation is defined locally. Also, unimplemented methods
are handled properly now in #:defaults. Previously, an unimplemented method in
a #:defaults specification would go into an infinite loop if applied, because
the implementation for the specific type wound up referring to the generic
implementation of the method.
A lot of the back-end implementation of generics changes in this commit:
- The new module racket/private/generic-methods provides a uniform mechanism for
defining method tables and recording static information about generics
groups. Both #:methods in [define-]struct and #:defaults in define-generics
use this framework now. In addition, generics based on existing properties
such as gen:stream, gen:equal+hash, and gen:custom-write now use the struct
from this module to store the names associated with the generics groups.
- Generic methods now expand directly into functions with the appropriate arity,
and refer directly to the appropriate argument to perform generic method
dispatch. The previous implementation used procedure-reduce-keyword-arity to
restrict the arity dynamically, and used list-ref to find the generic
argument.
- Some error messages have changed slightly; hopefully for the better, but this
change did require some changes to tests for specific error messages.
Now the primitive generics form just defines generics with their properties and
methods, and the surface-level form calls the contract definition form
directly. This means the primitive generics form now requires an explicit name
for a struct property accessor, so that the same name can be used for the
contract.
In the few cases that used this option, the only definition needed from the
private define-generics macro was gen:<name>. This is easy to define directly
without using the macro, so I have changed the code to do so and avoid redundant
definitions of methods.
The implement of `free-id-table' uses `identifier-binding'
to decide on a symbolic name as a key for each identifier,
but `identifier-binding' doesn't provide enough information
for local and top-level bindings. The new `identifier-binding-symbol'
function provides that information.
Closes PR 13911
(plus one new file to act as a driver for
running the whole test suite)
Beyond the obviousness of this cleanup, it also helps me
test the contract system without having to load any code
that depends on it (making the test cases far more useful
when I'm breaking low-level parts of the contract system
(like ->))