The best way to learn is by experimenting yourself.
You can reach a MUF shell by running Muq from the commandline under unix in a scratch db:
muq/c> make muq/c> rm muq-* muq/c> muq-db-c muq/c> ./muq root: 2 2 + root: 4
You can also reach a MUF shell by logging into a Micronesia
server as a normal user and doing @muf
.
The primitive MUF datatypes include strings ("abc"), floats (12.34) and integers (123).
Integer precision is normally 62 bits internally, but is transparently extended to the kilobit range upon arithmetic overflow, with no need to explicitly invoke a bignum package:
root: 2 3 + root: 6 pop 10 3 - root: 7 3 * root: 21 2 % root: 1 pop 2 10 expt root: 1024 40 expt root: 2582249878086908589655919172003011874329705792829223512830659356540647622016841194629645353280137831435903171972747493376 pop root: 123 456 789 exptmod ( Modular exponentiation -- 123 to the 456th mod 789 ) root: 699
The exptmod
function is sufficiently optimized to be usable for
digital signatures.
Float precision is about 62 bits:
root: 3.4 4.5 expt root: 246.408 sin root: 0.978691 asin root: 1.36399 2.4e30 * root: 3.27357e+30
Strings are currently limited to about 64K in length (this will be fixed in future releases):
root: "abc" "def" join root: "abcdef" pop root: [ "Print strings %-5s ints %04x and floats %-8.3f just like printf." "abc" 5 3.4 | ]print root: "Print strings abc ints 0005 and floats 3.400 just like printf." pop root: "12 128.45 hike!" "%d %f %s" unprint[ ( Works much like C sscanf ) root: [ 12 128.45 "hike!" | ]pop root:
(MUF comments are enclosed in parentheses. There must be whitespace on each side of each paren.)
Muq's character type uses syntax much like that of C, but is not as often used:
root: 'a' '\n' root: 'a' '\n'
Functions are defined using the traditional Forth syntax of a colon followed
by the function name followed by the function body ended with a semicolon.
Here we define x
to be a function working just like the built-in
*
multiplication operator:
root: : x * ; root: 2.3 4.5 x root: 10.35
Anonymous functions are defined identically, except that the
colon is replaced by a double colon and the name dropped.
They are usually invoked using the call
primitive:
root: :: "Hello, World!\n" , ; root: #<c-fn _> call Hello, World! root:
Anonymous functions are handy any time you just need to hand a short fragment of code to some function -- we'll see them again shortly as object slot initializers.
Assignment to local variables within a function is done using
value -> variable
syntax. Here's a more long-winded
version of the above function:
root: : x2 -> arg2 ( Save second parameter to a local var. ) -> arg1 ( Save first parameter to a local var. ) arg1 arg2 * -> arg3 ( Do the multiplication, save in a var. ) arg3 ( Return the result. ) ; root: 2.3 4.5 x2 root: 10.35
These local variables are created by assignment and live until the end of the function. (They are not block structured.)
Using local variables eliminates many of the readability problems that dog traditional Forths, including tinyMuck MUF. Local variables are very fast: Use them freely.
Unlike traditional Forths, MUF allows you to declare the number of arguments accepted and returned by a function. Here's an even more verbose version of the above function which does that:
root: : x3 { $ $ -> $ } ( Accepts two args, returns one result. ) -> arg2 ( Save second parameter to a local var. ) -> arg1 ( Save second parameter to a local var. ) arg1 arg2 * -> arg3 ( Do the multiplication, save in a var. ) arg3 ( Return the result. ) ; root: 2.3 4.5 x3 root: 10.35
The MUF compiler will verify that the declaration is correct:
root: : x4 { $ -> $ $ } ( Accepts two args, returns one result. ) -> arg2 ( Save second parameter to a local var. ) -> arg1 ( Save second parameter to a local var. ) arg1 arg2 * -> arg3 ( Do the multiplication, save in a var. ) arg3 ( Return the result. ) ; Sorry: Line 79: Fn x4: Fn arity conflict: 2 -> 1 vs 1 -> 2 root:
This error-checking forstalls many of the obscure bugs common in traditional Forths.
The MUF compiler is not very bright, so sometimes it will be unable
to deduce the arity correctly, for example in recursive functions.
You can disable the arity checking in such cases by including a
final !
in the arity declaration:
root: : x5 { $ $ -> $ ! } ; root:
This is also useful when you need to introduce a forward declaration of a function ahead of its actual definition: Simply define it with an empty body and suppress the arity check, as in the above example.
The basic MUF conditional expression is the much-reviled
Forth someExpression if firstChoice else secondChoice fi
construct. Here is a function to flip a coin and return "heads"
or "tails":
root: : flip { -> $ } 1 trulyRandomInteger -> oneOrZero oneOrZero 1 = if "heads" else "tails" fi -> result result ; root: flip root: "tails" pop flip root: "tails" pop flip root: "heads" pop flip root: "tails" pop flip root: "tails" pop flip root: "heads"
Unlike traditional Forths, MUF allows you to do at the commandline anything you can do inside a function definition, in particular loops and conditional expressions. (1)
In MUF the value nil
is "false" and all other
values are "true" for purposes of conditional expressions.
The conventional "true" value when an arbitrary one is
needed is t
. (Avoid using t
as the name for
a variable!)
The MUF comparison operators are
!= < <= = > >=
. They work on strings (and other
things) as well as numbers. To do case-insensitive
comparison of strings, use
!=-ci <-ci <=-ci =-ci >-ci >=-ci
.
MUF has a basic count-up or count-down loop control structure. The MUF operator for printing is the comma. Remember that blanks are significant in MUF, as in all Forths -- the comma must have whitespace on both sides!
root: for i from 0 upto 3 do{ i , "\n" , } 0 1 2 3 root: for i from 0 below 3 do{ i , "\n" , } 0 1 2 root: for i from 3 above 0 do{ i , "\n" , } 3 2 1 root: for i from 3 downto 0 do{ i , "\n" , } 3 2 1 0 root: for i from 100 downto 0 by 10 do{ i , "\n" , } 100 90 80 70 60 50 40 30 20 10 0 root:
Global variables are created and assigned to using the
expression --> variable
syntax.
Note that this assignment operator has one more dash than the one used to assign to local variables. You will be miserable in MUF until you master the distinction!
Assigning to global variables is more dangerous and expensive, so we give it a longer syntax, counting on human laziness to then encourage people to use local variables rather than global ones whenever reasonably possible:
root: 12 --> twelve ( Creates a permanent global variable. ) root: twelve root: 12 pop root: 13 -> thirteen ( Creates a local function variable -- vanishes instantly! ) root: thirteen Sorry: Undefined identifier: thirteen root: "twelve" --> twelve ( Variables have no types, only values have types. ) root: twelve root: "twelve"
Use pf
(or printFunctions
) to list the functions you have
defined.
Use pv
(or printVariables
) to list the global variables
you have defined.
To maintain sanity in a large multi-user sytem, MUF supports multiple namespaces, which are called "packages". All the above examples have been done in the default package for the root user, which is named "root", which is why the prompt printed after each command twas "root:".
Use "inPackage" to switch to another package. If the package doesn't exist, it will be created:
root: "test" inPackage test: pv test: 12 --> test12 test: 13 --> test13 test: pv test12 12 test13 13 test: "junk" inPackage junk: 12 --> junk12 junk: 13 --> junk13 junk: pv junk12 12 junk13 13 junk: "test" inPackage test: pv test12 12 test13 13 test: "root" inPackage root:
By default, variables and functions you define in a package are private to that
package. To make them visible from other packages, use export
:
root: test:test13 Sorry: Undefined identifier: test:test13 root: "test" inPackage test: 'test13 export test: "root" inPackage root: test:test13 root: 13
You can actually examine unexported symbols in other packages using the double-colon qualifier, but that is considered cheating -- it is intended as a debugging facility, not something to use in routine coding:
root: test:test12 Sorry: Undefined identifier: test:test12 root: test::test12 root: 12
Functions are exported and referenced in exactly the same way. Here
we define a function gibberish
in package test
which
when called prints out n
random verbs, then export it and
call it from the root
package:
root: "test" inPackage test: : gibberish -> n for i from 0 below n do{ 10 trulyRandomInteger -> x dict:intToVerb[x] , "\n" , } ; test: 'gibberish export test: "root" inPackage root: 3 test:gibberish blitz pelt dress root:
Go to the first, previous, next, last section, table of contents.