Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Scheme (programming language)
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
==Implementation standards== This subsection documents design decisions that have been taken over the years which have given Scheme a particular character, but are not the direct outcomes of the original design. ===Numerical tower=== {{Main|Numerical tower}} Scheme specifies a comparatively full set of numerical datatypes including [[complex number|complex]] and [[rational number|rational]] types, which is known in Scheme as the numerical tower (R5RS sec. 6.2<ref name="r5rs"/>). The standard treats these as abstractions, and does not commit the implementor to any particular internal representations. Numbers may have the quality of exactness. An exact number can only be produced by a sequence of exact operations involving other exact numbers—inexactness is thus contagious. The standard specifies that any two implementations must produce equivalent results for all operations resulting in exact numbers. The R5RS standard specifies procedures <code>exact->inexact</code> and <code>inexact->exact</code> which can be used to change the exactness of a number. <code>inexact->exact</code> produces "the exact number that is numerically closest to the argument". <code>exact->inexact</code> produces "the inexact number that is numerically closest to the argument". The R6RS standard omits these procedures from the main report, but specifies them as R5RS compatibility procedures in the standard library (rnrs r5rs (6)). In the R5RS standard, Scheme implementations are not required to implement the whole numerical tower, but they must implement "a coherent subset consistent with both the purposes of the implementation and the spirit of the Scheme language" (R5RS sec. 6.2.3).<ref name="r5rs"/> The new R6RS standard does require implementation of the whole tower, and "exact integer objects and exact rational number objects of practically unlimited size and precision, and to implement certain procedures...so they always return exact results when given exact arguments" (R6RS sec. 3.4, sec. 11.7.1).<ref name="r6rs"/> Example 1: exact arithmetic in an implementation that supports exact rational complex numbers. <syntaxhighlight lang="Scheme"> ;; Sum of three rational real numbers and two rational complex numbers (define x (+ 1/3 1/4 -1/5 -1/3i 405/50+2/3i)) x ===> 509/60+1/3i ;; Check for exactness. (exact? x) ===> #t </syntaxhighlight> Example 2: Same arithmetic in an implementation that supports neither exact rational numbers nor complex numbers but does accept real numbers in rational notation. <syntaxhighlight lang="Scheme"> ;; Sum of four rational real numbers (define xr (+ 1/3 1/4 -1/5 405/50)) ;; Sum of two rational real numbers (define xi (+ -1/3 2/3)) xr ===> 8.48333333333333 xi ===> 0.333333333333333 ;; Check for exactness. (exact? xr) ===> #f (exact? xi) ===> #f </syntaxhighlight> Both implementations conform to the R5RS standard but the second does not conform to R6RS because it does not implement the full numerical tower. ===Delayed evaluation=== {{See also|Lazy evaluation}} Scheme supports delayed evaluation through the <code>delay</code> form and the procedure <code>force</code>. <syntaxhighlight lang="Scheme">(define a 10) (define eval-aplus2 (delay (+ a 2))) (set! a 20) (force eval-aplus2) ===> 22 (define eval-aplus50 (delay (+ a 50))) (let ((a 8)) (force eval-aplus50)) ===> 70 (set! a 100) (force eval-aplus2) ===> 22 </syntaxhighlight> The lexical context of the original definition of the promise is preserved, and its value is also preserved after the first use of <code>force</code>. The promise is only ever evaluated once. These primitives, which produce or handle values known as [[Futures and promises|promises]], can be used to implement advanced [[lazy evaluation]] constructs such as [[stream (computing)|stream]]s.<ref name="srfi-41">{{Cite web |last=Philip L. Bewig |date=2008-01-24 |title=SRFI 41: Streams |url=http://srfi.schemers.org/srfi-41/srfi-41.html |access-date=2012-08-09 |publisher=The SRFI Editors, schemers.org}}</ref> In the R6RS standard, these are no longer primitives, but instead, are provided as part of the R5RS compatibility library (rnrs r5rs (6)). In R5RS, a suggested implementation of <code>delay</code> and <code>force</code> is given, implementing the promise as a procedure with no arguments (a [[thunk]]) and using [[memoization]] to ensure that it is only ever evaluated once, irrespective of the number of times <code>force</code> is called (R5RS sec. 6.4).<ref name="r5rs"/> SRFI 41 enables the expression of both finite and infinite sequences with extraordinary economy. For example, this is a definition of the [[Fibonacci sequence]] using the functions defined in SRFI 41:<ref name="srfi-41"/> <syntaxhighlight lang="Scheme"> ;; Define the Fibonacci sequence: (define fibs (stream-cons 0 (stream-cons 1 (stream-map + fibs (stream-cdr fibs))))) ;; Compute the hundredth number in the sequence: (stream-ref fibs 99) ===> 218922995834555169026 </syntaxhighlight> ===Order of evaluation of procedure arguments=== Most Lisps specify an order of evaluation for procedure arguments. Scheme does not. Order of evaluation—including the order in which the expression in the operator position is evaluated—may be chosen by an implementation on a call-by-call basis, and the only constraint is that "the effect of any concurrent evaluation of the operator and operand expressions is constrained to be consistent with some sequential order of evaluation." (R5RS sec. 4.1.3)<ref name="r5rs"/> <syntaxhighlight lang="Scheme"> (let ((ev (lambda(n) (display "Evaluating ") (display (if (procedure? n) "procedure" n)) (newline) n))) ((ev +) (ev 1) (ev 2))) ===> 3 </syntaxhighlight> <syntaxhighlight lang="output"> Evaluating 1 Evaluating 2 Evaluating procedure </syntaxhighlight> ev is a procedure that describes the argument passed to it, then returns the value of the argument. In contrast with other Lisps, the appearance of an expression in the operator position (the first item) of a Scheme expression is quite legal, as long as the result of the expression in the operator position is a procedure. In calling the procedure "{{mono|+}}" to add 1 and 2, the expressions {{mono|(ev +), (ev 1)}} and {{mono|(ev 2)}} may be evaluated in any order, as long as the effect is not as if they were evaluated in parallel. Thus the following three lines may be displayed in any order by standard Scheme when the above example code is executed, although the text of one line may not be interleaved with another because that would violate the sequential evaluation constraint. ===Hygienic macros=== {{Main|Hygienic macro}} In the R5RS standard and also in later reports, the syntax of Scheme can easily be extended via the macro system. The R5RS standard introduced a powerful hygienic macro system that allows the programmer to add new syntactic constructs to the language using a simple [[pattern matching]] sublanguage (R5RS sec 4.3).<ref name="r5rs"/> Prior to this, the hygienic macro system had been relegated to an appendix of the R4RS standard, as a "high level" system alongside a "low level" macro system, both of which were treated as extensions to Scheme rather than an essential part of the language.<ref name="r4rs">{{Cite journal |year=1991 |title=Revised<sup>4</sup> Report on the Algorithmic Language Scheme |url=http://www.cs.indiana.edu/scheme-repository/R4RS/r4rs_toc.html |journal=ACM Lisp Pointers |volume=4 |issue=3 |pages=1–55 |access-date=2012-08-09 |editor=William Clinger and Jonathan Rees}}</ref> Implementations of the hygienic macro system, also called <code>syntax-rules</code>, are required to respect the lexical scoping of the rest of the language. This is assured by special naming and scoping rules for macro expansion and avoids common programming errors that can occur in the macro systems of other programming languages. R6RS specifies a more sophisticated transformation system, <code>syntax-case</code>, which has been available as a language extension to R5RS Scheme for some time. <syntaxhighlight lang="Scheme"> ;; Define a macro to implement a variant of "if" with a multi-expression ;; true branch and no false branch. (define-syntax when (syntax-rules () ((when pred exp exps ...) (if pred (begin exp exps ...))))) </syntaxhighlight> Invocations of macros and procedures bear a close resemblance—both are s-expressions—but they are treated differently. When the compiler encounters an s-expression in the program, it first checks to see if the symbol is defined as a syntactic keyword within the current lexical scope. If so, it then attempts to expand the macro, treating the items in the tail of the s-expression as arguments without compiling code to evaluate them, and this process is repeated recursively until no macro invocations remain. If it is not a syntactic keyword, the compiler compiles code to evaluate the arguments in the tail of the s-expression and then to evaluate the variable represented by the symbol at the head of the s-expression and call it as a procedure with the evaluated tail expressions passed as arguments to it. Most Scheme implementations also provide additional macro systems. Among popular ones are [[Hygienic macro#syntactic closures|syntactic closures]], [[Hygienic macro#explicit renaming|explicit renaming macros]] and <code>define-macro</code>, a non-hygienic macro system similar to <code>defmacro</code> system provided in [[Common Lisp]]. The inability to specify whether or not a macro is hygienic is one of the shortcomings of the macro system. Alternative models for expansion such as scope sets provide a potential solution.<ref>{{Cite book |last=Flatt |first=Matthew |title=Proceedings of the 43rd Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages |year=2016 |isbn=978-1-4503-3549-2 |pages=705–717 |chapter=Binding as sets of scopes |doi=10.1145/2837614.2837620 |s2cid=15401805}}</ref> ===Environments and eval=== Prior to R5RS, Scheme had no standard equivalent of the <code>eval</code> procedure which is ubiquitous in other Lisps, although the first Lambda Paper had described <code>evaluate</code> as "similar to the LISP function EVAL"<ref name="lambda_paper_1"/> and the first Revised Report in 1978 replaced this with <code>enclose</code>, which took two arguments. The second, third and fourth revised reports omitted any equivalent of <code>eval</code>. The reason for this confusion is that in Scheme with its lexical scoping the result of evaluating an expression depends on where it is evaluated. For instance, it is not clear whether the result of evaluating the following expression should be 5 or 6:<ref name="rees_1992">Jonathan Rees, [http://mumble.net/~jar/pubs/scheme-of-things/june-92-meeting.ps The Scheme of Things The June 1992 Meeting] {{Webarchive|url=https://web.archive.org/web/20110716071317/http://mumble.net/~jar/pubs/scheme-of-things/june-92-meeting.ps |date=2011-07-16}} (postscript), in Lisp Pointers, V(4), October–December 1992. Retrieved 2012-08-09</ref> <syntaxhighlight lang="Scheme"> (let ((name '+)) (let ((+ *)) (evaluate (list name 2 3)))) </syntaxhighlight> If it is evaluated in the outer environment, where <code>name</code> is defined, the result is the sum of the operands. If it is evaluated in the inner environment, where the symbol "+" has been bound to the value of the procedure "*", the result is the product of the two operands. R5RS resolves this confusion by specifying three procedures that return environments and providing a procedure <code>eval</code> that takes an s-expression and an environment and evaluates the expression in the environment provided. (R5RS sec. 6.5)<ref name="r5rs"/> R6RS extends this by providing a procedure called <code>environment</code> by which the programmer can specify exactly which objects to import into the evaluation environment. With modern scheme (usually compatible with R5RS) to evaluate this expression, one needs to define a function <code>evaluate</code> which can look like this: <syntaxhighlight lang="Scheme"> (define (evaluate expr) (eval expr (interaction-environment))) </syntaxhighlight> <code>interaction-environment</code> is the interpreter's global environment. ===Treatment of non-Boolean values in Boolean expressions=== In most dialects of Lisp including Common Lisp, by convention the value <code>NIL</code> evaluates to the value false in a Boolean expression. In Scheme, since the IEEE standard in 1991,<ref name="ieee1178"/> all values except <code>#f</code>, including <code>NIL</code>'s equivalent in Scheme which is written as <code>'()</code>, evaluate to the value true in a Boolean expression. (R5RS sec. 6.3.1)<ref name="r5rs"/> Where the constant representing the Boolean value of true is <code>T</code> in most Lisps, in Scheme it is <code>#t</code>. ===Disjointness of primitive datatypes=== In Scheme the primitive datatypes are disjoint. Only one of the following predicates can be true of any Scheme object: <code>boolean?</code>, <code>pair?</code>, <code>symbol?</code>, <code>number?</code>, <code>char?</code>, <code>string?</code>, <code>vector?</code>, <code>port?</code>, <code>procedure?</code>. (R5RS sec 3.2)<ref name="r5rs"/> Within the numerical datatype, by contrast, the numerical values overlap. For example, an integer value satisfies all of the <code>integer?</code>, <code>rational?</code>, <code>real?</code>, <code>complex?</code> and <code>number?</code> predicates at the same time. (R5RS sec 6.2)<ref name="r5rs"/> ===Equivalence predicates=== {{See also|relational operator}} Scheme has three different types of equivalence between arbitrary objects denoted by three different ''equivalence predicates'', relational operators for testing equality, <code>eq?</code>, <code>eqv?</code> and <code>equal?</code>: * <code>eq?</code> evaluates to <code>#f</code> unless its parameters represent the same data object in memory; * <code>eqv?</code> is generally the same as <code>eq?</code> but treats primitive objects (e.g. characters and numbers) specially so that numbers that represent the same value are <code>eqv?</code> even if they do not refer to the same object; * <code>equal?</code> compares data structures such as lists, vectors and strings to determine if they have congruent structure and <code>eqv?</code> contents.(R5RS sec. 6.1)<ref name="r5rs"/> Type dependent equivalence operations also exist in Scheme: <code>string=?</code> and <code>string-ci=?</code> compare two strings (the latter performs a case-independent comparison); <code>char=?</code> and <code>char-ci=?</code> compare characters; <code>=</code> compares numbers.<ref name="r5rs"/> ===Comments=== {{See also|Comment (computer programming)}} Up to the R5RS standard, the standard comment in Scheme was a semicolon, which makes the rest of the line invisible to Scheme. Numerous implementations have supported alternative conventions permitting comments to extend for more than a single line, and the R6RS standard permits two of them: an entire [[s-expression]] may be turned into a comment (or "commented out") by preceding it with <code><nowiki>#;</nowiki></code> (introduced in SRFI 62<ref>{{Cite web |last=Taylor Campbell |date=2005-07-21 |title=SRFI 62: S-expression comments |url=http://srfi.schemers.org/srfi-62/srfi-62.html |access-date=2012-08-09 |publisher=The SRFI Editors, schemers.org}}</ref>) and a multiline comment or "block comment" may be produced by surrounding text with <code><nowiki>#</nowiki>|</code> and <code>|#</code>. ===Input/output=== Scheme's input and output is based on the ''port'' datatype. (R5RS sec 6.6)<ref name="r5rs"/> R5RS defines two default ports, accessible with the procedures <code>current-input-port</code> and <code>current-output-port</code>, which correspond to the Unix notions of [[Standard streams|standard input and standard output]]. Most implementations also provide <code>current-error-port</code>. [[Redirection (computing)|Redirection]] of input and standard output is supported in the standard, by standard procedures such as <code>with-input-from-file</code> and <code>with-output-to-file</code>. Most implementations provide string ports with similar redirection capabilities, enabling many normal input-output operations to be performed on string buffers instead of files, using procedures described in SRFI 6.<ref name="srfi-6">{{Cite web |last=William D Clinger |date=1999-07-01 |title=SRFI 6: Basic String Ports |url=http://srfi.schemers.org/srfi-6/srfi-6.html |access-date=2012-08-09 |publisher=The SRFI Editors, schemers.org}}</ref> The R6RS standard specifies much more sophisticated and capable port procedures and many new types of port. The following examples are written in strict R5RS Scheme. Example 1: With output defaulting to (current-output-port): <syntaxhighlight lang="Scheme"> (let ((hello0 (lambda() (display "Hello world") (newline)))) (hello0)) </syntaxhighlight> Example 2: As 1, but using optional port argument to output procedures <syntaxhighlight lang="Scheme"> (let ((hello1 (lambda (p) (display "Hello world" p) (newline p)))) (hello1 (current-output-port))) </syntaxhighlight> Example 3: As 1, but output is redirected to a newly created file <syntaxhighlight lang="Scheme"> ;; NB: with-output-to-file is an optional procedure in R5RS (let ((hello0 (lambda () (display "Hello world") (newline)))) (with-output-to-file "helloworldoutputfile" hello0)) </syntaxhighlight> Example 4: As 2, but with explicit file open and port close to send output to file <syntaxhighlight lang="Scheme"> (let ((hello1 (lambda (p) (display "Hello world" p) (newline p))) (output-port (open-output-file "helloworldoutputfile"))) (hello1 output-port) (close-output-port output-port)) </syntaxhighlight> Example 5: As 2, but with using call-with-output-file to send output to a file. <syntaxhighlight lang="Scheme"> (let ((hello1 (lambda (p) (display "Hello world" p) (newline p)))) (call-with-output-file "helloworldoutputfile" hello1)) </syntaxhighlight> Similar procedures are provided for input. R5RS Scheme provides the predicates <code>input-port?</code> and <code>output-port?</code>. For character input and output, <code>write-char</code>, <code>read-char</code>, <code>peek-char</code> and <code>char-ready?</code> are provided. For writing and reading Scheme expressions, Scheme provides <code>read</code> and <code>write</code>. On a read operation, the result returned is the end-of-file object if the input port has reached the end of the file, and this can be tested using the predicate <code>eof-object?</code>. With the standard, SRFI 28 also defines a basic formatting procedure resembling Common Lisp's <code>format</code> function, after which it is named.<ref name="srfi-28">{{Cite web |last=Scott G. Miller |date=2002-06-25 |title=SRFI 28: Basic Format Strings |url=http://srfi.schemers.org/srfi-28/srfi-28.html |publisher=The SRFI Editors, schemers.org |access-date=2012-08-09}}</ref> ===Redefinition of standard procedures=== In Scheme, procedures are bound to variables. At R5RS the language standard formally mandated that programs may change the variable bindings of built-in procedures, effectively redefining them. (R5RS "Language changes")<ref name="r5rs"/> For example, <code>+</code> can be extended to accept strings as well as numbers by redefining it: <syntaxhighlight lang="Scheme"> (set! + (let ((original+ +)) (lambda args (apply (if (or (null? args) (string? (car args))) string-append original+) args)))) (+ 1 2 3) ===> 6 (+ "1" "2" "3") ===> "123" </syntaxhighlight> In R6RS every binding, including the standard ones, belongs to some library, and all exported bindings are immutable. (R6RS sec 7.1)<ref name="r6rs"/> Because of this, redefinition of standard procedures by mutation is forbidden. Instead, it is possible to import a different procedure under the name of a standard one, which in effect is similar to redefinition. ===Nomenclature and naming conventions=== In Standard Scheme, procedures that convert from one datatype to another contain the character string "->" in their name, predicates end with a "?", and procedures that change the value of already-allocated data end with a "!". These conventions are often followed by Scheme programmers. In formal contexts such as Scheme standards, the word "procedure" is used in preference to "function" to refer to a lambda expression or primitive procedure. In normal usage, the words "procedure" and "function" are used interchangeably. Procedure application is sometimes referred to formally as ''combination''. As in other Lisps, the term "[[thunk]]" is used in Scheme to refer to a procedure with no arguments. The term "proper tail recursion" refers to the property of all Scheme implementations, that they perform tail-call optimization so as to support an indefinite number of active [[tail call]]s. The form of the titles of the standards documents since R3RS, "Revised<sup>n</sup> Report on the Algorithmic Language Scheme", is a reference to the title of the [[ALGOL|ALGOL 60]] standard document, "Revised Report on the Algorithmic Language Algol 60," The Summary page of R3RS is closely modeled on the Summary page of the ALGOL 60 Report.<!-- --><ref name="algol_report">{{Cite journal |last1=J.W. Backus |last2=F.L. Bauer |last3=J.Green |last4=C. Katz |last5=J. McCarthy P. Naur |display-authors=etal |date=January–April 1960 |title=Revised Report on the Algorithmic Language Algol 60 |url=http://www.masswerk.at/algol60/report.htm |journal=Numerische Mathematik, Communications of the ACM, and Journal of the British Computer Society |access-date=2012-08-09}}</ref><ref name="r3rs">{{Cite journal |date=December 1986 |editor2-last=William Clinger |title=Revised(3) Report on the Algorithmic Language Scheme (Dedicated to the Memory of ALGOL 60) |url=http://groups.csail.mit.edu/mac/ftpdir/scheme-reports/r3rs-html/r3rs_toc.html |journal=ACM SIGPLAN Notices |volume=21 |issue=12 |pages=37–79 |citeseerx=10.1.1.29.3015 |doi=10.1145/15042.15043 |access-date=2012-08-09 |editor=Jonathan Rees |hdl=1721.1/6424 |s2cid=43884422}}</ref>
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)