MANOOL
MANOOL is Not an Object-Oriented Language!”

Go to Lesson: 1 | 2 | 3 | 4 | 5

Lesson 1 — Tutorial

Updated:

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

  1. The first line -- ... is just a comment, which is ignored by the MANOOL translator (see later).

  2. The construct WriteLine[Out; "..."] on the second line resembles a function call in many languages, where the construct WriteLine would specify a function, and Out and "Hello, world!" would be the arguments.1 Here, as a side effect of an evaluation of this expression, the phrase Hello, world!, specified by a string literal, eventually appears on the standard output, specified by the argument Out.

  3. During compilation of the whole expression {{extern "..."} in ...}, all identifier definitions (particularly the one for Out)2 from the standard library module specified by the string literal "manool.org.18/..." are imported (injected) into the scope that follows the keyword in.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

Continue

Updated:
  1. In MANOOL ordinary functions are called procedures, and the construct WriteLine[Out; "..."] is more generally called an applicative expression (more on this later). In overall, MANOOL is a multiparadigm language with a functional core.

  2. …or rather their analogs in MANOOL called bindings

  3. {extern "..."} actually has its own meaning here (more on this later), but for now you can safely ignore this fact.

  4. The later example uses the notation that reflects abstract syntax trees (ASTs) for all syntactic constructs in MANOOL in the most straightforward way (like S-expressions in Lisps).

  5. Nonetheless, this particular example might fit better on a single line as we've already seen.

  6. We are switching here to the “Object-Oriented” notation and multi-line formatting just to dive into more real-world code examples as soon as possible.

  7. Similar to the case of multiple notations, this feature is rather unintentional but may be useful in some cases.

  8. The following alternative constructs actually make no sense otherwise (in most practical cases).

  9. Nonetheless, this does not affect a result of concatenation.

  10. MANOOL is a strongly typed language where 5 is not the same thing as "5", and implicit coercion has actually limited scope.

  11. MANOOL has simple operator precedence rules inspired in classic (Pascal-family) languages. In particular, unlike Lisp, Smalltalk, and APL, MANOOL does have precedence rules, but unlike ML and Haskell it has a limited set of operators that occupy few fixed precedence levels. MANOOL uses only 4 levels for infix operators.

  12. One disadvantage of the ~ operator is that it is also overloaded to mean Boolean/bitwise negation/complement for some data types, and one advantage is that it resembles more the traditional notation - (which cannot be used as-is in MANOOL because of grammar ambiguity introduction in such case).

  13. This is partly because MANOOL and its specification strive to be extremely accurate about the exact semantics and behavior of various operations in the language.