diff --git a/docs.json b/docs.json index 2b2a15983..0aa2e8fac 100644 --- a/docs.json +++ b/docs.json @@ -428,6 +428,7 @@ "group": "Fift", "pages": [ "languages/fift/overview", + "languages/fift/variables", "languages/fift/fift-and-tvm-assembly", "languages/fift/deep-dive", "languages/fift/multisig", diff --git a/languages/fift/variables.mdx b/languages/fift/variables.mdx new file mode 100644 index 000000000..f043d7c91 --- /dev/null +++ b/languages/fift/variables.mdx @@ -0,0 +1,278 @@ +--- +title: "Words, Constants, and Variables" +sidebarTitle: "Words, Constants, and Variables" +noindex: "true" +--- + +import { Aside } from '/snippets/aside.jsx'; + +## Words + +A _word_ is an identifier for an execution token, also known as a `WordDef`. To define a new word, first define a `WordDef` by enclosing code inside `{ }`; then invoke word `:` followed by the identifier for the word. For instance, + +```fift +// square takes the square of the integer at the top of the stack +{ dup * } : square +``` + +defines a new word `square`, which executes `dup` and `*` when invoked. Typing `5 square` becomes equivalent to typing `5 dup *`, and produces the same result: + +```fift +5 square // Produces 25 at the top of the stack +5 dup * // Produces 25 at the top of the stack +``` + +It is possible to use the new word inside new word definitions: + +```fift +// **5 raises the integer at the top of the stack to the 5th power +{ dup square square * } : **5 +3 **5 // Produces 243 at the top of the stack. +``` + +If the word indicated after `:` is already defined, it is redefined. However, all existing definitions of other words will continue to use the old definition of the redefined word. For instance, if `square` is redefined after the definition of `**5` above, `**5` will continue to use the original definition of `square`. + +## Constants + +A _constant_ is a word that pushes a predefined value when invoked. Constants can defined using the word `constant`. For instance, + +```fift +1000000000 constant Gram +``` + +defines a constant `Gram` equal to `10^9`. In other words, `1000000000` will be pushed into the stack whenever `Gram` is invoked: + +```fift +// Pushes Gram and 2 into the stack. +// Then, multiplies them, producing +// 2000000000 at the top of the stack. +Gram 2 * +``` + +Of course, it is possible to use the result of a computation to initialize the value of a constant: + +```fift +// Define constant mGram with the result +// of the computation Gram 1000 / +Gram 1000 / constant mGram +mGram // Pushes 1000000 into the stack +``` + +The value of a constant does not necessarily have to be an `Integer`. For instance, a string constant can be defined in the same way: + +```fift +"Hello, world!" constant hello +hello // Pushes "Hello, world!" into the stack +``` + + + +It is possible to store two values into one "double" constant by using the word `2constant`. For instance: + +```fift +355 113 2constant pifrac +``` + +defines a new word `pifrac`, which will push `355` and `113` (in that order) when invoked. The two components of a double constant can be of different types. + +If a constant with a fixed name within a block or a colon definition is needed, use `=:` and `2=:`, instead of `constant` and `2constant`. +The word `=: ` takes the value at the top of stack, creates constant `` and assigns the value to ``. +Similarly, word `2=: ` takes the two top-most values in the stack, creates constant `` and assigns the values to ``. + +For instance, the following defines a word `setxy`, which sets constants `x` and `y`: + +```fift +{ dup =: x dup * =: y } : setxy +3 setxy x y + // Produces 12 at the top of the stack +7 setxy x y + // Produces 56 at the top of the stack +``` + +The code `3 setxy x y +`, which is equivalent to `3 dup =: x dup * =: y x y +`, changes the stack as follows: + +```fift +3 // Stack: 3 +dup // Stack: 3 3 +=: x // Stack: 3 (x is 3) +dup // Stack: 3 3 +* // Stack: 9 +=: y // Stack: (y is 9) +x // Stack: 3 +y // Stack: 3 9 ++ // Stack: 12 +``` + +The code `7 setxy x y +` has a similar explanation. + +To recover the execution-time value of a constant inside a block definition, prefix the constant name with the word `@'`. For instance, using the definition of `setxy` as above, the following code defines a new word `addxy` which accesses the constants `x` and `y` and adds them: + +```fift +{ @' x @' y + } : addxy +3 setxy addxy // Produces 12 at the top of the stack +``` + +The code `3 setxy addxy` has the same effect as the code `3 setxy x y +`. The main difference between `3 setxy addxy` and `3 setxy x y +` is that in `3 setxy addxy`, constants `x` and `y` are accessed inside a code block definition, which require the use of word `@'` to access them; while in `3 setxy x y +`, the constants are accessed outside a code block definition, which does not require the use of word `@'` to access them. + +The drawback of this approach is that `@'` has to look up the current definition of constants `x` and `y` in the dictionary each time `addxy` is executed. [Variables](#variables) provide a more efficient way to achieve similar results. + +## Variables + +_Variables_ are a much more efficient way to represent changeable values. To declare a variable, use the word `variable` followed by the identifier. Internally, the word `variable` creates an empty box, which can then be updated with word `!`, and read with word `@`. + +For instance: + +```fift +// Create two variables x and y, initialized to null +variable x variable y +// Set the value of x to 2 +2 x ! +// Set the value of y to 10 +10 y ! +// Read x and place the value at the top of the stack +x @ +// Read y and place the value at the top of the stack +y @ +// Add the two values ++ // Produces 12 at the top of the stack +``` + +The word `variable` produces variables initialized to `null`. Instead, to create initialized variables to a specific value, use the phrase `box constant`: + +```fift +// Creates variable x and initializes it with value 17 +17 box constant x +// Read x and place the value at the top of the stack +x @ // 17 at the top of the stack +// Increase 17 by 1 +1 + +// Update x, now storing 18 +x ! +``` + +It is possible to define a special word for creating variables, if they are needed often: + +```fift +{ box constant } : init-variable +// Create a variable x, initialized to 17 +17 init-variable x +// Create a variable y, initialized to "test" +"test" init-variable y +``` + +Variables have one disadvantage compared to [constants](#constants): accessing the value stored in a variable requires the use of word @. This can be mitigated by defining a "getter" and a "setter" word for a variable, and use these words to write better-looking code: + +```fift +// First, create the box storing the variable contents +variable x-box + +// Define word x so that it is the procedure that +// reads the box contents. +// So, that now, x can be treated as if +// it was the "variable". +// Instead of writing "x-box @" +// to read the variable contents, simply write "x". +{ x-box @ } : x + +// Define a similar procedure for updating variables x. +{ x-box ! } : x! + +// Update variable with 5 +5 x! + +// Read the variable twice and add the results +x x + // Produces 10 at the top of the stack +``` + +It is possible to define "getters" and "setters" for variables in a more generic way. The following code defines the word `variable-get-set`, which creates a fresh variable and takes the two strings following `variable-get-set` to name the variable's getter and setter, respectively. For example, `variable-get-set x x!` will create a variable with getter `x` and setter `x!`. + +```fift +{ hole dup 1 ' @ does create 1 ' ! does create } : variable-get-set +``` + +Word `variable-get-set` works as follows: + +```fift +// Create a fresh box containing null +hole // Stack: Box +// Duplicate the box +dup // Stack: Box Box +// Push 1 +1 // Stack: Box Box 1 +// Push the word definition for @ +' @ // Stack: Box Box 1 WordDef-for-@ +// Create an execution token { Box WordDef-for-@ } +// that first pushes Box and then calls @. +// The 1 argument in the stack tells "does" that it should +// consume only one argument below 1 +// in the stack. +does // Stack: Box { Box WordDef-for-@ } +// Assign the execution token { Box WordDef-for-@ } +// to the first string comming after the invocation of variable-get-set +create // Stack: Box +// Push 1 +1 // Stack: Box 1 +// Push the word definition for ! +' ! // Stack: Box 1 WordDef-for-! +// Create an execution token { Box WordDef-for-! } +// that first pushes Box and then calls !. +// The 1 argument in the stack tells "does" that it should +// consume only one argument below 1 +// in the stack. +does // Stack: { Box WordDef-for-! } +// Assign the execution token { Box WordDef-for-! } +// to the second string comming after the invocation of variable-get-set +create // Stack: +``` + +For instance, `variable-get-set` can be used as follows: + +```fift +// Create a fresh variable with getter x and setter x! +variable-get-set x x! +// Create a fresh variable with getter y and setter y! +variable-get-set y y! +// Set x and y to 5 and 10, respectively. +5 x! 10 y! +// Swap variables x and y +x y x! y! +// Push x +x // Top of stack has 10 +// Push y +y // Top of stack has 5 +``` + +For more details on words `create`, `' `, `does`, refer to Sections [4.5](/languages/fift/whitepaper#4-5-defining-words-and-dictionary-manipulation), [4.6](/languages/fift/whitepaper#4-6-dictionary-lookup), and [4.7](/languages/fift/whitepaper#4-7-creating-and-manipulating-word-lists) in the Fift whitepaper. + +As another example of `variable-get-set`, the following implements a simple counter. The example uses auxiliary words `reset-counter` and `incr-counter` to reset the counter variable to `0` and increment the counter by one, respectively. + +```fift +// Create the getter "counter" and setter "counter!" +variable-get-set counter counter! + +// Resets the counter to 0 +{ 0 counter! } : reset-counter + +// Increments the counter by one. +{ counter 1 + counter! } : incr-counter + +reset-counter // counter variable has 0 +incr-counter // counter variable has 1 +incr-counter // counter variable has 2 +reset-counter // counter variable has 0 +incr-counter // counter variable has 1 +counter // Pushes 1 to the top of the stack +``` + +Word `incr-counter` works as follows: + +```fift +// Push the current value of counter +counter // Stack: c +// Push 1 +1 // Stack: c 1 +// Add c and 1 ++ // Stack: c+1 +// Store the new value back into the counter variable +counter! // Stack: +```