make
. It's used for generating sequences easily where it wouldn't work to use a literal, a sequence initializer, or something that operates on an existing sequence. In its simplest form, make can be used like
[ 1 , 2 , 3 , ] { } make
which is equivalent to
{ 1 2 3 }
. The word ,
appends the top of the stack to an invisible array, which is outputted at the end. Of course, ,
can be used in a loop, and you can even call another word to add elements to the loop. For example,
: add-all ( seq -- ) [ , ] each ;
[ 3 add-all 4 add-all ] { } make
which generates { 0 1 2 0 1 2 3 }. [Note: this takes advantage of a curious property of Factor, that numbers themselves are sequences representing the range 0..n-1]
add-all
turns out to be a pretty useful word, so it's actually included in the Factor standard library under the name %
(with a more efficient implementation). So the previous code could be rewritten [ 3 % 4 % ] { } make
. These names may seem obscure at first, but you'll get used to them quickly.The importance of this is that, with
make
, you can do many complex things in constructing sequences that would be much more annoying when explicitly passed around. An example of this is take-until
, defined with the imperative parsing code in previous entries and make
as:
: take-until ( quot -- string )
#! Take the substring of a string starting at spot
#! from code until the quotation given is true and
#! advance spot to after the substring.
[ [
dup slip swap dup [ get-char , ] unless
] skip-until ] "" make nip ;
Here's another example: say you want to write a function that takes a number and outputs the string "The answer to [whatever the number is] squared is [the number squared].". Factor doesn't require complicated string interpolation or inefficient multiple concatenations as other languages would; instead, it uses
make
. The word #
converts a number to a string and then appends it to the current product of make. The code is below:
: square-description ( num -- )
[
"The answer to " %
dup #
" squared is " %
sq #
"." %
] "" make ;
When used in this way,
"" make
turns out rather like the C function sprintf
. In other cases, like most uses of { } make
, it ends up acting as a substitute for Lisp's `
(quasiquote).make
isn't any sort of magical builtin; it's actually implemented in very simple Factor code. It is in the vocabulary namespaces
and defined as such:
: make ( quot exemplar -- seq )
>r [
V{ } clone building set
call
building get
] with-scope
r> like ; inline
In English, all that does is make a new empty vector, place it in the
building
variable (which is dynamically scoped), call the given quotation, retrieve the variable, and convert the contents to the given exemplar. It operates inside a new scope so any outside definition of building
is unchanged, and make
's value for it is invisible after the word is run.Obviously, something's missing: how do elements get on this new sequence? The answer is that
,
puts them on. Its implementation is even simpler than that of make
:
: , ( elt -- ) building get push ;
and that is basically all there is to
make
Update: That's not all, folks! There's also a way to push a whole sequence, all at once, onto the
building
. How? Using a word cryptically called %
:
: % ( seq -- ) building get push-all ;
The
push-all
word is an in-place append.