Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
history
You can edit this page using
wiki syntax
for markup.
Article contents:
== SRFI-189: Maybe and Either This SRFI defines two disjoint immutable container types known as Maybe and Either, both of which can contain objects collectively known as their payload. A Maybe object is either a Just object or the unique object Nothing (which has no payload); an Either object is either a Right object or a Left object. Maybe represents the concept of optional values; Either represents the concept of values which are either correct (Right) or errors (Left). Note that the terms Maybe, Just, Nothing, Either, Right, and Left are capitalized in this SRFI so as not to be confused with their ordinary use as English words. Thus "returns Nothing" means "returns the unique Nothing object"; "returns nothing" could be interpreted as "returns no values" or "returns an unspecified value". [[toc:]] == SRFI Description This page includes excerpts from the SRFI document, but is primarily intended to document the forms exported by this egg. For a full description of the SRFI, see the full [[https://srfi.schemers.org/srfi-189/srfi-189.html|SRFI document]]. == Authors John Cowan (text) and Wolfgang Corcoran-Mathe (sample implementation) == Specification The four types Just, Nothing, Left, and Right are disjoint from each other and from all other Scheme types. We speak of unwrapping a container when we extract its payload, and wrapping values in a container when we create the container with the values as its payload. The following names are used for the arguments: <table> <tr><td>''obj'', ''default''</td><td>Any Scheme object.</td></tr> </td></tr> <tr><td>''maybe''</td><td>A Maybe object.</td></tr> </td></tr> <tr><td>''either''</td><td>An Either object.</td></tr> </td></tr> <tr><td>''failure''</td><td>A procedure that accepts zero arguments (unless specified otherwise).</td></tr> </td></tr> <tr><td>''success''</td><td>A procedure that accepts one or more arguments.</td></tr> </td></tr> <tr><td>''pred''</td><td>A predicate that accepts zero or more arguments.</td></tr> </td></tr> <tr><td>''equal''</td><td>A predicate that accepts two arguments and returns a boolean value. It is not necessarily an equivalence relation.</td></tr> </td></tr> <tr><td>''proc''</td><td>A procedure that accepts zero or more arguments and returns zero or more values.</td></tr> </td></tr> <tr><td>''mproc''</td><td>A procedure that accepts zero or more arguments and returns zero or more values wrapped in a single Maybe or Either container.</td></tr> </td></tr> <tr><td>''list''</td><td>A Scheme list.</td></tr> </td></tr> <tr><td>''producer''</td><td>A procedure that accepts no arguments and returns some number of values.</td></tr> </table> If the procedures are passed arguments that do not have the type implied by the argument names, then a condition of kind {{(exn type assertion)}} is raised. == Maybe and Either === Constructors <procedure>(just obj ...)</procedure> Monadic pure. Returns the ''objs'' wrapped in a Just. <procedure>(nothing)</procedure> Returns the unique Nothing object. <procedure>(right obj ...)</procedure> Monadic pure. Returns the ''objs'' wrapped in a Right. <procedure>(left obj ...)</procedure> Returns the ''objs'' wrapped in a Left. <procedure>(list->just list)</procedure> <procedure>(list->left list)</procedure> <procedure>(list->right list)</procedure> Returns a Just / Left / Right whose values are the elements of ''list''. Note that these procedures are not monadic; they are equivalent to just/left/right but accept a list rather than multiple arguments. There is no need for {{list->nothing}} distinct from {{nothing}}. <procedure>(maybe->either maybe obj ...)</procedure> If ''maybe'' is a Just, returns a Right with the same payload objects in the sense of {{eqv?}}; otherwise returns a Left of ''objs''. <procedure>(either->maybe either)</procedure> If ''either'' is a Right, returns a Just with the same payload objects in the sense of {{eqv?}}; otherwise returns Nothing. <procedure>(either-swap either)</procedure> If ''either'' is a Left, return a Right with the same payload objects (in the sense of {{eqv?}}), and vice versa. === Predicates <procedure>(just? obj)</procedure> <procedure>(nothing? obj)</procedure> <procedure>(right? obj)</procedure> <procedure>(left? obj)</procedure> Returns {{#t}} if ''obj'' is a Just, Nothing, Left, or Right respectively, and {{#f}} otherwise. <procedure>(maybe? obj)</procedure> Returns {{#t}} if ''obj'' is a Maybe (that is, either a Just or Nothing) and {{#f}} otherwise. <procedure>(either? obj)</procedure> Returns {{#t}} if ''obj'' is an Either (that is, either a Right or a Left) and {{#f}} otherwise. <procedure>(maybe= equal maybe1 maybe2 ...)</procedure> Returns {{#t}} if all ''maybes'' are Nothing, or if they all are Justs and their respective payload objects are the same in the sense of ''equal'', and {{#f}} otherwise. <procedure>(either= equal either1 either2 ...)</procedure> Returns {{#t}} if all ''eithers'' are all Lefts or all Rights and their respective payload objects are the same in the sense of ''equal'', and {{#f}} otherwise. === Accessors <procedure>(maybe-ref maybe failure [success])</procedure> If ''maybe'' is a Just, tail-calls the procedure ''success'' on the values of its payload and returns the result. Otherwise, it tail-calls the procedure ''failure'' on no arguments and returns the result. The default value of ''success'' is {{values}}. <procedure>(either-ref either failure [success])</procedure> If ''either'' is a Right, tail-calls the procedure ''success'' on the values of its payload and returns the result. Otherwise, it tail-calls the procedure ''failure'' on the values of its payload and returns the result. The default value of ''success'' is {{values}}. <procedure>(maybe-ref/default maybe default ...)</procedure> <procedure>(either-ref/default either default ...)</procedure> If ''maybe''/''either'' is a Just/Right, returns the values of its payload; otherwise returns the ''defaults'' as multiple values. === Join and bind <procedure>(maybe-join maybe)</procedure> <procedure>(either-join either)</procedure> Monadic join. If ''maybe''/''either'' is a Just/Right whose single payload object is a Maybe/Either, returns that payload; if it is a Nothing/Left, returns ''maybe''/''either''. In all other cases (including a Just/Right with multiple values) it is an error; specifically, a condition of kind {{(exn type payload)}} is raised. Thus {{(maybe-join (just (just x))}} returns Just {{x}} and {{(maybe-join (just (nothing))}} returns Nothing, and similarly for {{either-join}}. <procedure>(maybe-compose mproc1 mproc2 ...)</procedure> <procedure>(either-compose mproc1 mproc2 ...)</procedure> Returns an mproc that accepts zero or more arguments and applies the following iterative algorithm to each ''mproc'': The ''mproc'' is applied to the arguments and returns a Maybe/Either interim result. If the result is Nothing / a Left, it is returned as the final result. If it is a Just/Right, the next ''mproc'' is applied to the values of its payload, returning the next interim result. When no more ''mprocs'' are available, the interim result is returned as the final result. It is an error if one of the ''mprocs'' does not accept as arguments the number of values wrapped in the Just/Right produced by its predecessor. <procedure>(maybe-bind maybe mproc1 mproc2 ...)</procedure> <procedure>(either-bind either mproc1 mproc2 ...)</procedure> Monadic bind. If ''maybe/either'' is a Just/Right, it behaves as if {{maybe-compose}}/{{either-compose}} were applied to ''mprocs'', and the resulting mproc applied to the payload of ''maybe''/''either''. But if ''maybe''/''either'' is a Nothing/Left, it is immediately returned. === Sequence operations These procedures treat Maybes (and in some cases Eithers) as sequences of length 0 or 1. This length depends only on the type of the container and not on the number of objects it contains. <procedure>(maybe-length maybe)</procedure> <procedure>(either-length either)</procedure> Return 1 if ''maybe''/''either'' is a Just/Right, and 0 otherwise. <procedure>(maybe-filter pred maybe)</procedure> <procedure>(maybe-remove pred maybe)</procedure> <procedure>(either-filter pred either obj ...)</procedure> <procedure>(either-remove pred either obj ...)</procedure> If ''maybe''/''either'' is a Just/Right and the values of its payload satisfy / do not satisfy ''pred'', then return ''maybe''; otherwise, returns Nothing / a Left of ''objs''. <procedure>(maybe-sequence mappable map [aggregator])</procedure> <procedure>(either-sequence mappable map [aggregator])</procedure> The purpose of these procedures is to transform a collection of Maybes/Eithers of some particular type into a Maybe/Either of a collection, often of the same type. The ''mappable'' argument is the collection to be transformed, and the ''map'' argument is the appropriate procedure that can transform it, one element at a time. The signature of ''map'' is {{(map proc mappable)}}, where ''proc'' is supplied by the implementation. Typical mappables are lists and vectors, and {{map}} and {{vector-map}} are useful map functions, though not the only possible ones. Each element is processed as follows: If it is a Just/Right, its values are unwrapped and the ''aggregator'' function (which defaults to {{list}}) is applied to them. But if it is a Left/Nothing, that object is immediately returned as the value of {{maybe-sequence}}/{{either-sequence}}. Assuming there are no Lefts/Nothings, the ''map'' function is then responsible for constructing the new mappable out of the results of the calls on ''aggregator''. For example, a list of Justs becomes a Just list of lists if the map procedure is {{map}} and the aggregator is {{list}}. In the same way, a vector of Rights containing one value each becomes a Right vector if the map procedure is {{vector-map}} and the aggregator is {{(lambda (x) x)}}. === Protocol Conversion The purpose of Maybe and Either is to provide first-class objects representing the distinction between success and failure in such a way that any number of success values (and, in the case of Either, any number of failure values) can be packaged up in a single object without reserving any values to indicate success or failure. A variety of ''protocols'' are already in use in Scheme and other Lisps to more or less perfectly represent the distinction. Some reserve a value to represent failure which cannot be returned as a success value; some handle only one success value; some are not first-class. In order to facilitate communication between code using Maybe/Either and code using another protocol, the following procedures convert between Maybe/Either objects and some of the most usual protocols. Conversion in either direction is frequently lossy. ☞ The following procedures interface between the Maybe protocol and the ''list'' protocol, which uses an empty list to represent failure and a non-empty list to represent success. <procedure>(maybe->list maybe)</procedure> <procedure>(either->list either)</procedure> Returns a list whose elements are the payload of ''maybe''/''either''; if ''maybe''/''either'' is Nothing/Left, returns the empty list. It is an error to mutate the result of this procedure, though this implementation can't enforce this requirement. <procedure>(list->maybe list)</procedure> <procedure>(list->either list obj ...)</procedure> If ''list'' is empty, returns Nothing / wraps ''objs'' in a Left and returns it; otherwise, wraps the list elements in a Just/Right and returns that. ☞ The following procedures interface between the Maybe and Either protocols and the ''truth'' protocol, which uses {{#f}} to represent failure and a single true value to represent success. <procedure>(maybe->truth maybe)</procedure> <procedure>(either->truth either)</procedure> If ''maybe''/''either'' is a Just/Right, returns its payload; otherwise returns {{#f}}. If the Just/Right does not have exactly one value, a condition of kind {{(exn type payload)}} is raised. <procedure>(truth->maybe obj)</procedure> <procedure>(truth->either obj fail-obj ...)</procedure> If ''obj'' is {{#f}}, return Nothing / a Left of ''fail-objs''; otherwise, return a Just/Right whose payload is ''obj''. ☞ The following procedures interface between the Maybe protocol and the ''list-truth'' protocol, which uses {{#f}} to represent failure and a list to represent success. <procedure>(maybe->list-truth maybe)</procedure> <procedure>(either->list-truth either)</procedure> If ''maybe''/''either'' is a Just/Right, returns a list whose elements are the payload; otherwise, returns {{#f}}. It is an error to mutate the result of this procedure, though this implementation can't enforce this requirement. <procedure>(list-truth->maybe list-or-false)</procedure> <procedure>(list-truth->either list-or-false obj ...)</procedure> If ''list-or-false'' is {{#f}}, returns Nothing / a Left of ''objs''; otherwise, wraps the list elements in a Just/Right and returns it. ☞ The following procedures interface between the Maybe and Either protocols and the ''generation'' protocol, which uses an end-of-file object to represent failure and any other value to represent success. <procedure>(maybe->generation maybe)</procedure> <procedure>(either->generation either)</procedure> If ''maybe''/''either'' is a Just/Right, then its payload is returned. If the Just/Right does not have exactly one value, then a condition of kind {{(exn type payload)}} is raised. If ''maybe''/''either'' is Nothing / a Left, then an end-of-file object is returned. <procedure>(generation->maybe obj)</procedure> <procedure>(generation->either obj fail-objs ...)</procedure> If ''obj'' is an end-of-file object, return Nothing / a Left of ''fail-objs''. Otherwise, return ''obj'' wrapped in a Just/Right. ☞ The following procedures interface between the Maybe and Either protocols and the ''values'' protocol, which returns one or more values to represent success and zero values to represent failure. <procedure>(maybe->values maybe)</procedure> <procedure>(either->values either)</procedure> If ''maybe''/''either'' is a Just/Right, returns the values of its payload; otherwise returns no values. <procedure>(values->maybe producer)</procedure> <procedure>(values->either producer fail-obj ...)</procedure> These procedures invoke ''producer'' with no arguments. If no values are returned, Nothing / Left of ''fail-objs'' is returned. Otherwise the values are wrapped in a Just/Right and returned. ☞ The following procedures interface between the Maybe protocol and the ''two-values'' protocol, which returns {{#f}} and {{#f}} to represent failure and a single value and {{#t}} to represent success. (This protocol is more often used in Common Lisp, where additional values are automatically discarded if the continuation expects only one.) <procedure>(maybe->two-values maybe)</procedure> If ''maybe'' is a Just, returns the value of its payload and the additional value {{#t}}; otherwise returns two values, both {{#f}}. If ''maybe'' does not have exactly one value, then a condition of kind {{(exn type payload)}} is raised. <procedure>(two-values->maybe producer)</procedure> This procedure is the inverse of {{maybe->two-values}}. It invokes ''producer'' with no arguments. If the second value is true, the first value is wrapped in a Just and returned; otherwise, Nothing is returned. <procedure>(exception->either pred thunk)</procedure> This procedure is in a sense the inverse of {{maybe-ref}}. A call to ''thunk'' is made, wrapped in an exception handler that examines any exception signaled during ''thunk'''s execution. If the condition object satisfies ''pred'', it is wrapped in a Left which is then returned; if it does not, the exception is reraised. But if no exception is raised, the values returned by ''thunk'' are wrapped in a Right and the Right is returned. === Map, fold and unfold <procedure>(maybe-map proc maybe)</procedure> <procedure>(either-map proc either)</procedure> Monadic map. If ''maybe''/''either'' is a Just/Right, applies ''proc'' to the payload values and wraps the returned values as a Just/Right; otherwise returns ''maybe''/''either''. <procedure>(maybe-for-each proc maybe)</procedure> <procedure>(either-for-each proc either)</procedure> If ''maybe''/''either'' is a Just/Right, applies ''proc'' to the payload values and discards the result; otherwise does nothing. Returns an unspecified result. <procedure>(maybe-fold kons nil maybe)</procedure> <procedure>(either-fold kons nil either)</procedure> If ''maybe''/''either'' is a Just/Right, ''kons'' is invoked on the values of its payload and the additional value ''nil'' and the result is returned; otherwise, ''nil'' is returned. <procedure>(maybe-unfold stop? mapper successor seed ...)</procedure> <procedure>(either-unfold stop? mapper successor seed ...)</procedure> If ''stop?'' returns true on ''seeds'', a Nothing / a Left of ''seeds'' is returned. Otherwise, ''successor'' is applied to ''seeds''. If ''stop?'' returns false on the results of ''successor'', it is an error. (This is a condition of kind {{(exn assertion)}}.) But if the second call to ''stop?'' returns true, ''mapper'' is applied to ''seeds'' and the results are wrapped in a Just/Right and returned. === Syntax <syntax>(maybe-if maybe-expr just-expr nothing-expr)</syntax> Equivalent to {{(if (just? maybe-expr) just-expr nothing-expr)}}, except that a condition of kind {{(exn type assertion)}} is raised if ''maybe-expr'' does not evaluate to a Maybe. <syntax>(maybe-and expr ...)</syntax> <syntax>(either-and expr ...)</syntax> The ''expr''s are evaluated in order. If an ''expr'' evaluates to a Nothing / Left, it is returned without evaluating any more expressions. Otherwise, the last Just / Right is returned. If there are no ''exprs'', then a Just/Right of an unspecified value is returned. If an ''expr'' does not evaluate to a Maybe/Either, a condition of kind {{(exn type assertion)}} is raised. <syntax>(maybe-or expr ...)</syntax> <syntax>(either-or expr ...)</syntax> The ''exprs'' are evaluated in order. If an ''expr'' evaluates to a Just/Right, it is returned without evaluating any more expressions. Otherwise, the last Nothing/Left is returned. If there are no ''exprs'', then Nothing / a Left of an unspecified value is returned. If an ''expr'' does not evaluate to a Maybe/Either, a condition of kind {{(exn type assertion)}} is raised. <syntax>(maybe-let* (claw ...) . body)</syntax> <syntax>(either-let* (claw ...) . body)</syntax> The Maybe/Either equivalent of {{and-let*}} from [[http://srfi.schemers.org/srfi-2/srfi-2.html|SRFI 2]]. However, instead of testing whether a ''claw'' is true or false, it tests whether it is Just/Right or Nothing/Left. If a ''claw'' does not evaluate to a Maybe/Either, an condition of kind {{(exn type assertion)}} is raised. If a ''claw'' evaluates to a Just or Right that wraps zero values or more than one value, then a condition of kind {{(exn type payload)}} is raised. A ''claw'' is either a bound identifier, or a list of length 1, in which case it is an expression, or a list of length 2, in which case it is a variable and an expression. In either case, the value of the ''claw'' is the value of the bound variable or expression. If the value of a ''claw'' is Nothing / a Left, the expression immediately returns that value. But if the value of a ''claw'' is a Just/Right, then the variable (if any) is bound to its unwrapped value; the scope of this binding is any remaining ''claws'' and the ''body''. If the values of all the ''claws'' are Just/Right, then the ''body'' is evaluated as if it were in a {{(let () ...)}}, and its values are wrapped in a Just/Right and returned. <syntax>(maybe-let*-values (claw ...) . body)</syntax> <syntax>(either-let*-values (claw ...) . body)</syntax> The same as {{maybe/either-let*}}, except that the first element of a two-element claw is a <formals> from {{lambda}}, and the variables mentioned in it (if any) are bound to the values that result from unpacking a Just/Right. Note that a claw with a single variable in {{maybe}}/{{either-let*}} is bound to the sole value contained in the Just/Right, whereas in {{maybe}}/{{either-let*-values}}, it is bound to a list of all the values, just as in a {{lambda}} expression, a <formals> consisting of a single variable is bound to a list of all the procedure arguments. <syntax>(either-guard pred-expr . body)</syntax> The syntactic analogue of {{exception->either}}. The ''body'' is evaluated, and the values it produces are wrapped in a Right and returned, unless an exception occurs. If the condition object that is raised satisfies the predicate that is the value of ''pred-expr'', the condition object is wrapped in a Left and returned; otherwise the condition is reraised as if by {{raise-continuable}}. === Trivalent logic These procedures provide trivalent logic in the style of SQL on Maybe objects containing a single value. For the purposes of this section, an object counts as false if it is Just {{#f}}, true if it is Just anything else. It is an error if any argument is not a Maybe. Unlike {{and}} and {{or}}, {{tri-and}} and {{tri-or}} are procedures and not syntax, because determining their value requires that all the arguments be evaluated in order to provide correct SQL-style semantics. For example, {{(and #f (nothing))}} will return {{#f}} immediately without evaluating its second argument, but {{(tri-and (just #f) (nothing))}} will return Nothing. <procedure>(tri-not maybe)</procedure> Returns Just {{#t}} if ''maybe'' is false, Just {{#f}} if ''maybe'' is true, and Nothing if ''maybe'' is Nothing. <procedure>(tri=? maybe1 maybe2 ...)</procedure> Similar to {{boolean=?}}, returning Just {{#t}} if all the ''maybes'' are true or if all are false. Otherwise, if any ''maybe'' is Nothing or any two ''maybes'' have different (trivalent) truth values, returns Just {{#f}}. Note: this function is not transitive and therefore is not an equivalence relation. <procedure>(tri-and maybe ...)</procedure> If all ''maybes'' are true, Just {{#t}} is returned. If any ''maybe'' is false or Nothing, then the first such ''maybe'' is returned. If there are no arguments, Just {{#t}} is returned. <procedure>(tri-or maybe ...)</procedure> If all ''maybes'' are false, Just {{#f}} is returned. If any ''maybe'' is true or Nothing, then the first such ''maybe'' is returned. If there are no arguments, Just {{#f}} is returned. <procedure>(tri-merge maybe ...)</procedure> If any ''maybes'' are true or false, then the first such ''maybe'' is returned. If all ''maybes'' are Nothing, then Nothing is returned. If there are no arguments, Nothing is returned. == Examples Here is a variant of [[https://srfi.schemers.org/srfi-1/srfi-1.html#Searching|SRFI 1]] {{find}} that returns a Maybe, and so can be safely used to search for {{#f}} in a list, just like any other value: <enscript highlight="scheme"> (define (maybe-find pred list) (cond ((null? list) (nothing)) ((pred (car list)) (just (car list))) (else (maybe-find pred (cdr list))))) </enscript> The following examples show how {{find}} and {{maybe-find}} differ in their results: <enscript highlight="scheme"> (define (atom? obj) (not (pair? obj))) (define with-t '((1) #t (3) 4 5)) (define with-f '((1) #f (3) 4 5)) (define without '((1) (2) (3) (4) (5))) (find atom? with-t) => #t (find atom? with-f) => #f (find atom? without) => #f (maybe-find atom? with-t) => Just #t (maybe-find atom? with-f) => Just #f (maybe-find atom? without) => Nothing </enscript> And here is {{head}}, which is more general than {{car}} because it lets the caller figure out what happens if its argument is not a pair and can handle an argument that is either an actual pair or Just a pair. <enscript highlight="scheme"> (define (head x) (cond ((pair? x) (just (car x))) ((and (just? x) (pair? (maybe-ref x))) (just (car (maybe-ref x)))) (else (nothing)))) </enscript> In other words, if the argument to {{head}} is a pair, whether wrapped in a Just or not, the result is the car of that pair wrapped in a Just; in all other cases, the result is Nothing. (There are simpler ways to do this using more advanced procedures.) == Exceptions This egg tries to provide useful information when things go wrong. Type errors raise conditions of kind {{(exn type assertion)}}, while arity mismatches cause {{(exn arity assertion)}} conditions to be raised. Whenever the payload of a Maybe/Either violates a requirement (e.g. that of {{maybe-join}}), a condition of kind {{(exn type payload)}} is raised. See [[Module (chicken condition)]] for more information. The SRFI requires that the forms in the Syntax section signal an error that satisfies {{error-object?}}; this egg raises more detailed exceptions. This is fine, since CHICKEN's {{error-object?}} (in [[r7rs]]) is simply an alias for {{condition?}}. == About This Egg === Dependencies The [[srfi-1]] egg is required. The [[test]] egg is required to run the included tests. === Maintainer Wolfgang Corcoran-Mathe <wcm at sigwinch dot xyzzy without the zy> === Repository [[https://github.com/Zipheir/srfi-189-chicken|GitHub]] === Version History ; 0.1 (2020-11) : Initial release. ; 1.0.0 (2022-08-15) : Rework exceptions, streamline dependencies, improve tests. ; 1.0.2 (2022-08-16) : Fix the kind of condition raised by {{maybe->two-values}} in some cases. ; 1.0.3 (2022-12-26) : Fix unresolved identifier problem. == License Copyright (C) John Cowan, Wolfgang Corcoran-Mathe (2020). 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 (including the next paragraph) 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. 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.
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you subtract 0 from 10?