-
Notifications
You must be signed in to change notification settings - Fork 24
feat(Fift): Words, Constants and Variables page #1472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3777405
1464e68
b649d0d
9bc16f9
b42906f
8b5253d
f1ce11e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,293 @@ | ||
| --- | ||
| 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 defined word inside new word definitions: | ||
|
|
||
| ```fift | ||
| // **5 raises the integer at the top of the stack to the 5th power. | ||
| // It makes use of word square previously defined. | ||
| { dup square square * } : **5 | ||
| 3 **5 // Produces 243 at the top of the stack. | ||
| ``` | ||
|
|
||
| This is the trace for `3 **5`: | ||
|
|
||
| ```fift | ||
| 3 // Stack: 3 | ||
| // Definition of **5 executes | ||
| dup // Stack: 3 3 | ||
| square // Stack: 3 9 | ||
| square // Stack: 3 81 | ||
| * // Stack: 243 | ||
| ``` | ||
|
|
||
| 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 be 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 * | ||
| ``` | ||
|
|
||
| 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 | ||
| ``` | ||
|
|
||
| <Aside> | ||
| If a constant is redefined, all existing definitions of other words will continue to use the old value of the constant. In this respect, a constant does not behave as a global variable. | ||
| </Aside> | ||
|
|
||
| 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 definition is needed, use `=:` and `2=:`, instead of `constant` and `2constant`. | ||
| The word `=: <IDENTIFIER>` takes the value at the top of the stack, creates constant `<IDENTIFIER>` and assigns the value to `<IDENTIFIER>`. | ||
| Similarly, word `2=: <IDENTIFIER>` takes the two top-most values in the stack, creates constant `<IDENTIFIER>` and assigns the values to `<IDENTIFIER>`. | ||
|
|
||
| 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 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 definition, which require the use of word `@'` to access them; while in `3 setxy x y +`, the constants are accessed outside a code 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 `@`. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would show that |
||
|
|
||
| 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 reads the box contents. | ||
| // Now, x can be treated as if | ||
| // it was the "variable". | ||
| // In other words, instead of writing "x-box @" | ||
| // to read the variable contents, write "x". | ||
| { x-box @ } : x | ||
|
|
||
| // Define a similar procedure for updating variable 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-@ | ||
| // "does" creates the block/execution token { Box WordDef-for-@ execute } | ||
| // that first pushes Box and then executes @. | ||
| // The 1 integer in the stack tells "does" that it should | ||
| // consume only one stack element below 1 | ||
| // in the stack. | ||
| // So, it only takes one Box immediately below 1 to include | ||
| // it inside the block/execution token. | ||
| does // Stack: Box { Box WordDef-for-@ execute } | ||
| // Assign the block/execution token { Box WordDef-for-@ execute } | ||
| // to the first string coming 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 a block/execution token { Box WordDef-for-! execute } | ||
| // that first pushes Box and then calls !. | ||
| // The 1 integer in the stack tells "does" that it should | ||
| // consume only one argument below 1 | ||
| // in the stack. | ||
| // So, it only takes one Box immediately below 1 to include | ||
| // it inside the block/execution token. | ||
| does // Stack: { Box WordDef-for-! execute } | ||
| // Assign the block/execution token { Box WordDef-for-! execute } | ||
| // to the second string comming after the invocation of variable-get-set | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fix the comment typo |
||
| 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 blocks/execution tokens and words `' <WORD_NAME>` and `execute`, refer to the Blocks Section in the Control Flow page. For more details on words `create`, `' <WORD_NAME>`, `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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add link to |
||
|
|
||
| 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! | ||
|
|
||
| // Reset the counter to 0 | ||
| { 0 counter! } : reset-counter | ||
|
|
||
| // Increment 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: | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
phrase
may be confusing after editing
types.mdx