Go to Lesson: 1 | 2 | 3 | 4 | 5
Lesson 1 — Tutorial
Hello World
The following is a traditional “Hello World” program in MANOOL:
-- Hello World program -- in "applicative" notation
{{extern "manool.org.18/std/0.6/all"} in WriteLine[Out; "Hello, world!"]}
You can play with MANOOL examples by using the online evaluator or running them from the command line according to the instructions. Here are just a couple of ideas about how to run MANOOL programs from the command line:
mnlexec hello.mnl
(assuming you have placed your source code into hello.mnl
) or, for short scripts:
mnlexec <(echo $'{{extern "manool.org.18/std/0.6/all"} in WriteLine[Out; "Hello, world!"]}')
(we use $'...'
with a leading $
here just to be sure we can more easily escape '
characters in Bash in the future).
The expected output is unsurprisingly
Hello, world!
Using the online evaluator is convenient but has certain limitations. Whatever approach you choose, first consult How to Download and Install MANOOL for more tips.
How it works
-
The first line
-- ...
is just a comment, which is ignored by the MANOOL translator (see later). -
The construct
WriteLine[Out; "..."]
on the second line resembles a function call in many languages, where the constructWriteLine
would specify a function, andOut
and"Hello, world!"
would be the arguments.1 Here, as a side effect of an evaluation of this expression, the phraseHello, world!
, specified by a string literal, eventually appears on the standard output, specified by the argumentOut
. -
During compilation of the whole expression
{{extern "..."} in ...}
, all identifier definitions (particularly the one forOut
)2 from the standard library module specified by the string literal"manool.org.18/..."
are imported (injected) into the scope that follows the keywordin
.3
Alternative notations
The following two alternative “Hello World” implementations are equivalent to the above one, up to an internal representation called abstract syntax tree:
-- Hello World program -- OOP-ish notation (equivalent to the above, up to abstract syntax tree)
{{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Hello, world!"]}
(note how the first argument now corresponds to a receiver, in OOP parlance),
-- Hello World program -- LISP-ish notation (ditto)
{{extern "manool.org.18/std/0.6/all"} in {WriteLine Out "Hello, world!"}}
(note how WriteLine
, the target of an applicative expression, is now the first element of a Lisp-inspired syntactic list)4.
Code formatting recommendations
MANOOL is a free-form and case-sensitive language. The most basic principles of MANOOL code formatting are illustrated in the following example (you are invited to see for yourself more principles in action further in this tutorial):5
-- Most recommended formatting for multi-line expressions
{ {extern "manool.org.18/std/0.6/all"} in
WriteLine[Out; "Hello, world!"]
}
Commenting code in MANOOL
MANOOL supports two kinds of comments (please see Comments for a complete reference):
{{extern "manool.org.18/std/0.6/all"} in Out.WriteLine[/*Out;*/ "Hello, world!"]} -- this is a comment
Combining Multiple Expressions
Now let's assemble the Hello, world!
phrase from several fragments. Although this may sound a bit boring, this task is perfect to illustrate how to combine
multiple applicative expressions together.
First, to stipulate their sequential evaluation (one after another), just for the purposes of side effects (i.e., ignoring any results they return), you can
simply write down the expressions one after another, optionally delimiting them by semicolons (;
):6
{ {extern "manool.org.18/std/0.6/all"} in -- Evaluation of multiple expressions in a row
Out.Write["Hello"]; Out.Write[", "]; Out.Write["world"]; Out.Write["!"]
Out.WriteLine[] -- produce a newline without any other output
}
(you can replace WriteLine
with just Write
to avoid producing newlines).
Output:
Hello, world!
Using multiple arguments
You can instead specify more than two arguments to WriteLine
(or Write
) to achieve the same net effect:
{ {extern "manool.org.18/std/0.6/all"} in -- Several arguments to WriteLine
Out.WriteLine["Hello"; ", "; "world"; "!"]; -- semicolon delimiters are optional
Out.WriteLine["Hello" ", "; "world" "!"] -- everywhere where they are allowed at all
}
(note how the rule regarding optionality of semicolons applies here as well)7.
Output:
Hello, world!
Hello, world!
String concatenation, infix operators
Or, you can apply the conventional infix operator +
to several string values to produce a concatenation of them and output the result as usual:
{ {extern "manool.org.18/std/0.6/all"} in -- String concatenation
Out.WriteLine["Hello" + ", " + "world" + "!"] -- (true) infix notation
}
Output:
Hello, world!
An infix/prefix/postfix operator in MANOOL is actually nothing more than a normal symbol (like Write
/WriteLine
) specially recognized and “desugared” by
the MANOOL parser. To suppress such special treatment, enclose an operator in parentheses (as in (+)
).
To illustrate how alternative notations would work and what the properly infix notation actually maps to (preserving the original AST):8
{ {extern "manool.org.18/std/0.6/all"} in -- String concatenation -- alternative notations
Out.WriteLine[(+)[(+)[(+)["Hello"; ", "]; "world"]; "!"]] -- applicative (prefix) notation (equivalent up to AST)
Out.WriteLine[{(+) {(+) {(+) "Hello" ", "} "world"} "!"}] -- LISPish (prefix) notation (ditto)
Out.WriteLine["Hello".(+)[", "].(+)["world"].(+)["!"]] -- OOPish (infix) notation (ditto)
}
(note that the +
operator is, unsurprisingly, left-associative)9.
Output:
Hello, world!
Hello, world!
Hello, world!
Simple Arithmetic
Let's perform some simple arithmetic operations on integral numbers.
To display results, you can directly pass the values to WriteLine
(or Write
), or you can first explicitly convert the values to a string representation by
applying the Str
operation:10
{ {extern "manool.org.18/std/0.6/all"} in -- Performing Integer arithmetic operations and displaying results
Out.WriteLine["2 + 3 = " 2 + 3; ", "; "5 - 2 = " 5 - 2] -- outputting piece-wise
Out.WriteLine["2 + 3 = " + Str[2 + 3] + ", " + "5 - 2 = " + Str[5 - 2]] -- explicit conversion to String and concatenation
}
Output:
2 + 3 = 5, 5 - 2 = 3
2 + 3 = 5, 5 - 2 = 3
Multiplication, operator precedence and associativity
You can also use more complex expressions involving multiple operators, which follow nearly conventional precedence and associativity rules:
{ {extern "manool.org.18/std/0.6/all"} in -- Demonstrating operator precedence (binding strength) and associativity
Out.WriteLine["2 + 3 * 4 - 5 = " 2 + 3 * 4 - 5; ", "; "(2 + (3 * 4)) - 5 = " (2 + (3 * 4)) - 5]
Out.WriteLine["(2 + 3) * (4 - 5) = " (2 + 3) * (4 - 5)] -- overriding with explicit grouping of operands
}
All these operators associate to the left, and *
binds tighter than +
and -
unless where explicitly overriden with parentheses (please refer to Syntactic
Analysis for the complete formal grammar of MANOOL).11
Output:
2 + 3 * 4 - 5 = 9, (2 + (3 * 4)) - 5 = 9
(2 + 3) * (4 - 5) = -5
Unary minus
In MANOOL an arithmetic negation (or unary minus) operation on a number can be always performed by applying the symbol Neg
to the argument. Alternatively, the
prefix operator ~
works the same way for most (but not all) types of numbers.12 Here is a short example:
{{extern "manool.org.18/std/0.6/all"} in Out.WriteLine["Neg[2] = " Neg[2]; ", "; "5 + ~2 = " 5 + ~2]}
Output:
Neg[2] = -2, 5 + ~2 = 3
Division
From a number-theoretic as well as practical standpoint, MANOOL provides a fairly comprehensive set of division-related operations on integral numbers:13
(/)
, Rem
, Div
, and Mod
.
Without going into much detail about Integer division, which is beyond the scope of this tutorial, let's construct a simple program that displays on the standard output a table that illustrates how these operations work (please see Integer operations for a complete reference):
{ {extern "manool.org.18/std/0.6/all"} in -- Integer division and related operations
-- Heading
Out.WriteLine[" +8,+3 -8,+3 +8,-3 -8,-3"]
-- Integer division (truncating)
Out.WriteLine[" / " (8 / 3).Str["+6d"] (~8 / 3).Str["+6d"] (8 / ~3).Str["+6d"] (~8 / ~3).Str["+6d"]]
-- Remainder from Integer division
Out.WriteLine["Rem" 8.Rem[3].Str["+6d"] (~8).Rem[3].Str["+6d"] 8.Rem[~3].Str["+6d"] (~8).Rem[~3].Str["+6d"]]
-- Flooring division
Out.WriteLine["Div" 8.Div[3].Str["+6d"] (~8).Div[3].Str["+6d"] 8.Div[~3].Str["+6d"] (~8).Div[~3].Str["+6d"]]
-- Modulo (remainder from flooring division)
Out.WriteLine["Mod" 8.Mod[3].Str["+6d"] (~8).Mod[3].Str["+6d"] 8.Mod[~3].Str["+6d"] (~8).Mod[~3].Str["+6d"]]
}
For certain simple data types, the Str
operation can accept an optional string argument that is a C printf
conversion specification (excluding an initial
%
).
Output:
+8,+3 -8,+3 +8,-3 -8,-3
/ +2 -2 -2 +2
Rem +2 -2 +2 -2
Div +2 -3 -3 +2
Mod +2 +1 -1 -2
Quiz
Try to figure out what is going on here (you should have acquired all the clues after completing Lesson 3):
{ {extern "manool.org.18/std/0.6/all"} in
{ (WriteLine) Out -- What do Write/WriteLine return?
Out.Write["Hello"] ", " Out.Write[", "] ", " Out.Write["world"] ", " Out.Write["!"] ", "
Out.WriteLine[]
}
{ (WriteLine) Out -- What are Out, WriteLine, (+)?.. Or can we print "unprintable" stuff?
Out ", " WriteLine ", " (+) ", " (~) ", " Foo ", " Bar
}
}
Output:
Hello, world!
Nil, Nil, Nil, Nil, Nil
value/object, WriteLine, +, ~, Foo, Bar