aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
authorGravatar Peter McGoron 2025-03-05 17:57:07 -0500
committerGravatar Peter McGoron 2025-03-05 17:57:07 -0500
commit122924a4d15b11a46ec4bade401174fe810cce15 (patch)
tree5d2640e25aa9df4ea915b89cebafaf63699a42d3 /README.md
parentremove special handling of the default implementation (diff)
add subtyping back
Diffstat (limited to 'README.md')
-rw-r--r--README.md139
1 files changed, 75 insertions, 64 deletions
diff --git a/README.md b/README.md
index 449fd00..451e52a 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,14 @@ surprises.
### Why Singly Polymorphic (aka Single Dispatch)?
-Multiple dispatch runs into the problems that subtype inheritance runs
-into (see the section "On Inheritance").
+Multiple dispatch runs into problems similar to that of multiple
+inheritance, except that there is no obvious dismabiguation pattern when
+two equally specific multiple dispatch methods exist.
-If one wanted multiple dispatch or multiple inheritance, it would be better
-to use a CLOS style system, which has much more fine-grained control over
-method dispatch. Dynamic and local scoping could be added to a CLOS style
-system, although the API would probably have to change.
+If one wanted multiple dispatch or multiple inheritance, it would be
+better to use a CLOS style system, which has much more fine-grained
+control over method dispatch. Dynamic and local scoping could be added
+to a CLOS style system, although the API would probably have to change.
### Why Scope?
@@ -52,11 +53,15 @@ without mutating global state and without overriding local scope.
### On Inheritance
-I am very conflicted on inheritance for subtypes of a type. The issue
-is that combining it with scoping introduces a form of the diamond
-problem. For example: imagine if type `T` has an implementation in global
-scope, and its supertype `T'` has an implementation in local scope. Which
-one should be used?
+Scheme, both through the numeric tower (see below) and R6RS, have single
+inheritance. I do not like inheritance: Rust (which inspired me to write
+this) does not have inheritance at all.
+
+Combining subtype inheritance (even when restricted to single inheritance)
+with scoping introduces a form of the diamond problem. For example:
+imagine if type `T` has an implementation in global scope, and its
+supertype `T'` has an implementation in local scope. Which one should
+be used?
SAHP
/ \
@@ -66,15 +71,9 @@ one should be used?
\ /
T
-There are two reasonable choices: most specific and closest scope. Closest
-scope is probably the best option, but in some instances it may not
-be enough. One might want to specify a default implementation without
-desiring to override all specific implementations of subtypes. This could
-be done by giving one the ability to specify how each implementation
-should be inherited.
-
-SAHPs currently do not automatically inherit implementations. Records
-designed with SAHPs in mind should use composition, not inheritance.
+SAHPs default to selecting the closest scope. However, an implementation
+for a type may be labeled `overridable`, in which case if a more specific
+type occurs in a higher scope, that implementation is selected.
### The Numeric Tower
@@ -94,50 +93,44 @@ defined. They will usually consist of
* irrationals,
* proper complex numbers.
-An implementation could also have unitful numbers, quarternions, etc.
-
-In order to support the numeric tower, SAHPs define portable sets of
-types arranged in a hierarchy of subsets. They are
-
- all-exact-integers
- ⊆ all-exact-rational
- ⊆ all-real
- ⊆ all-complex
- ⊆ all-number
+Notably, exactness *is* decided by type (or more accurately, the machine
+representation).
-Type sets are lists that contain other type sets or concrete
-types. Defining an implementation against a type set explicitly
-defines the type set against all implementations, overriding previous
-implementations: hence the prefix `all`. These sets are static.
+SAHPs dispatch on the following hierarchy of types:
-### What Types are dispatched against?
+* `exact-integer`
+* `exact-rational`
+* `real`
+* `complex`
+* `number`
-All fundamental types (pairs, fixnums, vectors, defined record types).
-Predicates are not dispatched against because that would make the type
-system inefficient. One could force this by putting predicates into the
-default implementation of a SAHP.
+This probably captures most practical uses of the numeric tower.
## API
A SAHP is a procedure object. There is no portable way to differentiate
-between a SAHP and a regular procedure. A SAHP has multiple tables (some
-conceptual) where an implementation is looked up. In order, the SAHP will
+between a SAHP and a regular procedure. A SAHP has multiple tables
+(some conceptual) where an implementation is looked up. The SAHP will
dispatch on (in order of precedence):
* local scope
* dynamic scope
* global scope
-* default implementation in local scope
-* default implementation in dynamic scope
-* default implementation in global scope
-The scope resolution tries to find the most specific implementation, and
-given a specific implementation, the closest scope.
+The scope resolution tries to find the most specific implementation in
+the closest scope. If the implementation in closest scope is not the
+most specific implementation, then:
+
+* If the implementation in closest scope is labeled `overridable`, then
+ the implementation in the next closest scope is selected, and this
+ disambiguation process continues again.
+* If the implementation in closest scope is not labeled `overridable`,
+ then it is selected.
The SAHP will raise an error object that satisfies the predicate
`SAHP-implementation-not-found-error?` if no implementation is found.
-Multiple SAHPs can share global scope tables, and dynamic scope.
+Multiple SAHPs can share global scope tables and dynamic scope.
The procedures stored in a SAHP must take at least one argument, which
is the argument whose type is dispatched against.
@@ -150,13 +143,10 @@ it will call `procedure` with all of the arguments passed to it.
A type expression is defined as:
-* The symbols `boolean`, `char`, `null`, `pair`, `procedure`, `symbol`,
- `bytevector`, `eof-object`, `port`, `string`, or `vector`, or
-* The values of `all-exact-integers`, `all-exact-rationals`,
- `all-real`, `all-complex`, and `all-number`, or
-* Lists of type expressions, or
+* The symbols `*` `boolean`, `char`, `null`, `pair`, `procedure`, `symbol`,
+ `bytevector`, `eof-object`, `port`, `string`, `vector`,
+ `exact-integers`, `exact-rationals`, `reals`, `complex`, or `numbers`, or
* the value bound to a name of a record type, or
-* the symbol `*`, for the default implementation
* any other implementation defined value.
### The Object
@@ -173,33 +163,50 @@ dynamic state. SAHPs that are `SAHP=?` are not necessarily `eqv?`.
### Global Scope
- (set-global-SAHP! SAHP type-expr procedure procedure)
+ (set-global-SAHP! SAHP type procedure)
+
+or
+
+ (set-global-SAHP! SAHP type flag procedure)
+
+The flag can be omitted, or the symbol `overridable`.
-Set `SAHP` to resolve to `procedure` given `type-expr` in global scope.
+Set `SAHP` to resolve to `procedure` given `type` in global scope.
- (define-global-SAHP (name (type-expr argument) . arg-rest) body ...)
+ (define-global-SAHP (name (type argument) . arg-rest) body ...)
+
+or
+
+ (define-global-SAHP (name (type flag argument) . arg-rest) body ...)
Creates a procedure with the given arguments and body. The SAHP will
-resolve to this procedure given `type-expr` in global scope.
+resolve to this procedure given `type` in global scope.
+
+The flag can be omitted, or the symbol `overridable`.
### Dynamic Scope
- (parameterize-SAHP ((SAHP (type-expr value) ...) ...) body ...)
+ (parameterize-SAHP ((SAHP (type value) ...) ...) body ...)
+
+or
+
+ (parameterize-SAHP ((SAHP (type flag value) ...) ...) body ...)
Each `SAHP` must resolve to an SAHP.
-Each SAHP will have each `type-expr` under it resolve to `value`
-in dynamic scope. These procedures take precedence over global scope
-procedures. The SAHPs are the same SAHP in the block in the sense of
-`eq?`.
+Each SAHP will have each `type` under it resolve to `value` in dynamic
+scope. These procedures take precedence over global scope procedures.
+The SAHPs are the same SAHP in the block in the sense of `eq?`.
+
+The flag can be omitted, or the symbol `overridable`.
### Local Scope
- (letrec-SAHP ((SAHP (type-expr value) ...) ...) body ...)
+ (letrec-SAHP ((SAHP (type type value) ...) ...) body ...)
Each `SAHP` must be an identifier that resolves to a SAHP.
-Each SAHP will have each `type-expr` under it resolve to the corresponding
+Each SAHP will have each `type` under it resolve to the corresponding
`value` in lexical scope. Each procedure will have the new SAHPs in
scope. These procedures will take precedence over dynamic and global
scope procedures.
@@ -213,6 +220,10 @@ If the SAHPs passed to `letrec-SAHP` had local bindings, then
`letrec-SAHP` will replace old bindings and add new ones without affecting
the previous SAHP object.
+The flag can be omitted, or the symbol `overridable`.
+
## TODO
* Standard SAHP library.
+* `if-not-exists` to provide default implementations for specific
+ types.