Earlier we voted on #125, #229 and #345 separately without regard to the formal semantics of eqv? from a top level. We need to first decide what the definition of eqv? is, and consider if there should be any exception cases as a secondary effect.
The debate is fundamentally one of whether we define eqv? in terms of "operational equivalence" as in R6RS or a simpler rule (e.g. resolve by =) as in earlier standards.
R2RS had the simplest historical rule which was simply to use =.
The term "operational equivalence" appears in R3RS but for numbers the definition is the same as in R4RS and R5RS, which is = plus the same exactness. This is the r5rs option.
R6RS defines two numbers to be "operationally equivalent" (and therefore eqv?) if they would return the same results for any finite composition of "Scheme's standard arithmetic procedures". Presumably this refers to everything in section 11.7 "Arithmetic", but not, for example, write. For R7RS this would be equivalent to all the standard procedures in the "Numbers" section 6.2.6, and is called the r6rs option. Alternately, the equivalence rules could include any procedure the implementation provides (such as decode-float) - this is the r6rs/all option. Operational equivalence is very desirable when implementing caches and programs which reason about programs.
In contrast to R6RS, R7RS does not require the full numeric tower. This means that any definition of operational equivalence would render many numbers unspecified from the perspective of the standard, yet users could rely on consistency within their own implementation, and broad agreement amongst most implementations which provide the full tower. For example, (eqv? 1/3 (/ 3.0)) is unspecified under any definition, because exact ratios may not be supported. However, (eqv? 0.0 -0.0) is specified and required to return #t under the r5rs rule, yet is unspecified under the r6rs rule - it would return #f for implementations which distinguish both -0.0 and -inf.0 (without which -0.0 does not produce different results from 0.0) and #t otherwise. Under r6rs/all, (eqv? 0.0 -0.0) should return #f under any implementation which would at least write -0.0 with the leading minus sign. Any notation using precision specifiers would also has undefined equivalence under the r6rs rule, e.g. (eqv? 1e0 1f0).
An exception is often desired for (eqv? +nan.0 +nan.0), which would naturally return #f for r5rs. For the operational semantics in r6rs this would also return #f, because the results are always +nan.0 which would never compare as =. However, R6RS makes an exception here and states (eqv? +nan.0 +nan.0) => unspecified in contradiction to its own rule. Others have argued that it should in fact return #t, in contrast to both R5RS and R6RS, on the grounds that what they really want to compare is the underlying representation (which would be the same for some but not all NaNs). In this sense we have a new option underlying-implementation, which should return #t iff is the arguments are of the same underlying type and bit layout (the same* option from earlier ballots). This option is new and perhaps unsuitable for a standard.
Since this is so much disagreement and confusion on the +nan.0 case it is reasonable to make it an explicitly unspecified exception, and the variant /unspecified-nan is provided for all options. Note r6rs implies (eqv? +nan.0 +nan.0) => #f, and r6rs/unspecified-nan is the exception case as actually listed in R6RS.
The other special case is procedures. In R3RS, an operational equivalence was defined for procedures, and this was subsequently removed. R6RS went the other direction and allowed the exact same procedure x to return #f for (eqv? x x), and R7RS currently reaffirms this. The rationale behind this is for compiler optimizations such as inlining local procedures. Add the variant /eq-procs to any option to also revert this change.
Keep in mind the semantics of eqv? also affect memv, assv and case. The result of this ticket will also answer formal comment #423.
Currently, R7RS-small says that when equal? is applied to records that are not eqv? (that were constructed by different calls to the record constructor), the result may be #t or #f at the implementation's discretion. The proposal is to treat records of the same type like pairs, strings, vectors, and bytevectors: that is, their contents are recursively compared.
Vote recursive to require recursive comparison of the record's fields, identity to return #t iff eqv? does, and unspecified to leave this open.
Note equal? is already required to handle cycles regardless.
Specifically, does it indicate case-folding and normalization support for the repertoire of any particular version of Unicode, or any version greater than 5 or 6 or 6.1, or no particular version?
Full unicode refers to the set of characters available. Case-folding and character predicates are required to work according to the Unicode standard for all supported characters. The question of which version of Unicode the property refers to is important. We could require a specific version (and all further revisions), or always require the latest official Unicode standard, in which case an implementation would not be compliant until it was updated with each new standard.
There have been a bunch of complaints about the formal semantics: it's no longer up to date, it has been incomplete since the beginning, it cannot be mechanized with a proof assistant, it doesn't help either users or implementers very much, and so on. See in particular #453.
This proposal is to remove it from the report altogether, and to urge the Steering Committee to create a new WG to produce one, likely in a "rolling" style with increasingly comprehensive releases, on its own schedule. Some members of the current WG have expressed interest in serving on such a group, and others have expressed their complete lack of interest, so a new WG seems the best choice if this is done.
R5RS says it's an error for a key to appear in more than one clause of case (or twice in the same clause, but that's trivial). R6RS allows the same key to appear more than one clause, but insists on left-to-right testing throughout, like cond. The R6RS editors thought this was better for machine-generated code, though worse for hand-written code.
The proposal is a compromise: allow keys to appear in more than one clause, but behave as if the key appeared only in the first (leftmost) clause. This allows hash-table or other non-left-to-right implementations.
For ease of implementation, the proposal is to make it an error for an imported identifier to be referenced or defined in a library before the library declaration that imports it. This allows strict left-to-right processing of library declarations, with no need to delay processing till the end of the library.
Therefore, this would be an error (but still permitted as an extension in Schemes that can easily provide it):
(module (begin (define x y)) (import (library defining y))This would necessitate replacing the penultimate paragraph of section
One possible implementation of libraries is as follows: After all cond-expand library declarations are expanded, a new environment is constructed for the library consisting of all imported bindings. The expressions and declarations from all begin, include, and include-ci declarations are expanded in that environment in the order in which they occur in the library declaration. Alternatively, cond-expand and import declarations may be processed in left to right order interspersed with the processing of expressions and declarations, with the environment growing as imported bindings are added to it by each import declaration.
Vote yes to add the restriction, or no to leave it out.
This is a proposal to limit numbers in library names to the range 0 to
can assume as the maximum size of an integer.
Numbers are mostly used for SRFI-based libraries anyway, which are not likely to reach either limit.
The option uint15 for the proposal as stated (0 to 32767), int16 for -32768 to 32767, int24 for -223 to 223-1, etc.
Vote unspecified to make no explicit requirement on the integers allowed in library names.
Add the following text to the discussion of library loading:
Regardless of the number of times that a library is loaded, each program or library that imports bindings from a library will receive bindings from a single loading of that library, regardless of the number of import or cond-expand declarations in which it appears.
to make it clear that, for example,
(import (prefix (foo) 'foo:)) (import (only (foo) bar))will cause bar and foo:bar to come from the same instantiation of the library '(foo)'
Vote yes to add this requirement.
Add an export-all form to the library declaration that means "export all identifiers that are defined in the library with begin, include, and include-ci but none that are imported with import."
The proposed include-library-declarations allows a library to incorporate a file containing arbitrary library declarations, not just Scheme code (definitions and expressions). This allows, for example, the exports of a module to be written directly in the library file, and its imports in a separate file.
An alternative would be something like (export-from <library>) to export the same bindings as another library. This does require the clumsiness of actually defining the identifiers in the other library if it is abstract.
R7RS currently says:
Within a program, each imported library is loaded at least once, and, if imported by more than one program or library, may possibly be loaded additional times.
Richard Kelsey thinks this is too liberal, and proposes:
Regardless of the number of times that a library is loaded, each program or library that imports bindings from a library will receive bindings from a single loading of that library, regardless of the number of import or cond-expand forms in which it appears.
Aaron Hsu, however, thinks this is too restrictive, and proposes (backed up by actual R6RS implementations):
If a library's definitions are referenced in the expanded form of a program or library body, then that library must be loaded before the expanded program or library body is evaluated. This rule applies transitively.
Similarly, during the expansion of a library, if a syntax keyword imported from a library is needed to expand the library, then the imported library must be visited before the expansion of the importing library.
Coverage for this R6RS feature is currently sparse: only Gauche, Chez, Vicare, Larceny, Ypsilon, Mosh, IronScheme, KSi, RScheme, Rep support it. But it is convenient when working in bases other than e such as 10, 2, or 16, and it is just a few extra lines of code, since `(log z b) => (/ (log z) (log b))` for arbitrary complex numbers z, b.
Vote yes to add the optional second argument from R6RS.
Draft 6 says that it's an error for an argument of / (other than the first) to be an exact zero. R6RS, however, says that it's an error only if all the arguments are exact. In other words, (/ 2.0 0) is an error according to the draft, but in R6RS it returns +inf.0 (assuming the implementation supports it). The proposal is to adopt the R6RS wording.
Cowan tested (/ 2.0 0) in the usual set of Schemes:
Vote error for the current draft semantics that it is an error, all-error for the R6RS semantics that it is only an error if all arguments are exact, or unspecified to make this case unspecified.
R5RS requires that - and / accept one or two arguments, and labels support for more than two as "optional". R6RS requires such support. The proposal is to require it.
All Schemes in the test suite support more than two arguments except Scheme48/scsh. (Owl Lisp does not support variadic procedures of any kind.)
Vote require for required n-ary behavior and optional to leave it optional as in R5RS. Alternately, vote forbidden to make this always an error in all implementations.
R5RS and draft 6 of R7RS don't say what (log 0.0) and (log 0) return. R6RS requires -inf.0 and an exception respectively. The proposal is to say that (log 0.0) returns -inf.0 on systems that have -inf.0, and that (log 0) is an error.
In Racket, Gambit, Chicken (with the numbers egg), Guile, Chibi, Chez, Ikarus/Vicare, Larceny, Ypsilon, Mosh, IronScheme, STklos, Spark, (log 0.0) returns -inf.0 and (log 0) raises an exception.
Gauche, MIT, Chicken (without the numbers egg), Bigloo, Scheme48/scsh, Kawa, SISC, SCM, NexJ, KSi, RScheme, XLisp, Rep, VX, SXM, Inlab return -inf.0 in both cases.
Elk, UMB, Oaklisp raise an exception in both cases.
Scheme 7 returns the wrong answer in both cases.
SigScheme, Shoe, TinyScheme, Dream, BDC, Owl Lisp don't support log.
Scheme 9 apparently goes into an infinite loop in both cases.
Vote r6rs for the R6RS behavior of returning -inf.0 and raising an error, respectively. Vote infinity to always return -inf.0.
This proposal allows (/ 0 x), where x is an inexact number, to return an exact value. Currently only Racket, Gambit, TinyScheme, Sizzle, Spark do this; see Zero for details.
Vote zero to allow (but not require) this to return exact 0. Vote no-nan to allow it to return 0 except when x is +nan.0, where it would return +nan.0.
Currently R7RS says nothing about the value of (max +inf.0 +nan.0) or (min -inf.0 +nan.0). R6RS required these functions to return the infinite value, but this was adopted by some but not all R6RS implementations (see MaxInfNan for details). R5RS implementations are also divided.
The proposal is to allow R7RS implementations to provide either value.
Vote both to explicitly add a note that either are allowed, infinity to require the infinite value as in R6RS, nan to require returning +nan.0, and unspecified leave unspecified (i.e. the same as both but without the note).
Currently both infinite? and nan? return #t to a complex number like +inf.0+nan.0i. Is this the Right Thing, or should infinite? only return #t if neither part is a NaN?
Note it is reasonable for an implementation to not support partial nan complex numbers.
Vote disjoint to ensure that infinite? and nan? are disjoint predicates as in the proposal, or overlap to allow the current behavior.
Whitespace characters include the space and newline characters. (Implementations may provide additional whitespace characters such as tab or page break.)
However, 7.1.1 has:
<intraline whitespace> -> <space or tab> <whitespace> -> <intraline whitespace> | <newline> | <return>
So 2.2 implies that supporting tabs is allowed but not required, yet
Vote required to require support for tab as a whitespace character by read. char-whitespace? is required to return #t for it regardless.
Currently we don't specify what display does with circular lists. Should it generate labels like write, or loop like write-simple, or leave it unspecified?
The #!fold-case and #!no-fold-case directives are read as comments, which means that they are treated as whitespace (section
implicit. This means that (1#!no-fold-cases) reads as (1 s). This seems unfortunate.
There is concern that the output of write cannot be read by non-R7RS implementations. This is not a strict requirement, but is reasonable if using simple sexp-based file/interchange formats.
Specifically, even though there are no cycles in
(let ((x (list 2))) (write (list x x)))
it previously output "((2) (2))" but now outputs "(#0=(2) #0#)".
The WG concern is that R5RS write is unsafe, easily causing infinite loops, and should therefore not be the default. Thus we renamed this "write-simple", requiring programmers to know they are writing a "simple" data structure up front.
Arguably, there are three procedures desired:
although even for write-shared people sometimes want to treat containers such as strings separately.
Note the algorithms for detecting shared structure differ from those for detecting cycles, so providing both -shared and -cyclic imposes an additional implementation burden.
Currently, we allow implementations to provide their own names for characters, but provide no guidance for them. There are two plausible sources: the names in the Unicode Standard, and the [http://www.w3.org/TR/xml-entity-names/ entity names specified by W3C] for use in HTML, MathML, and other markup standards (ultimately derived from ISO SGML character entity sets).
The Unicode names are in all upper case and contain significant spaces and apostrophes as name characters, which would require some mapping to make valid Scheme identifiers. The W3C name list is incomplete though fairly large (currently 2237 names), covering mainly the Greek and Cyrillic scripts and non-ASCII punctuation and symbols. It distinguishes between iota (small) and Iota (capital).
Vote w3c for the W3C list, unicode to use the Unicode list with names mapped by converting to lowercase and replacing any non-identifier character (space and apostrophe) with hyphens. Vote unspecified to leave the character name extensions entirely up to the implementation.
With the acceptance of #278, we reduced the set of division operators to truncate-* and floor-* and move these into the base library. Three of these procedures are simply aliases for quotient, remainder and modulo, so it is worth considering removing the old names.
Since the old names are in IEEE Scheme we need strong justification for removing them from (scheme base), and even if we do so they will remain available in (scheme r5rs).
We have precedence for changing names, but only in the case when the existing names were both actively misleading and had already been changed in R6RS. Specifically, in ticket #328 we replaced the names inexact->exact and exact->inexact with the more accurate exact and inexact.
Arguably the new division operator names are clearer, but the old names are not actually misleading.
Vote yes to remove the old names from (scheme base), or no to leave them in as aliases.
This is compatible with Chicken, and "more Scheme-like, less Java-like". Okay, it's bikeshedding.
Under this proposal, the name would be bytevector-copy and the signature would be
(bytevector-copy bytevector [start [end]])
Vote yes for this simplification.
One proposal is port-last with a signature of:
(write-bytevector ''bytevector'' [''start'' [''end'' [''port'']]])
This has the disadvantage of being required to call bytevector-length when writing to a specific port.
Alternately we could do offsets-last:
(write-bytevector ''bytevector'' [''port'' [''start'' [''end'']]])
which has the disadvantage of separating the bytevector from its offsets.
Alternately, vote separate to keep these as two separate procedures.
This is a proposal to add optional start (inclusive) and end (exclusive) arguments to string->vector and vector->string. We now have start (inclusive) and end (exclusive) arguments for string->list and vector->list, but our non-R5RS and non-SRFI procedures to convert directly between strings and vectors don't provide these.
Vote yes to add these optional arguments.
R7RS requires an error to be signalled (which means an exception is raised as if by raise) in the following circumstances:
This proposal is to provide four standard predicates that identify these specific conditions, to be used in guard clauses or in with-exception handlers as a portable means of detecting these errors. Although these predicates may return #t on other objects, if one reports #t on an object, the others must report #f. Proposed names are file-error?, scheme-report-error?, read-error?, and expt-error? respectively.
Vote yes to add these procedures, or file-only to only add the file-error? predicate.
We should define the predicate record? so that it's possible to distinguish instances of record types from all other types. It should not be necessary to enumerate all record type predicates in order to determine whether an object is an instance of a record.
This is Alexey Radul's suggestion.
This was requested by Formal Comment #424.
These procedures would be provided for parallelism with the byte-vector I/O operations:
Byte |
Character |
Bytevector |
String |
read-u8 |
read-char |
read-bytevector(!) |
read-string(!) |
write-u8 |
write-char |
write-bytevector |
write-string |
If #385 passes, optional start (inclusive) and end (exclusive) index arguments would be added to write-string. Otherwise write-partial-string would be provided.
Vote yes to add all three, immutable to add only read-string and write-string, or no to leave them out.
Marc Feeley proposes it should be possible to convert from any container type to another, possibly via an intermediary such as
(list->B (A->list a))
proposing specifically "list" be the universally available intermediary, although "vector" would also be worth considering.
The container types are list, vector, string and bytevector. String and bytevector are second-class in that they are not general-purpose container types, and may raise errors converting from lists or vectors.
Vote list for the proposal to add the following procedures to complete the cycle:
Vote vector to add the equivalent procedures to allow converting between any of the types and vectors, specifically the following two new procedures:
The latin-1 proposal also adds the Latin-1-centric ideas of string to bytevector conversion, where each element of the bytevector is converted to/from a character with char->integer/integer->char.
The matrix proposal requires all 43=64 conversions.
This is for completeness with append and string-append, and is useful for the reconstruction phase of divide-and-conquer algorithms on vectors. See #436 for the Formal Comment that triggered this ticket.
This is for consistency with append, string-append, and vector-append (per ticket #444) procedures. It is useful for the reconstruction phase of divide-and-conquer algorithms on arbitrary byte sequences.
Replace port-open? with input-port-open? and output-port-open?, since a bidirectional port can be closed on one side without the other. See Formal Comment #439.
Vote replace to replace port-open? with just the two new versions, or add to have all three.
Marc Feeley writes:
It is a bad idea for the fill parameter of vector-copy to have a default. When fill is absent, it should be an error when start and end are not within the bounds of the sequence. Otherwise, some index calculation errors (off-by-one on end) may go unnoticed. Moreover, when it is supplied, fill should also be used when start is less than 0, for consistency with the case where end is greater to the length of the sequence.
Vote required to make the fill parameter required, error to make it an error in the case that fill is absent yet needed, or default for the current status quo.
Pass exception handlers a second, Boolean argument that declares whether the exception is continuable.
See Formal Comment #372 for the argument. Cowan writes: "I support this proposal. I don't support the alternative proposal to just say that any true value reports success and only #f reports failure, for there is usually only one kind of success (0 on Posix and Windows, "" on Plan 9, 2 on VMS) and many kinds of failure."
It is reasonable and convenient to use #t/#f as generic success/failure for portable programs, with (exit) as a shorthand for the "normal" completion (exit #t).
Another reasonable extension is fallback for certain success values that the implementation cannot understand. Specifically, 0 is commonly used for success on Posix systems, and the empty string "" as success on Plan9. We could require that if the implementation does not know how to pass these value types (string or number) to the OS, then it should recognize 0 and "" as true. Any value other than these which cannot be passed to the OS should be treated as a generic error. That way, a program written for Posix that alternatively uses (exit 0) and (exit <n>) will still work as desired on a Plan9 system, only losing details of the type of failure (and likewise for Plan9 programs running on Posix).
In either case, unless someone makes a proposal to the contrary, unknown values should always be treated as generic failure, and never raise an exception or fail to exit (from #374).
This procedure provides instant guaranteed process exit without running dynamic-wind thunks. This is a low-level and dangerous procedure.
Vote emergency-exit to add this procedure, or no to leave it out. If you want to write in an alternate name, be sure to include emergency-exit as a secondary option after it.
Cowan writes:
"I have reluctantly come to the same conclusion as the R6RS editors: that in a Scheme with libraries, scheme-report-environment and null-environment don't make much sense. They are not in IEEE Scheme or R4RS, so there is no formal barrier to removing them.
"Semantically, scheme-report-environment holds all the identifiers in R5RS, excepting any which the implementation doesn't provide, like make-rectangular if it does not have complex numbers. Null-environment, on the other hand, contains only the syntax keywords with none of the standard procedures: it is not an empty environment. R6RS preserves these procedures only in the R5RS compatibility library, where they expose only R5RS content.
"When adapting the definition to R7RS, I changed scheme-report-environment to contain all the identifiers in all the standard libraries that the implementation provides, and null-environment all the syntax keywords in those libraries. This was the best I thought I could do, but now I think that it provides very little utility.
"It's possible to construct any specific environment you want by using the environment procedure, which turns a sequence of import-specs into an environment. In particular, we now have the (scheme r5rs) library, which essentially provides what (scheme-environment-procedure 5) should provide, and there is no portable use of any argument other than 5."
Vote remove to remove these two procedures entirely, or move to move them from (scheme eval) and provide them only as portability options in (scheme r5rs), where only the argument 5 is required to be supported. Vote keep to leave them as-is.
The proposal is to require eval to accept definitions as well as expressions, as long as the specified environment is mutable. See EvalDefine for which Schemes already handle this.
The standard allows the following extension to force:
Some implementations may implement "implicit forcing," where the value of a promise is forced by primitive procedures like `cdr' and `+'
We should remove this note or tighten the definition.
A simple definition is any primitive that would require a type-check can perform implicit forcing. This would include all type predicates themselves except for promise?. Note if #405 passes, then in implementations which support this extension an object could return #t for promise? in addition to one other type.
Currently there is no way to inspect an object to see if it's a promise. This proposal makes promises first-class by adding a promise? predicate. It also requires that if the argument to make-promise is a promise, it is returned without rewrapping it, and that if force is given a non-promise argument, it returns it unchanged. (These things cannot be provided by the user without a promise? predicate, and are trivial to provide with it.)
Vote disjoint to add promise? and make it a disjoint type, or yes to add it as a not-necessarily disjoint predicate.
Our charter calls for one or more reference implementations. As of today, Chibi is very close to being so. The proposal is to bless it as a sample or model implementation, but not technically a reference implementation -- if it disagrees with the standard, the standard wins.