Lii

Lii is a declarative object-oriented mildly esoteric language created by User:Fergusq in 2014. It is named after the fictional 31st century tea company Lii Tea (from a Sci-Fi book, I forgot the name). The language is a mix of functional and object-oriented programming.
== Overview ==
In Lii, all objects are immutable and do not contain any fields. This means that all instances of a class are equal. New "values" are created by declaring new anonymous classes. All methods are side effectless.
A traditional Hello World:
 Main { main -> BS { "Hello World!"; } }
Here, the class Main with the method main is declared. The method returns a BasicString (BS for short) object. The interpreter should output that string. This is the only way to output things in Lii.
== Classes and methods ==
Classes always have a super class (default Object). Methods always have a return type.
For example, a class that represents a function (for implementation of the Natural class, see examples):
 Function {
 call(Natural val) -> Natural;
 }
=== Anonymous classes ===
The ^ operator is used to create a new anonymous class instance.
 ^Function {
 call(Natural val) -> Natural {
 val.increment;
 }
 }
== Built-in classes ==
Lii has a small standard library of important classes.
=== Object (O) ===
The base class of all classes. toString is an abstract method, calling it will raise an error.
 Object : Object {
 class -> Class;
 toString -> BS;
 }
=== Class (C) ===
Represents a class. The base class of all metaclasses. Can be used with the ^ operator to create a new anonymous class.
 Class : Object {}
=== BasicString (BS) ===
Used for output. toString returns the string.
 BasicString : Object {
 append(BasicString str) -> BasicString;
 toString -> BasicString;
 }
== Computational class ==
Lii is Turing complete, for example here are the implementations of SKI combinators:
 Function {
 	call(Function f) -> Function;
 }
 I : Function {
 	call(Function x) -> Function {
 		x;
 	}
 }
 K : Function {
 	call(Function x) -> Function {
 		^ Function {
 			call(Function y) -> Function {
 				x;
 			}
 		};
 	}
 }
 S : Function {
 	call(Function f) -> Function {
 		^ Function {
 			call(Function g) -> Function {
 				^ Function {
 					call(Function x) -> Function {
 						f.call(x).call(g.call(x));
 					}
 				};
 			}
 		};
 	}
 }
== Related languages ==
=== CLii ===
Influenced by F#, CLii is a less esoteric variant of Lii. Although syntax has been completely redesigned, the core runtime system is same as Lii's.
 Main {
 main ->
 "Hello, world!"
 }
Another example:
 Main {
 	.factorial(n) ->
 		if n = ^Natural{} then ^Natural{}.inc else .factorial(n.dec) * n
 
 	main ->
 		let test1 = .list()
 		let test2 = .fib(10).toString()
 		let test3 = .factorial(6).toString()
 		""+test1+"\n"+test2+"\n"+test3
 	
 	list ->
 		let list = ^List{}.cons("world").cons("Hello, ") .. "!"
 		list.toString()
 	
 	fib(i) ->
 		if i <= 1 then i else .fib(i - 1) + .fib(i - 2)
 }
== Examples ==
=== Hello world ===
 Main {
 	main -> BS { "Hello world"; }
 }
=== Fibonacci sequence ===
Calculates fib(6).
Main {
	main -> BS {
		self.fib(^Natural{}.increment.increment.increment.increment.increment.increment).toString();
	}
	
	fib(self m, Natural n) -> Natural {
		// Please note that 0.decrement returns 0.
		n.decrement.if(^Factory{
			create->O {
				m.fib(n.decrement).add(m.fib(n.decrement.decrement));
			}
		}, ^Factory{
			create->O {
				n;
			}
		});
	}
}
=== Useful classes ===
==== Natural numbers via linked lists ====
Factory {
	create -> O;
}
Natural {
	link -> Natural;
	
	if(Factory f, Factory o) -> O {
		o.create();
	}
	
	toString -> BS {
		"";
	}
	
	increment(self i) -> Natural {
		^Natural {
			link -> Natural {
				i;
			}
			if(Factory f, Factory o) -> O {
				f.create();
			}
			toString -> BS {
				"*".append(i.toString());
			}
			decrement -> Natural {
				self.link;
			}
		};
	}
	
	decrement -> Natural {
		self;
	}
	
	// Addition and subtraction using if function. There are possible better ways to implement using linked lists
	add(self a, Natural b) -> Natural {
		b.if(
			^ Factory {
				create -> O {
					a.increment.add(b.decrement);
				}
			},
			^ Factory {
				create -> O {
					a;
				}
			}
		);
	}
	subtract(self a, Natural b) -> Natural {
		b.if(
			^ Factory {
				create -> O {
					a.decrement.add(b.decrement);
				}
			},
			^ Factory {
				create -> O {
					a;
				}
			}
		);
	}
}
==== Tape via stacks ====
 Main {
 	main -> BasicString {
 		^ Tape {
 			// Initialize tape, push some zeros to the stacks
 			stack1 -> Stack {
 				^Stack{}.push(^ Natural)
 					.push(^ Natural)
 					.push(^ Natural);
 			}
 			stack2 -> Stack {
 				^Stack{}.push(^ Natural)
 					.push(^ Natural)
 					.push(^ Natural)
 					.push(^ Natural);
 			}
 		// Example usage
 		}.right().increment().increment().left().right().current().toString();
 	}
 }
 
 Entry {
 	prev -> Entry;
 	next -> Entry;
 	obj -> Natural;
 }
 
 Stack {
 	first -> Entry;
 	push(self stack, Natural o) -> Stack {
 		^ stack.class() {
 			first -> Entry {
 				^ Entry {
 					next -> Entry { stack.first(); }
 					obj -> Natural { o; }
 				};
 			}
 		};
 	}
 	pop(self stack) -> Stack {
 		^ stack.class() {
 			first -> Entry {
 				stack.first.next;
 			}
 		};
 	}
 }
 
 Tape {
 	stack1 -> Stack { ^ Stack; }
 	stack2 -> Stack { ^ Stack; }
 	
 	current(self tape) -> Natural {
 		tape.stack2.first.obj;
 	}
 	
 	left(self tape) -> Tape {
 		^ tape.class() {
 			stack1 -> Stack { tape.stack1.pop(); }
 			stack2 -> Stack { tape.stack2.push(tape.stack1.first.obj); }
 		};
 	}
 	
 	right(self tape) -> Tape {
 		^ tape.class() 
 			stack1 -> Stack { tape.stack1.push(tape.stack2.first.obj); }
 			stack2 -> Stack { tape.stack2.pop(); }
 		};
 	}
 	
 	increment(self tape) -> Tape {
 		^ tape.class() {
 			stack2 -> Stack {
 				^ Stack {
 					first -> Entry {
 						^ tape.stack2.first.class {
 							obj -> Natural {
 								tape.stack2.first.obj.increment();
 							}
 						};
 					}
 				};
 			}
 		};
 	}
 	
 	decrement(self tape) -> Tape {
 		^ tape.class() {
 			stack2 -> Stack {
 				^ Stack {
 					first -> Entry {
 						^ tape.stack2.first.class {
 							obj -> Natural {
 								tape.stack2.first.obj.decrement();
 							}
 						};
 					}
 				};
 			}
 		};
 	}
 }
==== Linked list ====
Main {
	main -> BS {
		^List{}.cons("world").cons("Hello, ").append("!").toString();
	}
}
// An empty pair
List {
	car -> Object;
	cdr -> List;
	
	// = [o, l]
	cons(self l, Object o) -> List {
		^Pair {
			car -> Object { o; }
			cdr -> List { l; }
		};
	}
	
	// = [o, []]
	append(self l, Object o) -> List {
		// This is same as cons, because the list is empty. See Pair for append for non-empty lists.
		^Pair {
			car -> Object { o; }
			cdr -> List { l; }
			// append = [o, [b, []]]
		};
	}
	
	toString -> BS {
		"";
	}
}
// A non-empty pair
Pair : List {
	toString -> BS {
		self.car.toString().append(self.cdr.toString());
	}
	// = [o, l.a(n)]
	append(self l, Object n) -> List {
		^ self.class {
			cdr -> List { l.cdr.append(n); }
		};
	}
}
Category:LanguagesCategory:Object-oriented paradigmCategory:Declarative paradigmCategory:Output onlyCategory:Turing completeCategory:2014