Why Mango?
A Rich Set of Primitives

Mango has a rich set of primitive types. This includes logicals (2 state binary [on/off], 3 state boolean [true/false/unknown], or 4 state signals [1/0/x/z]), characters (ascii, utf, etc), base 2 registers (user specified size in bits), signal buses (each signal can have 4 states [1/0/x/z]), cardinal numbers (unsigned integers from 1 bit to 64 bits), integer numbers (signed integers from 8 bits to 64 bits), subranges (upper and lower bounds are user specified), rational numbers (a ratio of 2 integers), fixed point decimal numbers, floating point numbers, and unlimited precision integers.

In addition, mango allows control over how overflow is handled (throw an exception, limit the result, or wrap around to the begining), byte order can be specified within the type system (numbers can be marked as have network or host byte order, with conversions happening automatically), and cardinal and integer types have base 2 fixed point fractional precision.

Some primitive declarations:

type boolean of logical/boolean;  

** a fixed point unsigned integer with 4 bits of fractional precision
type fixed_point_cardinal of cardinal/32 {4} having overflow checked;

type float_32 of number/32;

type dollar of decimal {10:2};

type reg_16 of register {15:0};

type upper_32 of signal {63:32};

type ascii of char/ascii having overflow checked;

Complex numbers and Numeric Units

Mango has built in support for complex numbers and numeric units. Both are specified within the type system. Having complex numbers built into language allows literals to be specified in the real or complex plane, as well as giving the compiler a better chance at optimization. Having numeric units built into the type system increases the degree of static analysis that can be done upfront, lowering the incident of bugs. For example, declaring a floating point complex number and initializing it is accomplished as follows:


declare x of number/32 complex;

assign 5 + 5i into x;

A numeric units example:

declare 
  time of integer unit seconds;
  length of integer unit meters;
  acceleration of integer unit seconds per meters^2;

assign 
  30 into time;
  100 into length;

** this assignment works

assign time / length / length into acceleration;

** this doesn't

assign time * length into acceleration;

Coordinate and Matrix Types

Mango has built in coordinate and matrix types. This is adventageous because having them as part of the type system offers seamless integration and upfront static analysis, as well as holding out the potential for the better optimizations.

Some examples:


type point of integer/32 having overflow limited coordinate {x,y,z};

type matrix_2_2 of integer/32 matrix {2:2};

program test is
  declare p of point;

  assign [1,1,1] into p;

  incr p with [2,2,2];

  declare m of matrix_2_2;

  assign [[1,4], [2,3]] into m;

  magnify m by 5;   ** multiplies each matrix element by 5

Anonymous type products (tuples)

Mango has anonymous type products, or tuples. This offers convenience when simple pairs or triplets are needed. There is no need to declare a named structure. Also, tuples allow for multiple return values from functions. Some examples:


** a fixed string of length 64, composed of a integer-boolean pair

type string of [integer, boolean](64);

** A function type

type fn of [left: integer, right: integer] -> [integer, boolean];

Labeled literals and literal expressions

Mango allows the programmer to define thier own literals. In addition, mango provides literal expressions, ie expressions that are evaluated at compile time. This interpreted language allows for the creation of complex literals that are much easier to write and understand. In addition, combined with options and parameters, it allows for dynamic configuration of the program. Mango also provides literal macro's the enhance expressiveness.


literal page_width is 80;
literal seperator is (' ', page_width);  ** creates a string of 80 spaces 

literal name is "joebob briggs";

macro pick with a, b, c is
  min(a,b) + max(b,c);

literal test is pick(10,30,50);

literal file_seperator is
    #operating_system
  such_that
    "linux_x86"  yields '/',
    "windows_95" yields '\\',
    "solaris"    yields '/',
    "windows_xp" yields '/',
    else         '?';

literal version_string is 
  *major_version & "." & *minor_version & "." & *program_revision;

Clean Object Orientation

Mango provides object orientation. Unlike traditional approaches, however, mango differs in two important respects. First, like language such as Java or C#, it uses interfaces to achieve polymorphism. Second, it uses aggregation rather than hierachical inheritence to achieve code reuse.

The combination of interfaces and aggregates allows the programmer to avoid the problems that plague traditional object oriented implementations. First, the use of interfaces allows the programmer to avoid tie-ins to specific implementations. The programmers does not need to inherit any code to establish a type relation. Seperating type relations from code reuse free's the programmer from having to worry about functionality changes in superclasses in cases where the superclasses implementation is not used. Second, the use of aggregates provides a much cleaner and simpler means of code reuse. In this style, all classes that compose the aggregate are all named up front. There is no hierachy that needs to be searched to understand class behavior. Moreover, aggregation provides the benefits of multiple inheritence without all the complexity surrounding naming conflicts that plagues a hierachical implementaion. Finally, the use of aggregates and interfaces is semantically cleaner. Having hierachical type relations or inhertance trees forces the programmer to model thier enviroment in a hierachical way, even when such classifications are not applicable.

Mango also differs from other languages in how interfaces are related to classes. Instead of specifing that relationship as part of a classes definition, interface mappings are provided external to a class through a special link directive. Having these mappings made externally gives the programmer added flexibility as they can map interfaces to classes without redefining the class. Check out this link for an excellent explaination of the evils of implementation inheritence.

Some simple examples:


aspct asp_1 is
  method xyz with integer is
   ...;

  method abc with integer is
   ...;

aspct asp_2 is
  method def with integer is
   ...;

  method abc with integer is
   ...;

interface intfc is
   method abc with integer;

**
** Aggregate uses method abc from asp_2. 
** 

aggregate aggr is
   insert asp_1;
   insert asp_2;

link aggr to intfc;

Object properties

Mango provides getter and setter methods. This has proven to be syntactically convenient.


object test_obj is
  state
    ppp of integer;
    ttt of cardinal;

  property ttt of cardinal is
     query
       return max(.ppp, .ttt);
     update
       assign operand into .ttt when operand > 0;

  ** automatically creates simple query and update methods for variable 'ppp'.
  property ppp;  

program test is
  get obj of test_obj;

  assign 10 into obj.ttt;
  assign 20 into obj.ppp;

  print obj.ttt, eol;

Safe manual memory management

Mango utilizes manual memory management. Unlike most manual management schemes, it is completely safe. That is, it is not possible (unless one circumvents the type system) to have dangling pointers. To accomplish this, Mango uses a three pronged approach: type durations, sentries, and recycling.

Firstly, all types can have one of three durations: static, local, or arbitrary. Static types live on the heap and are never destroyed. Once created they exist for the duration of the program. Local types exist on the stack. They are only valid for the duration of their respective scope. Arbitrary tpes live on the heap, but can be destroyed. By specifying the duration of a type, the compiler can statically or dynamically check to ensure that no dangling pointer are created.

In mango, local types are checked statically. That is, any assignment is check so that a reference to a locally defined type cannot level it's scope. Static types would require no check, as they exist forever. Arbitrary types require a dynamic check to ensure they still exist.

For arbitrary types, the dynamic check is accomplished through the use of sentries. A sentry is a small data structure that accompanies the creation of an arbitrary type. Whereas an arbitrary type may be deallocated on the heap, it's a associated sentry exists for the duration of the program, and indicates if the type is still allocated. So for any read/write access of an arbitrary type, it's sentry must be checked to see if that type is still valid. To make this look up quick, all pointers to arbitrary types are fat. That is, the contain an address to the data, as well as an address to the accompianing sentry.

The problem such a scheme presents, of course, is a massive proliferation of sentries. To avoid this, Mango uses an arena allocation scheme. A group of arbitrary types all use the same arena for allocation. While new datums can be allocated arbitrarily, all items within an arena can only be dellocated as a group. Since deallocation is shared, only one sentry is needed for all objects in the arena. Having only one sentry per arena prevents thier proliferation. The use of arenas combined with the on chip caches of modern processors will mean that sentry lookups will have minimal cost.

In addition to arenas and sentries, mango provides an additional means of managing the heap space in safe way. This method is recycling. Recycling is used for statically allocated datums. When statically allocated objects are reclaimed, they are not deleted from the heap. Rather, they are simply reset to an intitial state and left on the heap. When the user requests a static object, the allocator looks for any returned datums of the same type, and "recycles" them. Since recycled objects never go out existence, there is no chance of dangling pointer problems.


procedure test_arb of a of integer' is
  print a, eol;

procedure test_loc of a of (integer)' is
  print a, eol;

procedure test_it of b of (integer)'' is
  declare x of integer;

  call test_arb with x;   ** The compiler will give a type error 

  call test_loc with x;   ** This works

  assign x into ^a;       ** the compiler will complain about passing a local type out of scope

program test is
   new global_arena of system::arena;    ** create an arena

   new y of integer' from global_arena;  ** allocate this pointer on the stack

   print y sentry address, eol;      ** print the address of the sentry 

   call test_it with y;

   get z of integer;  ** create a statically allocated integer

   delete z;

   get q of integer;  ** recycle the integer

   require z address == q address;  ** this should evaluate to true


Modules, Non-Hierarchical Naming, and Shallow Overloading

Each source code file is by default a module. Modules can include directories as part of thier name. so a module with a base name "io" in directory "sys" would have a full name of "sys/io". Each module also is part of a namespace. All symbols within a module belong to that namespace. If no namespace is provided, the default namespace is the base name of the module. So if module "sys/io" has no namespace provided, the default namespace for that module is "io".

While mango has namespaces, naming is not hierachical, but rather geometric. That is, names for symbols occur within a 4 dimensional grid. The dimensions being: namespace, keyword, extension and aspect. When modules are imported, this 4d space is collapsed into one dimension, utilizing the keyword and/or the extension of a symbol. Symbols can be accessed using either the four dimensional proper name, or it's 1 dimensional alias. This collapse from 4d to 1d space is the means by which Mango handles operator and keyword overloading.

Having "shallow" overloading (ie superficial), has the advantage in that it prevents conflicts when symbols are overloaded. Every symbol has a unique name by which it can be referenced.


module test in system;  ** test is part of the namespace io

procedure test is  ** procedure test's full name is system::test
  ...;

enum color is blue, green;

---------------------------------

module io;         ** located in the directory sys

import sys/io;     ** module importing itself...this will have no effect

import test;

procedure test is  ** the procedure test full name is io::test
 ...;

** named io::color@red, io::color@green, io::color@blue

enum color is red, green, blue;  

program doit is
  call test;  ** this will cause a unresolvable symbol name conflict
  call system::test;  ** this will work just fine

  print red;    ** this will print the word 'red'
  print color@red;  ** this will print the word 'red'

  print blue;  ** this will an unresolvable symbol name conflict
  print io::color@blue;  ** this will work

Incremental Compilation

Mango supports incremental compilation. Mango accomplishes this by generating header files automatically, and comparing the syntax of a modified source file with the previously generated header file. If the public aspects of the source have changed, then recompilation will occur. Mango also does dependency analysis. Only the files that need to be recompiled will be recompiled, pending changes in a modules dependencies.

To enhance incremental compilation, Mango include two import directives: include and import. The import directive is private; only the module that is importing can have access to the foreign module. The include directive is public. In this case, the included modules symbols are exported to other modules.

Built in Container Types

Mango provides a rich set of container types. Having containers withing the language rather than a library is a syntactic convience. There are a variety of operators that work only on collections. In addition, have container types built into the language offers enhanced opportunities for optimization. The container types mango offers include:

entry:  a node of a linked list
segment:  a combination of string and pointer
list: a list, with elements allocated in pages, FIFO order
stack: a (prioritized) stack
sequence: a list, with elements allocated in pages, LIFO order
queue: a (prioritized) queue
mask:  a bit mask
range:  a numeric range with upper and lower bound
set: a hashed set. Also doubles as a one-to-one map
table: a hashed one-to-many mapping.  Useful for rolling hash tables
group: a special collected used for comparisons
graph: used for frequency tables
Some examples definitions:
type entry of integer entry;
type segment of ascii segment {100};
type list of user_record list {256};
type stack of <user_record, cardinal> stack;
type sequence of user_record sequence {32};
type queue of user_record queue;
type mask of ascii mask;
type range of number range;
type set of ascii string set {19};
type table of <ascii string, cardinal> table {89};
type group of integer range group;
type graph of <ascii string, number> graph;
Functions and Constants

Though mango is a procedural language, it does provide constructs that give it a bit of a function feel. One of the constructs is the function. In contrast to procedures, which are a series of statements, functions are essentially a single expression, extended with two case constructs. One of these case's is condition, the other is conversion. For example:

function test with ascii+ string string, integer/32 yielding logical/boolean is
    s[x] when x > 0,
    s[y] when y > 0,
    "x"  otherwise
  of_type
    ascii+ string
  such_that
    "true" yields true,
    "false yields false
           else   unknown
  where
     s = operand.1, x = operand.2, y = x + 100;

In addition to functions, Mango also provides constants. A constant is a function that takes no arguments and is evaluated only once. The result of this evaluation is cached, and any subsequent calls use the cached value. Constants are a clean way of instatiating global objects (aka the once function in eiffel).

constant global_arena of arena is get arena;
Typed I/O

One of the intersting features of Mango is it's built in I/O. Mango allows I/O streams to be typed. This provides added safety and convienience. There are six types of I/O devices in mango:

file:  an operating system file
text:  a line oriented text file
stream:  an I/O stream
port:   a connection listener
database:  a database
document:  a document

Below is a simple example using text I/O. The compiler is automatically able to infer the type of variable "line" because the file handle tx is typed.


open tx of ascii text where path = "test.txt";

foreach line in tx over i do
  print [width=5] -> i, ":", line;

close tx;

I/O Formatting
Stream output is accomplished using the write operator. For example, a write operation looks as follows:
enum format_codes is eof, eol;

writer print_ascii with [ascii stream, (ascii+) string | format_codes] is
  ...;

program hello_world is
  write stdout with "hello", " ", "world", "!", eol;
The write procedure "print_ascii" is called for each argument.

Since Mango has tuples, supplying format information is very easy:


declare x,y of integer where x = 100, y = 10_000;

write stdout with "[", [[width=10, align=right], x], "   ", [[width=20, align=left], y], "]", eol;

In addition to write, mango include two other output operators: print and log. Print is similair to write, except that the target stream is standard out. The log operator is identical synatically to write. However, logging is atomic. All arguments are passed at once to the target procedure.

Mango also includes a read operator. The read operator is typed and straightforward:


program echo is
  declare i of integer;
  read (i) from stdin;

  read [a of ascii list] from stdin;

  print [[width=3], i], ":", a, eol;

Parameters and Options

Too help ease build customization, Mango provides Parameters and Options. Parameters are variables used by the compiler

Strong Typing

Iterators

Iterator expressions

Mutexes

Enumerations

Packet and Device Types

Record Abstractions

Build customization

when statement

when clauses in type declarations

Built in debug aids

pause, before, after atomic logging during statement assertion statements

Read/Write operators

Exception Handling

External symbols and subroutines

Precise layout control

Global Constructors

Bit Pointers

Nested functions

A rich set of assignment statements

Safe/Unsafe tagging for modules

Dynamic Character Strings

Integrated library construction

Runtime type identification

Simple polymorphism

Immutable, volatile and shared types

Strings, arrays and buffers