ERR5RS Records
William D Clinger
This SRFI is currently in ``draft'' status. To see an explanation of
each status that a SRFI can hold, see here.
To provide input on this SRFI, please
mail to
<srfi minus 99 at srfi dot schemers dot org>
. See
instructions here to subscribe to
the list. You can access previous messages via
the archive of the mailing list.
Many Scheme programmers have considered records to be one of the most important features missing from the R5RS. The R6RS proposed a record system, but its design has been widely criticized and it was not intended for use in R5RS programs anyway.
This SRFI proposes a better record system for use in R5RS, ERR5RS, and R6RS programs. The syntactic layer of this SRFI's record system is an extension of SRFI 9. The procedural and inspection layers of this SRFI's record system are perfectly compatible with its syntactic layer. This entire SRFI is compatible with the procedural and inspection layers of the R6RS record system, but offers several worthwhile improvements over the R6RS system.
This SRFI's proposed answers to the following questions are indicated within parentheses.
make-record-constructor-descriptor
procedure of the R6RS. Larceny, for example, allows that
procedure to be used with ERR5RS record type descriptors.)
make-rtd
procedure, the succinct
identifier-only field spec implies mutability.
The define-record-type
syntax provides
succinct abbreviations for both immutable and mutable
fields.)
In most programming languages, records (aka structures or classes) are important because they can package component values of different types into a single object.
Scheme's vectors and procedures provided that capability already, but records remained important for two reasons:
For many programmers, records were the most important new feature of the R6RS, but the specific record systems that were proposed by the R6RS have been widely criticized. Over 30% of those who voted against ratification mentioned the record systems as one of their reasons. [1]
The ERR5RS record system described by this SRFI provides a simpler and fully portable alternative to the R6RS record system. The ERR5RS record system consists of
The ERR5RS record system does not mandate support for the non-generative, sealed, and/or opaque features of the R6RS record system. Implementations of this SRFI may extend the ERR5RS record system to support those features, however, and this SRFI recommends an API to implementations that support those features. With those extensions, the ERR5RS record system has the same expressive power as the R6RS record system. Hence the record system described by this SRFI can serve as either or both of the following:
The following subsections develop the rationale for this SRFI by considering
The importance of adding records to Scheme has been
recognized for more than twenty years.
[2]
The basic idea behind the SRFI 9 and R6RS record systems
was outlined by Norman Adams on 8 July 1987, following
similar ideas that had been implemented in T and MIT CScheme.
[3]
Jonathan Rees posted a revision of Adams's proposal on
26 May 1988.
[4]
Pavel Curtis proposed an extension of Rees's proposal
on 18 August 1989, noting that it had been approved by
consensus at the first meeting of BASH (Bay Area Scheme
Hackers?).
[5]
The rrrs-authors
archive includes several responses to
these proposals that are worth reading.
The Rees/Curtis proposal was revived in 1992. [6] When the RnRS authors met on 25 June 1992 in Palo Alto, they felt that this proposal needed more discussion. [7] Kent Dybvig objected to the proposal on several grounds, including the provision of inspection facilities, the inability to define immutable records, and the use of procedures instead of special forms. Although 9 authors favored adoption of the records proposal, 11 opposed it.
The topic of records was revived again on 23 April 1996 by Bruce Duba, Matthew Flatt, and Shriram Krishnamurthi. [8] Alan Bawden and Richard Kelsey observed that the Duba/Flatt/Krishnamurthi proposal was essentially the same as Pavel Curtis's, which Kelsey reposted. Kent Dybvig objected once again, on the same three grounds. He also argued that procedural interfaces are difficult to compile efficiently, and that this inefficiency would create portability problems. [9]
In reality, however, procedural interfaces add no inefficiency.
It is now agreed that syntactic interfaces offer no
advantages for generative records.
[10]
Even for non-generative records, the claimed inefficiency
consists of a single load instruction, which optimizing
compilers can eliminate---along with the entire runtime check
that includes the load instruction---for all but the first of
a sequence of operations that access the same record. (That
optimization is a straightforward extension of the optimization
that eliminates the pair check when computing the cdr
of a list whose car
has already been computed.)
Furthermore, it turns out that
even the occasional load instruction is no harder to remove
using a procedural interface than when using a syntactic
interface.
[11]
In R6RS library chapter 6, therefore, both of the statements that
claim an advantage in efficiency for the syntactic layer have no
basis in fact. (These two statements appear in the next-to-last
paragraph before section 6.1, and in the note that follows
the specification of parent-rtd
.)
On 24 April 1996, Bill Rozas suggested the idea of having two separate APIs, one procedural and one syntactic, for the same record facility. [12] Two days later, Dybvig proposed a compromise along those lines [13] that incorporated several artificial restrictions, which were apparently motivated by concerns about the alleged extra load instruction. [14] Dybvig and Rozas continued to develop this proposal, and presented a summary of it following the 1998 Scheme Workshop. [15] I have been unable to locate a written or online copy of this proposal.
SRFI 9, submitted by Richard Kelsey in July 1999, is a syntactic API in the tradition of the Rees, Curtis, and Duba/Flatt/Krishnamurthi proposals. [16]
Single inheritance was added by Larceny in 1998, and by Chez Scheme in 1999. [17]
SRFI 57, submitted by Andre van Tonder in September 2004, features label polymorphism, which can be considered a form of structural subtyping and multiple inheritance. [18]
The R6RS proposes a three-layer single inheritance system, with syntactic, procedural, and inspection layers. [19]
The R6RS procedural layer generally requires at least three separate definitions for each level of inheritance: the record-type descriptor, at least one record-constructor descriptor, and an actual constructor (if instances of the record-type are to be created).
The (unratified) R6RS rationale [20] describes the constructor-descriptor mechanism as "an infrastructure for creating specialized constructors, rather than just creating default constructors that accept the initial values of all the fields as arguments. This infrastructure achieves full generality while leaving each level of an inheritance hierarchy in control over its own fields and allowing child record definitions to be abstracted away from the actual number and contents of parent fields." Neither the (ratified) R6RS library document nor the (unratified) R6RS rationale consider the fact that the constructor-descriptor mechanism adds unnecessary complexity to what is by far the most common case: record definitions that do not require specialized constructors. Neither document considers the fact that the benefits of the constructor-descriptor mechanism are small even when specialized constructors are needed, as in the first example below.
The R6RS library specification of records says that a record type is "specified by a record-type descriptor, which is an object that specifies the fields of the record and various other properties that all records of that type share." [21] Since the record-type descriptor is an object, it can be the value of a variable that is exported by a library. As discussed below, however, the R6RS syntactic layer uses a different notion of record type that libraries may not be able to export.
The R6RS syntactic layer consists of a
define-record-type
syntax
that is incompatible with the syntaxes of the same name
defined by SRFI 9 and SRFI 99 (this SRFI).
According to R6RS library section 6.2, an R6RS
define-record-type
form binds the record name
"to an expand-time or run-time representation of the record
type [that] can be used as parent name in syntactic
record-type definitions that extend this definition.
It can also be used as a handle to gain access to the
underlying record-type descriptor and constructor
descriptor".
Note that portable code cannot assume the record name is bound to a record-type descriptor. Portable code can only assume that the record name is bound to "an expand-time or run-time representation", whose semantics is not otherwise explained by the R6RS and R6RS library documents. In particular, portable code cannot assume that the record name can be exported by a library; libraries can export names that are bound to objects or to syntax, but the R6RS does not require the thing to which a record name is bound to be either of those things.
The mysterious entity to which a record name is bound can
be used as a handle to recover a record-type descriptor or
constructor descriptor by using the
record-type-descriptor
or
record-constructor-descriptor
syntaxes,
respectively. The recovered record-type descriptor and
constructor descriptor may be exported from a library,
and that is apparently the only portable way for a library
to export an R6RS record type that was defined using the
R6RS syntactic layer.
The recovered record-type descriptor and constructor
descriptor also provide a way for the procedural layer
to define new record types that inherit from record
types defined by the syntactic layer.
Similarly, it is possible for the syntactic layer to
use a parent-rtd
clause to define new
record types that inherit from record types defined
by the procedural layer.
The two notions of record type that are used by the procedural and syntactic layers are not interchangeable, however. In either direction, defining a new record type that inherits from some previously defined record type requires the programmer to know whether the previously defined record type was defined using the procedural or the syntactic layer. If the procedural and syntactic layers of the R6RS were fully compatible, then changing a record type definition from procedural to syntactic (or vice versa) would be transparent to clients. As the R6RS record facility is defined, however, that minor change will break all code that inherits from the record type.
R6RS library chapter 6 attempts to excuse that incompatibility, and the interoperability and maintenance problems that result from it, on the basis of efficiency. Recall, however, that the claimed efficiency of the R6RS syntactic layer is illusory. In reality, the R6RS design offers no advantages over a simpler and more orthogonal design (such as the one specified by this SRFI) in which the syntactic and procedural layers both use the same notion of record type.
The problems described above were known and had been documented before the R6RS documents were put to a vote, but the R6RS documents were ratified anyway. [22] At this point, the best that can be done is to use the SRFI process to specify a better record facility, and to warn programmers of the problems they will encounter if they use the record facilities described within the R6RS library document.
The ERR5RS syntactic layer described by this SRFI is based upon the Rees/Curtis/Duba/Flatt/Krishnamurthi/Kelsey/SRFI-9 tradition, changing only a few details to improve interoperability with records defined by the ERR5RS and R6RS procedural layers.
The define-record-type
syntax specified by
this SRFI is compatible with and extends SRFI 9, which
is one of the more widely accepted SRFIs.
The extensions include single inheritance and (optional)
implicit naming, along with succinct abbreviations for
specifying whether a field is immutable or mutable.
The procedural layer specified by this SRFI is fully
compatible with its define-record-type
syntax.
Both the procedural and syntactic layers can define new
record types that inherit from previously defined record
types without requiring programmers to know which layer
was used to define the parent type.
In implementations of the R6RS, a SRFI 99 record type coincides with the R6RS notion of a record-type descriptor. Portable libraries can safely export SRFI 99 record types even if they were defined using the syntactic layer of SRFI 99.
In procedure names, SRFI 99 uses rtd
as
an abbreviation for record-type descriptor. This naming
convention prevents name clashes between SRFI 99 and the
R6RS procedural and inspection layers, which makes it
easier for R6RS programs to import SRFI 99 libraries.
R6RS programs must take care when importing the R6RS
syntactic layer, however, because that library's exports
conflict with both SRFI 9 and with SRFI 99.
When implemented properly, SRFI 99 records will be just
as efficient as R6RS records. SRFI 99 is simpler than
R6RS records, both in specification and in implementation.
SRFI 99 is strictly less powerful than the R6RS records
facility because SRFI 99 does not require implementations
to provide sealed, opaque, or non-generative records.
On the other hand, SRFI 99 describes three optional
extensions (the sealed
, opaque
,
and uid
arguments to make-rtd
)
that would give SRFI 99 the same power as R6RS records.
With those three extensions, SRFI 99 becomes a simple
and efficient foundation for implementing R6RS records.
The record system described by this SRFI has been
implemented in Larceny. It is the primary record
system used by Larceny's implementation of the R6RS,
including the (rnrs records syntactic)
library. Larceny demonstrates both the efficiency
of ERR5RS records and the ease of interoperability
between SRFI 9, ERR5RS, and the procedural and
inspection layers of R6RS records.
All implementations of SRFI 99 must provide the following libraries:
(srfi :99) ; alias for (srfi :99 records) (srfi :99 records) ; composite of the next three (srfi :99 records procedural) (srfi :99 records inspection) (srfi :99 records syntactic)
Implementations of ERR5RS should provide the following aliases as well:
(err5rs records) ; alias for (srfi :99 records) (err5rs records procedural) ; alias for (srfi :99 records procedural) (err5rs records inspection) ; alias for (srfi :99 records inspection) (err5rs records syntactic) ; alias for (srfi :99 records syntactic)
The specification also describes how Scheme's standard equivalence predicates behave with respect to records, and shows how some R6RS examples can be translated to use the ERR5RS libraries instead.
When the following specification says that a procedure is said to be equivalent to some R6RS procedure, the equivalence holds only when all arguments have the properties required of them by the R6RS specification. Neither ERR5RS nor this SRFI mandate the R6RS exception semantics for programs that violate the specification.
The (srfi :99 records procedural)
library
exports the following procedures.
(make-rtd name fieldspecs)
(make-rtd name fieldspecs parent)
name is a symbol, which matters only to the
rtd-name
procedure of the inspection layer.
fieldspecs is a vector of field specifiers, where
each field specifier is one of
(mutable name)
,
where name is a symbol naming the mutable field;(immutable name)
,
where name is a symbol naming the immutable field.
The optional parent is an rtd or #f
.
It is an error for any of the symbols in fieldspecs to
name more than one of the fields specified by fieldspecs,
but the field names in fieldspecs may shadow field names
in the parent record-type.
Implementations may wish to extend this procedure to support the non-generative, sealed, and/or opaque features of the R6RS. The recommended way to support those features is to allow any combination of the following arguments to follow the optional parent argument:
sealed
means the new rtd cannot
be used as the parent of other rtds.opaque
means the record?
predicate will not recognize instances of the new rtd.uid
, following by another symbol
id, means the new rtd is non-generative with uid id.
The semantics of this extension is the same as described by the
R6RS.Returns an R6RS-compatible record-type descriptor. Could be defined (without the recommended error checking, and without the extensions described above) in terms of the R6RS procedural layer by
(define (make-rtd name fieldspecs . rest) (make-record-type-descriptor name (if (null? rest) #f (car rest)) #f #f #f (vector-map (lambda (fieldspec) (if (symbol? fieldspec) (list 'mutable fieldspec) fieldspec)) fieldspecs)))
(rtd? obj)
Equivalent to the record-type-descriptor?
procedure of the R6RS.
(rtd-constructor rtd)
(rtd-constructor rtd fieldspecs)
rtd is a record-type descriptor, and fieldspecs is an optional vector of symbols.
If no fieldspecs argument is supplied, then
rtd-constructor
returns a procedure
that expects one argument for each field of the
record-type described by rtd and returns an
instance of that record-type with its fields
initialized to the corresponding arguments.
Arguments that correspond to the fields of the
record-type's parent (if any) come first.
If fieldspecs is supplied, then
rtd-constructor
returns a procedure
that expects one argument for each element of
fieldspecs and returns an instance of the
record-type described by rtd with the named
fields initialized to the corresponding arguments.
It is an error if some symbol occurs more than once in fieldspecs. Fields of a derived record-type shadow fields of the same name in its parent; the fieldspecs argument cannot be used to initialize a shadowed field.
Note: The optional second argument was proposed by Pavel Curtis, and interoperates well with SRFI 9.
Could be defined in terms of the R6RS procedural layer and ERR5RS inspection layer by:
(define (rtd-constructor rtd . rest) ; Computes permutation and allocates permutation buffer ; when the constructor is created, not when the constructor ; is called. More error checking is recommended. (define (make-constructor fieldspecs allnames maker) (let* ((k (length fieldspecs)) (n (length allnames)) (buffer (make-vector n (unspecified))) (reverse-all-names (reverse allnames))) (define (position fieldname) (let ((names (memq fieldname reverse-all-names))) (assert names) (- (length names) 1))) (let ((indexes (map position fieldspecs))) ; The following can be made quite efficient by ; hand-coding it in some lower-level language, ; e.g. Larceny's mal. Even case-lambda would ; be good enough in most systems. (lambda args (assert (= (length args) k)) (for-each (lambda (arg posn) (vector-set! buffer posn arg)) args indexes) (apply maker (vector->list buffer)))))) (if (null? rest) (record-constructor (make-record-constructor-descriptor rtd #f #f)) (begin (assert (null? (cdr rest))) (make-constructor (vector->list (car rest)) (vector->list (rtd-all-field-names rtd)) (record-constructor (make-record-constructor-descriptor rtd #f #f))))))
(rtd-predicate rtd)
Equivalent to the record-predicate
procedure of the R6RS.
(rtd-accessor rtd field)
field is a symbol that names a field of the record-type described by the record-type descriptor rtd. Returns a unary procedure that accepts instances of rtd (or any record-type that inherits from rtd) and returns the current value of the named field.
Fields in derived record-types shadow fields of the same name in a parent record-type.
(rtd-mutator rtd field)
field is a symbol that names a field of the record-type described by the record-type descriptor rtd. Returns a binary procedure that accepts instances of rtd (or any record-type that inherits from rtd) and a new value to be stored into the named field, performs that side effect, and returns an unspecified value.
Fields in derived record-types shadow fields of the same name in a parent record-type.
The (srfi :99 records inspection)
library
exports the following procedures.
(record? obj)
Equivalent to its R6RS namesake.
(record-rtd record)
Equivalent to its R6RS namesake.
(rtd-name rtd)
Equivalent to the record-type-name
procedure of the R6RS.
(rtd-parent rtd)
Equivalent to the record-type-parent
procedure of the R6RS.
(rtd-field-names rtd)
Equivalent to the record-type-field-names
procedure of the R6RS. (That is, it returns
a vector of the symbols that name the fields of the
record-type represented by rtd, excluding
the fields of parent record-types.)
(rtd-all-field-names rtd)
Returns a vector of the symbols that name the fields
of the record-type represented by rtd, including
the fields of its parent record-types, if any.
The fields of parent record-types come before the
fields of its children, with each subsequence in the
same order as in the vectors that would be returned
by calling rtd-field-names
on rtd
and on all its ancestral record-type descriptors.
Could be defined by
(define (rtd-all-field-names rtd) (define (loop rtd othernames) (let ((parent (rtd-parent rtd)) (names (append (vector->list (rtd-field-names rtd)) othernames))) (if parent (loop parent names) (list->vector names)))) (loop rtd '()))
(rtd-field-mutable? rtd field)
rtd is a record-type descriptor, and field
is a symbol naming a field of the record-type
described by rtd. Returns #t
if the named field is mutable; otherwise returns
#f
.
The syntactic layer consists of SRFI 9 extended with single inheritance and (optional) implicit naming.
All ERR5RS record-type definitions are generative, but ERR5RS drops the SRFI 9 restriction to top level, mainly because the R6RS allows generative definitions wherever a definition may appear.
The (srfi :99 records syntactic)
library
exports the define-record-type
syntax specified below.
The syntax of an ERR5RS record-type definition is
<definition> -> <record type definition> ; addition to 7.1.6 in R5RS <record type definition> -> (define-record-type <type spec> <constructor spec> <predicate spec> <field spec> ...) <type spec> -> <type name> -> (<type name> <parent>) <constructor spec> -> #f -> #t -> <constructor name> -> (<constructor name> <field name> ...) <predicate spec> -> #f -> #t -> <predicate name> <field spec> -> <field name> -> (<field name>) -> (<field name> <accessor name>) -> (<field name> <accessor name> <mutator name>) <parent> -> <expression> <type name> -> <identifier> <constructor name> -> <identifier> <predicate name> -> <identifier> <accessor name> -> <identifier> <mutator name> -> <identifier> <field name> -> <identifier>
The semantics of a record type definition is the same as in SRFI 9: the record type definition macro-expands into a cluster of definitions that
<type name>
as the
record-type descriptor for the new record-type;
#f
);
#f
);
An ERR5RS record type definition extends SRFI 9 with the following additional options:
<parent>
expression is specified,
then it must evaluate to an rtd that serves as the parent
record-type for the record-type being defined.
#f
is specified for the constructor or
predicate, then no constructor or predicate procedure is defined.
(This is useful when the record-type being defined will be used
as an abstract base class.)
#t
is specified for the constructor or
predicate, then the name of the constructor is the type name
prefixed by make-
, and the name of the predicate
is the type name followed by a question mark (?
).
#t
or
as an identifier, then the constructor's arguments correspond
to the fields of the parent (if any) followed by the new fields
added by this record-type definition.
-
) followed by the field name.
-
) followed by the field name;
-
) followed by the field name
followed by -set!
.
Two ERR5RS records with fields are eqv?
if and only if they were created by the same (dynamic)
call to some record constructor. Two ERR5RS records
are eq?
if and only if they are
eqv?
.
Two ERR5RS records for which the record?
predicate returns true
are equal?
if and only if they are
eqv?
.
This SRFI does not specify the semantics of equal?
on opaque records, which are not required by this SRFI.
(The R6RS requires equal?
and eqv?
to behave the same on all records, unless the records happen
to be opaque records that represent pairs, vectors, strings,
or bytevectors.)
equal?
behave the same as eqv?
.)
A define-record-type
form macro-expands
into code that calls make-rtd
each time
the expanded record-type definition is executed.
Two ERR5RS record-type descriptors are eqv?
if and only if they were created by the same (dynamic)
call to make-rtd
.
R6RS library section 6.3 includes two extended examples that provide a nice comparison of the R6RS and ERR5RS record systems, especially since these two examples were designed to highlight the use of R6RS record-constructor descriptors in combination with inheritance.
Using ERR5RS records, the first example becomes:
(define rtd1 (make-rtd 'rtd1 '#((immutable x1) (immutable x2)))) (define rtd2 (make-rtd 'rtd2 '#((immutable x3) (immutable x4)) rtd1)) (define rtd3 (make-rtd 'rtd3 '#((immutable x5) (immutable x6)) rtd2)) (define protocol1 (lambda (p) (lambda (a b c) (p (+ a b) (+ b c))))) (define protocol2 (lambda (n) (lambda (a b c d e f) (let ((p (n a b c))) (p (+ d e) (+ e f)))))) (define protocol3 (lambda (n) (lambda (a b c d e f g h i) (let ((p (n a b c d e f))) (p (+ g h) (+ h i)))))) (define make-rtd1 (protocol1 (rtd-constructor rtd1))) (define make-rtd2 (let ((maker2 (rtd-constructor rtd2))) (protocol2 (protocol1 (lambda (x1 x2) (lambda (x3 x4) (maker2 x1 x2 x3 x4))))))) (define make-rtd3 (let ((maker3 (rtd-constructor rtd3))) (protocol3 (protocol2 (protocol1 (lambda (x1 x2) (lambda (x3 x4) (lambda (x5 x6) (maker3 x1 x2 x3 x4 x5 x6))))))))) (make-rtd3 1 2 3 4 5 6 7 8 9) ; evaluates to a record whose fields contain ; 3 5 9 11 15 17
The purpose of the R6RS record-constructor descriptors is to
automate the idiom shown in the definitions of
make-rtd1
, make-rtd2
,
and make-rtd3
above, and to provide an alternative
to procedural abstraction when eliminating the duplication of
code seen in make-point/abs
and
make-cpoint/abs
below.
The second example illustrates the shadowing of fields in a parent record-type by fields in a derived record-type. Using ERR5RS records, the second example becomes:
(define :point (make-rtd 'point '#((mutable x) (mutable y)))) (define make-point (rtd-constructor :point)) (define point? (rtd-predicate :point)) (define point-x (rtd-accessor :point 'x)) (define point-y (rtd-accessor :point 'y)) (define point-x-set! (rtd-mutator :point 'x)) (define point-y-set! (rtd-mutator :point 'y)) (define p1 (make-point 1 2)) (point? p1) => #t (point-x p1) => 1 (point-y p1) => 2 (point-x-set! p1 5) (point-x p1) => 5 (define :point2 (make-rtd 'point2 '#((mutable x) (mutable y)) :point)) (define make-point2 (rtd-constructor :point2)) (define point2? (rtd-predicate :point2)) (define point2-xx (rtd-accessor :point2 'x)) (define point2-yy (rtd-accessor :point2 'y)) (define p2 (make-point2 1 2 3 4)) (point? p2) => #t (point-x p2) => 1 (point-y p2) => 2 (point2-xx p2) => 3 (point2-yy p2) => 4 (define make-point/abs (let ((maker (rtd-constructor :point))) (lambda (x y) (maker (abs x) (abs y))))) (point-x (make-point/abs -1 -2)) => 1 (point-y (make-point/abs -1 -2)) => 2 (define :cpoint (make-rtd 'cpoint '#((mutable rgb)) :point)) (define make-cpoint (let ((maker (rtd-constructor :cpoint))) (lambda (x y c) (maker x y (color->rgb c))))) (define make-cpoint/abs (let ((maker (rtd-constructor :cpoint))) (lambda (x y c) (maker (abs x) (abs y) (color->rgb c))))) (define cpoint-rgb (rtd-accessor :cpoint 'rgb)) (define (color->rgb c) (cons 'rgb c)) (cpoint-rgb (make-cpoint -1 -3 'red)) => (rgb . red) (point-x (make-cpoint -1 -3 'red)) => -1 (point-x (make-cpoint/abs -1 -3 'red)) => 1
To simplify maintenance, the reference implementation is provided separately.
I am grateful to David Rush and Andre van Tonder for their comments and criticisms, as well as to all those mentioned by name in the Rationale. The reference implementation is adapted from Larceny v0.97.
Copyright (C) William D Clinger 2008. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. REMEMBER, THERE IS NO SCHEME UNDERGROUND. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.