CORN Programming language

Tutorial


1. Simple program
2. First rules of programming in corn language
3. Functional programming
4. Object-oriented programming
5. Concurrent programming
6. Monitor class type
7. Selector class type



1. Simple program

Three steps to execute simple program in corn language:

  1. Write down code to some file (ie. hello.corn):

    
    class program1.class1
        when say => {
    	user.println("Hello!");
        }
    end
    
  2. Compile to binary representation (ie. hello.pack):

    $ corncmp -o hello.pack hello.corn
    
  3. Execute program:

    $ corn -pack hello.pack -exec program1.class1 say
    

... and you will see Hello! in console output.



2. First rules of programming in corn language

These are few things that differ corn language from other ones, they will be useful later, when you will learn other language features:

  • There are no defined starting point of application. It means, that you can implement many of them, and every time decide from where to start your application. In example, try to execute that program:

    
    class ex.ex1
        when sayHello => user.println("Hello!")
        when sayBye => user.println("Bye!")
        other message,ap => {
    	user.println("want me to: "+message+" ?")
        }
    end
    

    by typing:

    $ corn -pack thisprog.pack -exec ex.ex1 sayHello
    $ corn -pack thisprog.pack -exec ex.ex1 keepSmiling
    
  • There are no 'processor', which will go through code in linear way. Please be patient in that, because many things will be done concurrently (in parallel way). Don't even try to keep an order of execution inside your program, because it will be hard - this language is not designed for linear computing. Let corn machine do all parallel and synchronization tasks needed.
     

  • Everything is evaluated in lazy way. One of the main features of corn language is to provide lazy computing. It means that during computation not all of code will be evaluated, but only necessary parts for requested computing. See example:

    
    class ex.ex2
        when run => {
    	user.println("line 1");
    	user.println("line 2");
        }
    end
    

    That program prints only "line 2", because it is given in result of run function to the caller (in this situation: corn machine), but not "line 1", because nobody wants it's result.
    Looks weird? it will be clearly explained later. See the other example:

    
    class ex.ex3
        when run => {
    	let a,b,c
    	in {
    	    a = user.println("line 1");
    	    b = user.println("line 2");
    	    c = user.println("line 3");
    	    **c;
    	    return a
    	}
        }
    end
    

    The output of that program will be:

    line 3
    line 1

    Why? Because command: **c , will evaluate value c , and then value a will be given in result.
     

  • Everything is an object, each call is a named message function. The first one is a principal clue of object-oriented programming , the second one identifies code-flow in concurrency world packed as a messages to the objects. Of course, a message is also an object, and then can also response to received messages. There are no types of variables - object is an object - see example:

    
    let a,b,c
    in {
        a = false;
        b = "xyz";
        c = 2+2;
        a = 0x4E + c;
        b = b + a::asString;
        a = [ true::not, {}, null ];
        b = ifnull **a[2] 12345 else b + "Hello";
    };
    
  • Everything is a function. You can associate vector of params to any object. If it was a function expecting params to evaluate, then the result will be a result from calling that function, if it was a common object, then it will be treated as constant function. Examples:

    
        a = f(x);
        b = f(f(x))(y)(f(f,x))(x);
        c = null(x);
        d = {fun x => x + 1}(y);
        e = {fun x => fun y => x + y}(2)(2);
        f = {fun x,g => x + g(x)}(3,fun x => 1 - x)
        g = { a = b }(c);
    
  • Conditional instructions don't hold binary logic. It means (which can looks strange) that true is not the opposite of false . The truth is, that true and false are an objects, which represent states in logic, but, as you can guess, there can be many more objects, ie. null are not true nor false , it is just null .
    Therefore in corn language, sentences "is true" and "is not false" are not the same conditions. Example of conditional instructions:

    
    while!null {
        iffalse **{x:asBool} {
    	fortrue(i=0; **i < count; ++i) {
    	    [z,x] = z.get(i);
    	};
    	ifeq y,z null
    	else  {
    	    do { x = x::next } while!eq x,{z=y};
    	}
        }
        else false;
    } { ++x };
    
  • There are no atomic instructions. Each of concurrently worked threads can be made in many different orders of it's instructions. Even simple association x = y is not an atomic instruction, and can be shuffled with instructions in other threads. That is the next one feature of concurrency programming.
     

  • There are no memory operation instructions. There are no allocate nor release instructions, which will operate directly in memory (platform or virtual). Memory allocation is a next one task of corn kernel.
    You can think, that an objects are created, but not destroyed (there are even no destructors for objects), because some of objects can evaluating code independly of general control (ie. object of selector class can be treated as a separate processes).
     

  • Conditional association. Not only source object can be chosen for association, but also structure of destination. Examples:

    x = { if... y else z };
    { if... x else y } = z;
    { switch ...
      ... then x1
      ... then x2
      else xn
      end       } = { switch ...
                      ... then y1
                      ... then y2
    		  else yn
    		  end       };
     
  • Lazy builded execution. Executed program can change it own code. Yes, it is true. Running program may change definitions of its own classes in the time of execution, by loading apropriate binary classes into its own kernel of corn runtime machine.
    Binary representation of code is not strongly linked, and can be executed even, when it is uncompleted (ie. not everything is connected together).
    Name of the called function can be also prepared in the time of execution, ie:

    message = { "print" + "ln" }::asString;
    x = y & message("hello");
    

    has the same effect as:

    x = y.println("hello");
 


3. Functional programming

Corn language provides functional programming. You can create internal functions inside the other ones, return functions in response to evaluate them by other objects, execute the same functions with other parameters, etc. This example code shows internal functions:


class ex.ex5
    when start => {
	let f1 = fun p => { p + 1 },
	    f2 = fun p => { p - 1 },
	    incOrDec,x,y,
	    makeFunction
	in {
	    incOrDec = fun what => {
		iftrue what f1 else f2;
	    };
	    execFunction = fun f,o,howManyTimes => {
		whiletrue **howManyTimes > 0 {
		    o = f(o);
		    --howManyTimes;
		};
		return o;
	    };
	    x = 0;
	    y = incOrDec(true)(f2(x));
	    **user.println(y);	// here: y = 0
	    y = execFunction(incOrDec(false),5,10);
	    **user.println(y);	// here: y = -5
	    y = execFunction({fun z => 2*z},1,10);
	    **user.println(y);	// here: y = 1024
	};
    }
end

Another example shows passing functions around objects:


class ex.ex6
    when getFunction => {
	let functionGiver
	in {
	    functionGiver =
		new class
		    when get c => {
			// selecting function for response
			switch c in
			    '+' then { fun x,y => x + y };
			    '-' then { fun x,y => x - y };
			    '*' then { fun x,y => x * y };
			    '/' then { fun x,y => x / y };
			    else {
				fun => { "unknown operation: " + c };
			    };
			end;
		    };
		end;
	    let x,y, getFunction
	    in {
		z = functionGiver.get('+')(2,2);
		**user.println( z );

		getFunction = functionGiver.get ;  // new function
		z = getFunction('-')(2,4);
		**user.println( z );

		z = getFunction('/')(1,0);         // no error here
		**z;                               // divide by zero error

		return getFunction                 // function in response
	    }
	}
    }
end


4. Object-oriented programming

Programs in corn language are organized in objects. Each object have it's own definition of behaviour, named class . There can be many object of one class, but only one class per object.
A class can be either named, to be accessed externally, or unnamed, to be created internally.
Typical structure of class is:


class [ name ] [on parent class]
    [ init - constructor ]
    [ var - internal variables ]
    [ when - public functions ]
    [ private - private functions ]
    [ other - to handle uncatched messages ]
end

To create new object of named class, write:

x = new package.classname( ... params to constructor ...);

To create object of unnamed class, write:

x = new class ... end ;

One function entry ( when or private ) can catch few different messages, syntax:

when msg1|msg2|msg3 ...param variables... => result object

A class can inherit from the other one, it means, that every uncatched messages will be automatically passed to that other object. inheritance is not strongly defined, and it can be changed (switching parent of prepared object) in the time of execution. Parent objects are not created with object, but only when they are needed (in lazy way).
Therefore, inheritance relation groups objects in chains. A chain can be infinite or ended by cycle. Every chain contains at least 2 objects. Each object in chain can access another with special variables: self , current and super .

  • self - the pointer to the first object in chain

  • current - the pointer to the current (caller) object

  • super - the pointer to the next one object in chain (parent)

This picture illustrates meaning of that variables in typical situations:


In example of inherit chain contains two objects, lets look at the true and false system objects class declaration (from file: .../corn/basis/true.corn ):

class corn.basis.false on corn.basis.true
    init => pass false
    when and | isTrue | asBool | isNull | clone	=> current
    when not | nand | isBool | isFalse => super
    when or | xor o => o
    when nor o => o::not
end

class corn.basis.true on corn.basis.false
    init => pass true
    when or | isBool | isTrue | asBool | clone => current
    when not | nor | isFalse | isNull => super
    when and o => o
    when nand | xor o => o::not
    other msg,ap => {
	throw new corn.exception.uncatched(msg);
    }
end

And so, that inherit chain looks that:

   

If there will be no other entry in one of those classes, then some unexpected message send to one of those objects will make deadlock, loops infinity, searching destination object.


If no parent class are specify to be inherit from, then in that place will be created object of class corn.root (of course in the point of time, when will be needed).


If chain of objects is not ended by cycle, then it is say to be infinity. A chain can partly resists in few machines, and corn kernel job, is to pass uncathed messages down to that chain.


There is also special pass instruction, which allows to switch created object in it's constructor, by another one. This example code makes infinite chain (the only limit is the memory size):

class ex.ex7 on class
		    init => {
			pass new ex.ex7();
		    }
		end
    // methods...
end


5. Concurrent programming

Corn programming language is finally also an concurrent language (if not to say: it is mainly parallel language). It provides many different kinds of concurrency, from starting alone threads, internally catching messages by selector class, executing one code in concurrently way in the same context of variables, up to mixing everything together. It also offers many synchronization methods for accessing the same resources, by independent threads.


The first, and the simplest example is to divide one thread into two threads working concurrently with the same variables. Syntax of that construction is a block of instructions with special char ( # ) :

{# ...thread 1... ; ...thread 2... ; ... }

Examples of usage:


    let x = ... ,
        y = ... ,
	z = ...
    in {
	...
	{# { x = y } ; { x = z } };   // x will be either y or z
	x = {# y ; z };               // x will be either y or z

	{# { x = 1 ; y = x; };
	   { x = 2 };
	};                            // x will be either 1 or 2; y will be x or 1

	{# { x = y }; { y = x } };    // try to guess the result :)

	iftrue {# true; false } {
	    // 50% chance to be here
	}
	else {
	    // 50% chance to be here
	};

	x = y.message(# {x = y},{ x = z } );
	    // params will be prepared concurrently
	    // so, the possibilities are: (y,z),(y,y),(z,z),but never (z,y)
    }

The above instructions make concurrently worked threads, and wait for their execution. There is also instructions to make threads, but not waiting for them. Syntax are:

<# ...thread 1... ; ...thread 2... ; ... > // start many threads
< ...thread 1... >                         // start single thread

Example:


    when run => {
	let n
	in {
	    n = 0;
	    < loop {   // this prints out numbers, even when control leaves that function
		    n = n + 1;
		    **user.println(n);
		}; >;

	    return n  // returns last knowed number before leaving
	};
    }

There are also special behaviour of objects in this concurrency world, named monitor class and selector class described below. The synchronization toys used in libraries (like semaphores, mutexes, ...) are mostly an objects of that special classes.


6. Monitor class type

Monitor is a well known structure in theory of concurrent programming.
Structure of monitor class is:


monitor [ name ] [on ...]
    [ var - internal variables ]
    [ condition - list of condition objects ]
    [ init - constructor ]
    [ when - public functions ]
    [ private - private functions ]
    [ other - to handle uncatched messages ]
end

Monitor is a class, which guaranties exclusive access to its public functions. There can be a list of conditions, on which monitor can execute typical condition functions: wait , signal , signalall .
Entry other also will be executed in exclusive mode with public functions when , but private class functions don't need exclusive access.

Typical example can be an implementation of simple semaphore class:

monitor simple.semaphore

    var value = 0
    condition nonzero

    when P n => {
	times n,{
	    whiletrue **value == 0 {
		wait nonzero
	    };
	    --value
	};
	return true;
    }
    when V n => {
	times n,{
	    ++value;
	    signal nonzero
	};
	return true;
    }
end

There are also instructions to release and lock monitor inside code, these are: leave and enter , ie:

monitor ex.ex8
    when message => {
	// ... have monitor lock
	leave {
	    // ... don't have monitor lock
	    enter {
		// ... have monitor lock
	    };
	    // ... don't have monitor lock
	};
	// ... have monitor lock
    }
end


7. Selector class type

Selector is a class, which receives messages inside own code, using special instructions. It can decide which message it will receive, and also decide in which order.
Structure of selector class is:


selector [ name ] [on ...]
    [ var - internal variables ]
    [ start - selector startup entry ]
end

There are no public nor private entries for messages, and only one startup entry, which will be executed when object will be created (with params passed to the constructor).
Those special instruction for internally receive messages are accept and acceptany .
Syntax looks like a public function:

accept msg1|msg2|msg3 msg,ap => returned object
acceptany msg,ap => returned object

First one instruction accepts incoming message with chosen name, the second one accepts any new incoming message. Parameter msg will be set to the name of incoming message, and parameter ap will be set to the array of message params.
When this instruction is done, a returned object will be passed as a response to the caller.


Example shows a selector class, which connecting callers in pairs. Callers call a message and expect as a result another caller, by execution:

    otherObject = connector.getAnother(myObject);

That selector class can be implemented like that:

selector ex9.connector
    start => {
	let caller1, caller2
	in {
	    loop {
		accept getAnother msg1,ap1 => {
		    // one caller is accepted and wait for response
		    caller1 = ap1[0];
		    // lets wait together for the other one
		    accept getAnother msg2,ap2 => {
			// another caller is accepted
			caller2 = ap2[0];
			// so, we've got now two waited callers

			// free the second caller, with 'caller1' in response
			// puting value as a result of select instruction
			caller1
		    };
		    // free the first one caller, with 'caller2' in response
		    caller2
		}
		// ok, we connected another pair of callers
	    }
	    // ... and again
	}
    }
end

Another example is a simple mutex object used in synchronization:
(from file ./yellow/sync/mutex.corn ):

selector yellow.sync.mutex
    start =>
	loop {
	    accept enter|PB , => {};   // accept one enter message
	    accept leave|VB , => {};   // accept one leave message
	}
end



  Corn version: 2.8.2