Lii
Paradigm(s) | declarative, functional, object-oriented |
---|---|
Designed by | User:Fergusq |
Appeared in | 2014 |
Type system | static |
Computational class | Turing complete |
Reference implementation | Unimplemented |
Influenced by | F# |
File extension(s) | .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); } }; } }