Go to the first, previous, next, last section, table of contents.


Basics

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.