http.server documentation
parent
35b5bd9898
commit
0ba1a1335e
|
@ -1,7 +1,9 @@
|
|||
IN: html.templates.fhtml
|
||||
USING: help.markup help.syntax ;
|
||||
|
||||
HELP: <fhtml> ;
|
||||
HELP: <fhtml> ( path -- fhtml )
|
||||
{ $values { "path" "a pathname string" } { "fhtml" fhtml } }
|
||||
{ $description "Creates an FHTML template descriptor." } ;
|
||||
|
||||
ARTICLE: "html.templates.fhtml" "FHTML templates"
|
||||
"The " { $vocab-link "html.templates.fhtml" } " vocabulary implements a templating engine which mixes markup with Factor code."
|
||||
|
|
|
@ -137,22 +137,27 @@ ARTICLE: "http.post-data" "HTTP post data"
|
|||
{ $subsection post-data }
|
||||
{ $subsection <post-data> } ;
|
||||
|
||||
ARTICLE: "http" "HTTP protocol objects"
|
||||
"The " { $vocab-link "http" } " vocabulary contains data types shared by " { $vocab-link "http.client" } " and " { $vocab-link "http.server" } "."
|
||||
$nl
|
||||
"The HTTP client sends an HTTP request to the server and receives an HTTP response back. The HTTP server receives HTTP requests from clients and sends HTTP responses back."
|
||||
$nl
|
||||
ARTICLE: "http.requests" "HTTP requests"
|
||||
"HTTP requests:"
|
||||
{ $subsection request }
|
||||
{ $subsection <request> }
|
||||
"Requests can contain form submissions:"
|
||||
{ $subsection "http.post-data" }
|
||||
{ $subsection "http.post-data" } ;
|
||||
|
||||
ARTICLE: "http.responses" "HTTP responses"
|
||||
"HTTP responses:"
|
||||
{ $subsection response }
|
||||
{ $subsection <response> }
|
||||
"Raw responses only contain a status line, with no header. They are used by webapps which need full control over the HTTP response, for example " { $vocab-link "http.server.cgi" } ":"
|
||||
{ $subsection raw-response }
|
||||
{ $subsection <raw-response> }
|
||||
{ $subsection <raw-response> } ;
|
||||
|
||||
ARTICLE: "http" "HTTP protocol objects"
|
||||
"The " { $vocab-link "http" } " vocabulary contains data types shared by " { $vocab-link "http.client" } " and " { $vocab-link "http.server" } "."
|
||||
$nl
|
||||
"The HTTP client sends an HTTP request to the server and receives an HTTP response back. The HTTP server receives HTTP requests from clients and sends HTTP responses back."
|
||||
{ $subsection "http.requests" }
|
||||
{ $subsection "http.responses" }
|
||||
"Both requests and responses support some common functionality:"
|
||||
{ $subsection "http.headers" }
|
||||
{ $subsection "http.cookies" }
|
||||
|
|
|
@ -257,7 +257,7 @@ test-db [
|
|||
"" add-responder
|
||||
add-quit-action
|
||||
<dispatcher>
|
||||
<action> "a" add-main-responder
|
||||
<action> "" add-responder
|
||||
"d" add-responder
|
||||
test-db <db-persistence>
|
||||
main-responder set
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
USING: help.markup help.syntax http.server.static multiline ;
|
||||
IN: http.server.cgi
|
||||
|
||||
HELP: enable-cgi
|
||||
{ $values { "responder" file-responder } }
|
||||
{ $description "Enables the responder to serve " { $snippet ".cgi" } " scripts by executing them as per the CGI specification." }
|
||||
{ $examples
|
||||
{ $code
|
||||
<" <dispatcher>
|
||||
"/var/www/cgi/" <static> enable-cgi "cgi-bin" add-responder" ">
|
||||
}
|
||||
}
|
||||
{ $side-effects "responder" } ;
|
||||
|
||||
ARTICLE: "http.server.cgi" "Serving CGI scripts"
|
||||
"The " { $vocab-link "http.server.cgi" } " implements CGI support. It is used in conjunction with a " { $link <static> } " responder."
|
||||
{ $subsection enable-cgi } ;
|
|
@ -0,0 +1,91 @@
|
|||
! Copyright (C) 2008 Your name.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: classes help.markup help.syntax io.streams.string
|
||||
multiline ;
|
||||
IN: http.server.dispatchers
|
||||
|
||||
HELP: new-dispatcher
|
||||
{ $values { "class" class } { "dispatcher" dispatcher } }
|
||||
{ $description "Creates a new instance of a subclass of " { $link dispatcher } "." } ;
|
||||
|
||||
HELP: dispatcher
|
||||
{ $description "The class of dispatchers. May be subclassed, in which case subclasses should be constructed by calling " { $link new-dispatcher } "." } ;
|
||||
|
||||
HELP: <dispatcher>
|
||||
{ $values { "dispatcher" dispatcher } }
|
||||
{ $description "Creates a new pathname dispatcher." } ;
|
||||
|
||||
HELP: vhost-dispatcher
|
||||
{ $description "The class of virtual host dispatchers." } ;
|
||||
|
||||
HELP: <vhost-dispatcher>
|
||||
{ $values { "dispatcher" vhost-dispatcher } }
|
||||
{ $description "Creates a new virtual host dispatcher." } ;
|
||||
|
||||
HELP: add-responder
|
||||
{ $values
|
||||
{ "dispatcher" dispatcher } { "responder" "a responder" } { "path" "a pathname string or hostname" } }
|
||||
{ $description "Adds a responder to a dispatcher." }
|
||||
{ $notes "The " { $snippet "path" } " parameter is interpreted differently depending on the dispatcher type." }
|
||||
{ $side-effects "dispatcher" } ;
|
||||
|
||||
ARTICLE: "http.server.dispatchers.example" "HTTP dispatcher examples"
|
||||
{ $heading "Simple pathname dispatcher" }
|
||||
{ $code
|
||||
<" <dispatcher>
|
||||
<new-action> "new" add-responder
|
||||
<edit-action> "edit" add-responder
|
||||
<delete-action> "delete" add-responder
|
||||
<list-action> "" add-responder
|
||||
main-responder set-global">
|
||||
}
|
||||
"In the above example, visiting any URL other than " { $snippet "/new" } ", " { $snippet "/edit" } ", " { $snippet "/delete" } ", or " { $snippet "/" } " will result in a 404 error."
|
||||
{ $heading "Another pathname dispatcher" }
|
||||
"On the other hand, suppose we wanted to route all unrecognized paths to a ``view'' action:"
|
||||
{ $code
|
||||
<" <dispatcher>
|
||||
<new-action> "new" add-responder
|
||||
<edit-action> "edit" add-responder
|
||||
<delete-action> "delete" add-responder
|
||||
<view-action> >>default
|
||||
main-responder set-global">
|
||||
}
|
||||
"The " { $slot "default" } " slot holds a responder to which all unrecognized paths are sent to."
|
||||
{ $heading "Dispatcher subclassing example" }
|
||||
{ $code
|
||||
<" TUPLE: golf-courses < dispatcher ;
|
||||
|
||||
: <golf-courses> ( -- golf-courses )
|
||||
golf-courses new-dispatcher ;
|
||||
|
||||
<golf-courses>
|
||||
<new-action> "new" add-responder
|
||||
<edit-action> "edit" add-responder
|
||||
<delete-action> "delete" add-responder
|
||||
<list-action> "" add-responder
|
||||
main-responder set-global">
|
||||
}
|
||||
"The action templates can now emit links to responder-relative URLs prefixed by " { $snippet "$golf-courses/" } "."
|
||||
{ $heading "Virtual hosting example" }
|
||||
{ $code
|
||||
<" <vhost-dispatcher>
|
||||
<casino> "concatenative-casino.com" add-responder
|
||||
<dating> "raptor-dating.com" add-responder
|
||||
main-responder set-global">
|
||||
}
|
||||
"Note that the virtual host dispatcher strips off a " { $snippet "www." } " prefix, so " { $snippet "www.concatenative-casino.com" } " would be routed to the " { $snippet "<casino>" } " responder instead of receiving a 404." ;
|
||||
|
||||
ARTICLE: "http.server.dispatchers" "HTTP dispatchers and virtual hosting"
|
||||
"The " { $vocab-link "http.server.dispatchers" } " vocabulary implements two responders which route HTTP requests to one or more child responders."
|
||||
{ $subsection "http.server.dispatchers.example" }
|
||||
"Pathname dispatchers implement a directory hierarchy where each subdirectory is its own responder:"
|
||||
{ $subsection dispatcher }
|
||||
{ $subsection <dispatcher> }
|
||||
"Virtual host dispatchers dispatch each virtual host to a different responder:"
|
||||
{ $subsection vhost-dispatcher }
|
||||
{ $subsection <vhost-dispatcher> }
|
||||
"Adding responders to dispatchers:"
|
||||
{ $subsection add-responder }
|
||||
"The " { $slot "default" } " slot holds a responder which receives all unrecognized URLs. By default, it responds with 404 messages." ;
|
||||
|
||||
ABOUT: "http.server.dispatchers"
|
|
@ -0,0 +1,12 @@
|
|||
USING: help.markup help.syntax http.server ;
|
||||
IN: http.server.filters
|
||||
|
||||
HELP: filter-responder
|
||||
{ $description "The class of filter responders. This class is intended to be subclassed." } ;
|
||||
|
||||
ARTICLE: "http.server.filters" "HTTP responder filters"
|
||||
"The " { $vocab-link "http.server.filters" } " vocabulary implements the common pattern where one responder wraps another, doing some processing before calling the wrapped responder."
|
||||
{ $subsection filter-responder }
|
||||
"To use it, simply subclass " { $link filter-responder } ", and call " { $link POSTPONE: call-next-method } " from your " { $link call-responder* } " method to pass control to the wrapped responder." ;
|
||||
|
||||
ABOUT: "http.server.filters"
|
|
@ -0,0 +1,26 @@
|
|||
USING: help.markup help.syntax urls strings http ;
|
||||
IN: http.server.redirection
|
||||
|
||||
HELP: relative-to-request
|
||||
{ $values { "url" "a " { $link url } " or " { $link string } } { "url'" "a " { $link url } " or " { $link string } } }
|
||||
{ $description "If the input is a relative " { $link url } ", makes it an absolute URL by resolving it to the current request's URL. If the input is a string, does nothing." } ;
|
||||
|
||||
HELP: <permanent-redirect>
|
||||
{ $values { "url" "a " { $link url } " or " { $link string } } { "response" response } }
|
||||
{ $description "Redirects to the user to the URL after applying " { $link relative-to-request } "." }
|
||||
{ $notes "This redirect type should always be used with POST requests, and with GET requests in cases where the new URL always supercedes the old one. This is due to browsers caching the new URL with permanent redirects." } ;
|
||||
|
||||
HELP: <temporary-redirect>
|
||||
{ $values { "url" "a " { $link url } " or " { $link string } } { "response" response } }
|
||||
{ $description "Redirects to the user to the URL after applying " { $link relative-to-request } "." }
|
||||
{ $notes "This redirect type should be used with GET requests where the new URL does not always supercede the old one. Use from POST requests with care, since this will cause the browser to resubmit the form to the new URL." } ;
|
||||
|
||||
ARTICLE: "http.server.redirection" "HTTP responder redirection"
|
||||
"The " { $vocab-link "http.server.redirection" } " defines some " { $link response } " types which redirect the user's client to a new page."
|
||||
{ $subsection <permanent-redirect> }
|
||||
{ $subsection <temporary-redirect> }
|
||||
"A utility used by the above:"
|
||||
{ $subsection relative-to-request }
|
||||
"The " { $vocab-link "furnace.redirection" } " vocabulary provides a higher-level implementation of this. The " { $vocab-link "furnace.conversations" } " vocabulary allows state to be maintained between redirects." ;
|
||||
|
||||
ABOUT: "http.server.redirection"
|
|
@ -0,0 +1,24 @@
|
|||
USING: help.markup help.syntax ;
|
||||
IN: http.server.remapping
|
||||
|
||||
HELP: port-remapping
|
||||
{ $var-description "An assoc mapping port numbers that the HTTP server listens on to external port numbers presented to the user." } ;
|
||||
|
||||
ARTICLE: "http.server.remapping" "HTTP server port remapping"
|
||||
"On Unix systems, non-root processes cannot bind to sockets on port numbers under 1024. Since running an HTTP server as root is a potential security risk, a typical setup runs an HTTP server under an ordinary user account, set up to listen on a higher port number such as 8080. Then, the HTTP port is redirected to 8080. On Linux, this might be done using commands such as the following:"
|
||||
{ $code
|
||||
"echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||
"iptables -t nat -F"
|
||||
"iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j DNAT --to :8443"
|
||||
"iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j DNAT --to :8080"
|
||||
}
|
||||
"However, the HTTP server is unaware of the forwarding, and still believes that it is listening on port 8080 and 8443, respectively. This can be a problem if a responder wishes to redirect the user to a secure page; they will be sent to port 8443 and not 443 as one would expect."
|
||||
$nl
|
||||
"The " { $vocab-link "http.server.remapping" } " vocabulary defines a variable which may store an assoc of port mappings:"
|
||||
{ $subsection port-remapping }
|
||||
"For example, with the above setup, we would set it as follows:"
|
||||
{ $code
|
||||
"{ { 8080 80 } { 8443 443 } } port-remapping set-global"
|
||||
} ;
|
||||
|
||||
ABOUT: "http.server.remapping"
|
|
@ -0,0 +1,29 @@
|
|||
USING: help.markup help.syntax io.streams.string strings
|
||||
http math ;
|
||||
IN: http.server.responses
|
||||
|
||||
HELP: <content>
|
||||
{ $values { "body" "a response body" } { "content-type" string } { "response" response } }
|
||||
{ $description "Creates a successful HTTP response which sends a response body with the specified content type to the client." } ;
|
||||
|
||||
HELP: <trivial-response>
|
||||
{ $values { "code" integer } { "message" string } { "response" response } }
|
||||
{ $description "Creates an HTTP error response." }
|
||||
{ $examples
|
||||
{ $code
|
||||
"USE: http.server.responses"
|
||||
"415 \"Unsupported Media Type\" <trivial-response>"
|
||||
}
|
||||
} ;
|
||||
|
||||
ARTICLE: "http.server.responses" "Canned HTTP responses"
|
||||
"The " { $vocab-link "http.server.responses" } " vocabulary provides constructors for a few useful " { $link response } " objects."
|
||||
{ $subsection <content> }
|
||||
{ $subsection <304> }
|
||||
{ $subsection <403> }
|
||||
{ $subsection <400> }
|
||||
{ $subsection <404> }
|
||||
"New error responses like the above can be created for other error codes too:"
|
||||
{ $subsection <trivial-response> } ;
|
||||
|
||||
ABOUT: "http.server.responses"
|
|
@ -0,0 +1,109 @@
|
|||
USING: help.markup help.syntax io.streams.string quotations strings urls http tools.vocabs math io.servers.connection ;
|
||||
IN: http.server
|
||||
|
||||
HELP: trivial-responder
|
||||
{ $description "The class of trivial responders, which output the same response for every request. New instances are created by calling " { $link <trivial-responder> } "." } ;
|
||||
|
||||
HELP: <trivial-responder> ( response -- responder )
|
||||
{ $values { "response" response } { "responder" trivial-responder } }
|
||||
{ $description "Creates a new trivial responder which outputs the same response for every request." } ;
|
||||
|
||||
HELP: benchmark?
|
||||
{ $var-description "If set to a true value, the HTTP server will log the time taken to process each request." } ;
|
||||
|
||||
HELP: call-responder
|
||||
{ $values
|
||||
{ "path" "a sequence of strings" } { "responder" "a responder" }
|
||||
{ "response" response } }
|
||||
{ $description "Calls a responder." } ;
|
||||
|
||||
HELP: call-responder*
|
||||
{ $values
|
||||
{ "path" "a sequence of strings" } { "responder" "a responder" }
|
||||
{ "response" response } }
|
||||
{ $contract "Processes an HTTP request and returns a response." }
|
||||
{ $notes "When this word is called, various dynamic variables are set; see " { $link "http.server.requests" } "." } ;
|
||||
|
||||
HELP: development?
|
||||
{ $var-description "If set to a true value, the HTTP server will call " { $link refresh-all } " on each request, and error pages will contain stack traces." } ;
|
||||
|
||||
HELP: main-responder
|
||||
{ $var-description "The responder which will handle HTTP requests." } ;
|
||||
|
||||
HELP: post-request?
|
||||
{ $values { "?" "a boolean" } }
|
||||
{ $description "Outputs if the current request is a POST request.s" } ;
|
||||
|
||||
HELP: responder-nesting
|
||||
{ $description "A sequence of " { $snippet "{ path responder }" } " pairs." } ;
|
||||
|
||||
HELP: http-server
|
||||
{ $class-description "The class of HTTP servers. New instances are created by calling " { $link <http-server> } "." } ;
|
||||
|
||||
HELP: <http-server>
|
||||
{ $values { "server" http-server } }
|
||||
{ $description "Creates a new HTTP server with default parameters." } ;
|
||||
|
||||
HELP: httpd
|
||||
{ $values { "port" integer } }
|
||||
{ $description "Starts an HTTP server on the specified port number." }
|
||||
{ $notes "For more flexibility, use " { $link <http-server> } " and fill in the tuple slots before calling " { $link start-server } "." } ;
|
||||
|
||||
HELP: http-insomniac
|
||||
{ $description "Starts a thread which rotates the logs and e-mails a summary of HTTP requests every 24 hours. See " { $link "logging.insomniac" } "." } ;
|
||||
|
||||
ARTICLE: "http.server.requests" "HTTP request variables"
|
||||
"The following variables are set by the HTTP server at the beginning of a request."
|
||||
{ $subsection request }
|
||||
{ $subsection url }
|
||||
{ $subsection post-request? }
|
||||
{ $subsection responder-nesting }
|
||||
"Additional vocabularies may be set by vocabularies such as " { $vocab-link "html.forms" } " and " { $vocab-link "furnace.sessions" } "." ;
|
||||
|
||||
ARTICLE: "http.server.responders" "HTTP server responders"
|
||||
"The HTTP server dispatches requests to a main responder:"
|
||||
{ $subsection main-responder }
|
||||
"The main responder may in turn dispatch it a subordinate dispatcher, and so on."
|
||||
$nl
|
||||
"Responders process requests and output " { $link "http.responses" } "; concretely are instances of classes which implement a generic word:"
|
||||
{ $subsection call-responder* }
|
||||
"To actually call a subordinate responder, use the following word instead:"
|
||||
{ $subsection call-responder }
|
||||
"A simple implementation of a responder which always outputs the same response:"
|
||||
{ $subsection trivial-responder }
|
||||
{ $subsection <trivial-responder> }
|
||||
{ $vocab-subsection "Furnace actions" "furnace.actions" }
|
||||
"In particular, writing new responders by hand is rarely necessary, because in most cases it is easier to use " { $vocab-link "furnace.actions" } " instead." ;
|
||||
|
||||
ARTICLE: "http.server.variables" "HTTP server variables"
|
||||
"The following global variables control the behavior of the HTTP server. Both are off by default."
|
||||
{ $subsection development? }
|
||||
{ $subsection benchmark? } ;
|
||||
|
||||
ARTICLE: "http.server" "HTTP server"
|
||||
"The " { $vocab-link "http.server" } " vocabulary implements an HTTP and HTTPS server on top of " { $vocab-link "io.servers.connection" } "."
|
||||
{ $subsection "http.server.responders" }
|
||||
{ $subsection "http.server.requests" }
|
||||
"Various types of responders are defined in other vocabularies:"
|
||||
{ $subsection "http.server.dispatchers" }
|
||||
{ $subsection "http.server.filters" }
|
||||
"Useful canned responses:"
|
||||
{ $subsection "http.server.responses" }
|
||||
{ $subsection "http.server.redirection" }
|
||||
"Configuration:"
|
||||
{ $subsection "http.server.variables" }
|
||||
{ $subsection "http.server.remapping" }
|
||||
"Features:"
|
||||
{ $subsection "http.server.static" }
|
||||
{ $subsection "http.server.cgi" }
|
||||
"The " { $vocab-link "furnace" } " framework implements high-level abstractions which make developing web applications much easier than writing responders by hand." ;
|
||||
|
||||
ABOUT: "http.server"
|
||||
|
||||
USE: vocabs.loader
|
||||
|
||||
"http.server.filters" require
|
||||
"http.server.dispatchers" require
|
||||
"http.server.redirection" require
|
||||
"http.server.static" require
|
||||
"http.server.cgi" require
|
|
@ -0,0 +1,37 @@
|
|||
! Copyright (C) 2008 Your name.
|
||||
! See http://factorcode.org/license.txt for BSD license.
|
||||
USING: help.markup help.syntax io.streams.string ;
|
||||
IN: http.server.static
|
||||
|
||||
HELP: <file-responder>
|
||||
{ $values { "root" "a pathname string" } { "hook" "a quotation with stack effect " { $snippet "( path mime-type -- response )" } } { "responder" file-responder } }
|
||||
{ $description "Creates a file responder which serves content from " { $snippet "path" } " by using the hook to generate a response." } ;
|
||||
|
||||
HELP: <static>
|
||||
{ $values
|
||||
{ "root" "a pathname string" }
|
||||
{ "responder" file-responder } }
|
||||
{ $description "Creates a file responder which serves content from " { $snippet "path" } "." } ;
|
||||
|
||||
HELP: enable-fhtml
|
||||
{ $values { "responder" file-responder } }
|
||||
{ $description "Enables the responder to serve " { $snippet ".fhtml" } " files by running them." }
|
||||
{ $notes "See " { $link "html.templates.fhtml" } "." }
|
||||
{ $side-effects "responder" } ;
|
||||
|
||||
ARTICLE: "http.server.static" "Serving static content"
|
||||
"The " { $vocab-link "http.server.static" } " vocabulary implements a responder for serving static files."
|
||||
{ $subsection <static> }
|
||||
"The static responder does not serve directory listings by default, as a security measure. Directory listings can be enabled by storing a true value in the " { $slot "allow-listings" } " slot."
|
||||
$nl
|
||||
"The static responder can be extended for dynamic content by associating quotations with MIME types in the hashtable stored in the " { $slot "special" } " slot. The quotations have stack effect " { $snippet "( path -- )" } "."
|
||||
$nl
|
||||
"A utility word uses the above feature to enable server-side " { $snippet ".fhtml" } " scripts, allowing a development style much like PHP:"
|
||||
{ $subsection enable-fhtml }
|
||||
"This feature is also used by " { $vocab-link "http.server.cgi" } " to run " { $snippet ".cgi" } " files."
|
||||
$nl
|
||||
"It is also possible to override the hook used when serving static files to the client:"
|
||||
{ $subsection <file-responder> }
|
||||
"The default just sends the file's contents with the request; " { $vocab-link "xmode.responder" } " provides an alternate hook which sends a syntax-highlighted version of the file." ;
|
||||
|
||||
ABOUT: "http.server.static"
|
|
@ -12,7 +12,6 @@ http.server.responses
|
|||
http.server.redirection ;
|
||||
IN: http.server.static
|
||||
|
||||
! special maps mime types to quots with effect ( path -- )
|
||||
TUPLE: file-responder root hook special allow-listings ;
|
||||
|
||||
: modified-since? ( filename -- ? )
|
||||
|
|
|
@ -4,7 +4,7 @@ IN: math.ranges
|
|||
|
||||
ARTICLE: "ranges" "Ranges"
|
||||
"A " { $emphasis "range" } " is a virtual sequence with real number elements "
|
||||
"ranging from " { $emphasis "a" } " to " { $emphasis "b" } " by " { $emphasis "step" } "."
|
||||
"ranging from " { $emphasis "a" } " to " { $emphasis "b" } " by " { $emphasis "step" } ". Ascending as well as descending ranges are supported."
|
||||
$nl
|
||||
"The class of ranges:"
|
||||
{ $subsection range }
|
||||
|
@ -19,9 +19,9 @@ $nl
|
|||
"Creating general ranges:"
|
||||
{ $subsection <range> }
|
||||
"Ranges are most frequently used with sequence combinators as a means of iterating over integers. For example,"
|
||||
{ $code
|
||||
"3 10 [a,b] [ sqrt ] map"
|
||||
}
|
||||
{ $code "3 10 [a,b] [ sqrt ] map" }
|
||||
"Computing the factorial of 100 with a descending range:"
|
||||
{ $code "100 1 [a,b] product" }
|
||||
"A range can be converted into a concrete sequence using a word such as " { $link >array } ". In most cases this is unnecessary since ranges implement the sequence protocol already. It is necessary if a mutable sequence is needed, for use with words such as " { $link set-nth } " or " { $link change-each } "." ;
|
||||
|
||||
ABOUT: "ranges"
|
Loading…
Reference in New Issue