Open All Sections     Close All Sections

Lima (pronounced "Lie-muh") is a high-level optionally-typed platform-independant language. It allows seamless language extension via parsers, and automatic optimizations via optimizer modules.

Lima is made to be easy (and terse) to write. Like separating the concerns of style from structure with HTML and CSS allows much easier redesign of pages, Lima allows separating the concerns of optimization from program operation, allowing much easier optimizations to be built and reused. Optimizations are intended to be handled by the compiler (and third party optimizer plugins) - a Lima program should never have to be hand-optimized. Input behavior (probability of certain types of inputs) can be specified so that the optimizers can take all things into consideration. All you should need to do is specify the behavior of your program. Lima is designed to give you that freedom.

Lima is intended to be zero-configuration. Make files or their like aren't needed to compile a single project. Settings that require compiler flags or similar in other languages would be included in the source files themselves - so that there is only one source of truth (no pun intended).

Without further ado, some code examples:

target[x86.win32 ppc.mac jvm] ;; Cross compiles - the 'target' declares target platforms and formats to compile to var websocket = load['websocket'] ;; external module loading string lastWords = 'Goodbye World.' ;; module variable - similar to, but safer than, global variables y = 5 ;; implicit variable declarations ;; many operating systems use 'main' as the for the program (e.g. Windows) main = fn: var myVariable ;; Variable declaration myVariable = 'Hello?' ;; Assignment int[x=8 y] ;; declaration and initialization z = 5 ;; throws exception - no implicit variable declartions in functions bool can_underscores_be_used_in_variables=true ;; but probably shouldn't very often GunIt[lastWords] ;; function call if y < x: ;; Conditions x += y else: x -= y var numbers = {1 2 3 4 5} ;; Lists/arrays var cubedList =[v^3] ;; List comprehensions var evens = numbers[[v%2==0]] ;; List slices (here getting a list of the even values) var square = fn x: ;; first class nested functions ret x^2 ;; exponentiation operator var cat = ;; Objects { legs = square[2] ;; implicit member declarations sound = 'meooow' 'siblings': {'Mr TFeathres' 'Furkrisake' 'Pawscat'} {'a' 'b' 3}: 'paws' ;; objects/lists can be keys makeNoise = fn: ;; object methods wout sound } var race = fn winner otherRunners: ;; Splats (here, being used for varargs) wout['The winner is: 'winner@] ;; @ stands for a newline - like \n in C wout['Runners up: '@] df otherRunners runner: ;; for-each style loops wout[runner@] ;; Destructuring assignments (x gets cat.legs - the value 4, myvariable gets cat.bam[1] - the value 2) {legs=x bam={1:myVariable}} = cat

Lima has a few defining attributes:

And also a couple interesting features:

Lima is also designed so that every construct in the language can be described by the programmer. Not only are functions first-class, but so are flow-control constructs like if and while, and attribute constructs like static, private, and const, and modules themselves (since modules represent objects). This give the programmer incredible power to build constructs that blend seamlessly with the core language, but it also forces the language to be self-consistent - the syntax of Lima's language features aren't special cases.

Modern languages fall into two general categories:

Domain-specific languages are sometimes made to bridge this gap, but it comes at the sacrifice of flexibility and generality. Lima is a language that bridges this gap without making sacrifices. It can do this by completely severing the tie between the code describing your program and the code describing how to make your program run fast. Code written in languages today have descriptions and optimizations that are hoplessly coupled.

In Lima, these concerns are decoupled by:

Not only are optimization extensions available, but since Lima provides few avenues for the programer to hand-optimize their code, programmers are forced to write properly moduler optimization extensions that can be reused by anybody. This philosophy allows Lima to be maximally expressive, since the language doesn't need to provide mechanisms for the programmer to hand-optimize code. Deciding whether to used a LinkedList vs ArrayList, Quick Sort vs Merge Sort, recursion vs iteration, memoization, caching - can all be left to optimization plugins, at worst, ones you write yourself.

In a given lima source file, which from now on I'll call a , you can define and initialize variables in a (similar to the "global" scope). Variable declarations include function definitions and object initializations. You can declare a variable anywhere you could use a variable, including in functions (e.g. setToFour[int[newInt]]).

Variables not in a must always be defined with a type. Variables that are defined in the , have the implied type var if none is specified (which is a type which includes all values except nil - similar to javascript's "var" and Java's "Object" type). The order of variable initializations doesn't matter in the , but there can obviously be no circular definitions.

Variable names can contain the characters a through z, A through Z, 0 through 9, and _ (underscore). They can't start with a number.

Inside the , every line must be a variable/member definition (or if statement).

Note that an uninitialized variables may not be used until they've been set. Also, while module variables (variables defined in the of a module are similar to global variables, they are not really global since they exist only in the module's scope. This is explained further in the section on objects.

var variable ;; This is a module variable declaration anotherVariable = 5 ;; Variable declaration initialized to 5 anotherVariable = 8 ;; This would throw an error because it is an implicit variable declaration of a variables that is already declared.

Also, variables declared in methods must be case-insensitive-unique in its scope, which means that no method variable can have the same case-insenstive name in the same scope.

var y=5 var y=6 ;; errors because y can't be declared twice in the same scope var Y=7 ;; errors because Y is the same case-insensitive variable name as y var x = { var y ;; is legal, but this y must be accessed with either or , and the original y cannot be accessed at all within this object var x ;; is also legal, but has the same restrictions as y var[a=b=c=90] ;; three variables declared and set to 90 var[d=1 ;; two variables declared over multiple lines e=2 ] var: f g ;; two variables using colon syntax var: h=1 ;; three variables using colon syntax on separate lines (with indendation) i=45 j='something else' var z = fn: var x ;; errors because x is already declared (actually in two places: the name of the object holding this function and there is a member with that name as well) fn! f = fn int[x y]: ;; errors because both x and y have already been declared above ret x+y }

Most of the time, the entry point to the program will be a function called 'main' (following the convention of C for operating systems that use a single entry-point). However, the entry-point is not neccessarily 'main' and some operating systems (like Android) even have multiple program entry points.

The entrypoint has the following parameters:

  1. A list of command-line arguments.
  2. signals - the list of signals sent to the program
main = fn {arg1 arg2} signals: con.out.=cat['Sum: ' arg1+arg2 ''@] change[signals]: con.out.=cat["Got a new signal: " signals.sort[-k][0] ''@]

As you've probably already seen, comments in lima begin with two semi-colons: ;;.
;; Single-line comment (everything after it on the same line is a comment).
;;[ ;;] Span-comment. Can be used as a multi-line comment, or a comment within a line. Span-comments can nest, meaning that the ;;[ and ;;] follow normal bracket-matching rules. This means that you can comment out a small part of code, and then comment out a larger part of code that includes the smaller commented out part - without rearranging the comment markers.
var x ;;[ a variable ;;] = 4 ;;[ var y var z ;;[ used somewhere else ;;] ;;]
All comments are treated exactly like whitespace. So, for example, abc;;[;;]def is two tokens, abc and def, not one abcdef token.

Flow control in lima is relatively similar to other major languages. You have an if construct:

if something == 5: ;; do something

a for-each style loop construct:

df aList oneValue: ;; do something with each value

a while-style loop construct:

int x = 0 while x < 5: ;; do something until x is greater than 4

and exception throwing and catching:

try: ;; do stuff throw "Oops, a problem" e: ;; catch and handle exception

There are also some more interesting flow control options, like lightweight thread syntax and event handling:

var done = false var file = thread: ret downloadLargeFile['someFileName.wtvz' 'utf-8'] ;; runs this asynchronously file.wait: wout["It's finally done!"]

The loop constructs have scoping rules like in C - variables created inside a df statement, for example, will only exist within the loop block. However, many (like if and try) do not define new scopes and thus variables defined within them can be used outside of them. In fact, the only core blocks that create new scopes are df, while, thread, and fn. All the flow-control constructs are detailed in the section Predefined Custom Functions (tho not all things there are neccessarily flow-control).

All of the flow control constructs can either be written using square-brackets as block delimiters:

if[ i==5: ;; do something ]
or with indentation (whitespace) as block delimiters:
if i==5: ;; do something

Bracket-delimited blocks work as expected, but whitespace delimited blocks are more forgiving then their python counterparts. Here's how they work: When a block is opened (by a custom function name like if), anything that is more indented than the line in which the block is opened is treated as inside the block. Usually a block will have some data on which to operate preceding a colon. And tabs aren't allowed to be used as indenting (although they are allowed for non-relevant whitespace).

if ;; block opened i == 5: ;; indented *further* than if (opening sub-block) doSomething[] ;; indented further than if (only indented further than the condition for easier reading ; ) else: ;; even else needs to be indented *further* than if doSomeMoreStuff[] if x==5: if y<70: doSomething[] ;; this is run if x==5 & y<70 y < 100: doSomeMore[] ;; this is run if x==5 & y<100 ;; note that theres no way to add another condition on the first if - with x==5 without putting "if y<70" on a new line if i==5: doSomething[] else: dontDoThis[] ;; this is actually a syntax error because else isn't inside the if block if i==5: doSomething[] ;; as ugly as this is, all 6 of these are in the same block, and is valid code doSomething[] doSomething[] doSomething[] doSomething[] doSomething[] ;; more ugly but valid code ;; only one of the function calls will happen if i==5: doSomething[] i<10: doSomething2[] else: doSomething3[] ;; even more ugly but valid code ;; this has the same behavior as the above example if i==5: doSomething[] i<10: doSomething2[] else: doSomething3[]

Personally I like delimiting blocks with whitespace, but sometimes bracket delimited blocks are neccessary for reasonable code construction.

;; lima's alternative to C's ternary operator (?:) var x = 'Lets '.cat[if[run: 'go running' else: 'walk'] ' to the park'] ;; this would be silly (but possible): var x = 'Lets '.cat[if run: 'go running' else: 'walk' ' to the park' ;; note that for this to work, the value on this line must *not* be any more indented than this, otherwise it would be under the else ] ;; a shorter but still silly alternative: var x = 'Lets '.cat[if run: 'go running' else: 'walk' ' to the park'] ;; the value on this line must still be no more indented than this

Operators are the basic form of interaction with values. An operator can be made of any combination of the symbols:

or can be made with brackets (this is described in the section on operator overloading). Operators appearing next to eachother must have whitespace between them. Note that # is a special case - this is explained in the section on strings.

Binary Operators

Binary operators are the most familiar type of operator. Uses infix notation. A binary operator may not written directly next to it's left or right value unless its written directly between both.

This is primarily so that there is a clear syntactic difference between binary and unary operators, so that the compiler can tell where a statement ends and the next one begins.
2*5 ;; fine 2 *5 ;; throws exception 2* 5 ;; throws exception 2 * 5 ;; fine 2 * ;; .. 5 ;; fine

Unary Operators

Unary operators can either be pre-operators or post-operators - either preceding or following the value they operate on, respectively.

Unary operators must have no spaces or newlines between the value they are operating on. If it is ambiguous whether an operator should be a pre- or post- fix operator, it is treated as a binary operation if a matching one exists, and if no matching one exists, an exception is thrown.

a! b ;; postfix exclamation operator a !b ;; prefix exclamation operator a!b ;; exception a ! ;; exception B var x = a ! ;; exception - its too far away var y = ! a ;; same exception var z = {1 -5 9} ;; treated as {1 (-5) 9} var w = {1-5 9} ;; treated as {(1-5) 9} var h = a-b ;; binary minus var h2 = a - b ;; also binary minus var h3 = a - b ;; also binary minus var h3 = a -b ;; unary minus

Destructuring Assignment

A special case of assignment, a desctructuring assignment looks like an object-literal lvalue (on the left side) assigned to a value that has a similar structure to the object-literal lvalue. The keys in the lvalue indicate what part of the structure of the rvalue to grab. If the entity at a key is a variable-name, that variable will be assigned to the value at the corresponding key in the rvalue. If the entity at a key is another object-literal, then the process is repeated with inner-object members (of both the lvalue and corresponding members of the rvalue).

var[a b c] {{a b} 'den':{cargo=c}} = {'hi' 'den':{cargo=65}} ;; same as a='h' b='i' c=65 {cat=a dog=b} = {"cat":50 "dog":190} ;; same as a=50 b=190 {a b c} += {1 2 3} ;; same as a+=1 b+=2 c+=3 {var[r1 r2}} ~> {'a' 'b'} ;; points r1 at 'a' and r2 at 'b'

Note that all the values in the rvalue are evaluated before executing the assignments, and therefore it can be used for inline swapping.

var[a='hi' b='lo'] {a b} = {b a} wout[a b] ;; prints 'lohi'

Which has the same affect as:
var[a='hi' b='lo'] atomic: fn: a=b fn: b=a wout[a b] ;; prints 'lohi'

Destructuring assignments work with any assignement operator and the reference pointing operator (~>), but work with no other operator. This feature is taken from Coffeescript and is a generalization of sequence unpacking in Python.

These are the order of operations for all binary operators defined by Lima's core language. Unary operators do not have arbitrary order of operations like binary operators, they are always at operator order 1.
(everything is evaluated from left to right except Assignment operators and the ^ operator)
Order 0 Evaluated First Dereference Operators and parens () . [] [[]] [? ] [[? ]]
1 . Unary operators - ! - ? ...
2 . Top-level (arithmetic) ^
3 . Mid-level (arithmetic) * / // % mod cross
4 . Bottom-level (arithmetic) + -
5 . Range Operators ...
6 . Comparison == <> ?? != !<> !?? > < >= <= >>   <<
7 . Upper-level (boolean) & !$ !&
8 . Lower-level (boolean) $
9 Evaluated Last Assignment (evaluated from right to left) and the nil-coalescense operator (evaluated from left to right) = -= += *= ^= /= %= ~> //= |

The following operators are here to give a general overview of the operators defined in the core language, but the operators are defined in full detail by the objects of which they are members.

Binary Operators

= Changes the value of a variable.
~> Sets a variable as a reference to another variable.
| The . Returns a if a is not nil, and b otherwise. Similar to the "Elvis operator"

. Member access.
+ Normal addition, matrix/vector addition..
- Normal subtraction, matrix/vector subtraction.
* Multiplication, matrix product.
/ Division, product of matrix inverse.
// Discrete division.
% Modulus, extended-space character.
^ Exponentiation.
! Factorial.

& Logical and, set-conjunction.
$ Logical or, set-union.
!$ Logical xor, set exclusive-disjunction.

== Equality.
!= Opposite of ==.
<> Set equality.
!<> Opposite of <>
< Less than, proper subset, naturally ordered preceding string.
> Greater than, proper superset, naturally ordered proceding string.
<= Less than, improper subset, naturally ordered preceding string or equal string.
>= Greater than, improper superset, naturally ordered proceding string or equal string.
<< >> <<= >>= Key-aware comparisons.

.. Range operator for numbers and strings. For example 2..10 returns a list with elements 2 through 10.
?? Tests if two values are the same data (e.g. two references that point to the same value).
!?? Opposite of ??

, Concatenation of strings

Unary Prefix Operators

- Negative.
@ String newline.

Unary Postfix Operators


Splat operator.


Safe navigation operator (postfix) - allows safe traversal of members without having to check them for nil. Also creates types that can hold nil.


Accesses an object representing a reference (rather than the value it points to).


Interface operator - creates a type defined by the object's interface. Also used for creating special string characters.





@ String newline.
operator[ order order backward chained ] = function

When used as an lvalue, operator is used for binary operator definition, redefinition, or overloading. Defines the based on the function.

Humans process symbols and pictures far faster than words. Similarly, programs that read like an essay can be far harder to read and understand, no matter how language-like they may sound. This is why nobody likes word-problems in school. Operator overloading helps us visualize what a statement does far better than a series of function calls could.

For binary operators defined with one parameter, the operator is defined the same way when the object is on either side of the operator (note that this doesn't neccessarily mean the operator is commutative). The parameter represents one operand and the other operand is assumed to be this.

a = { var x = 2 operator[ + ] = fn other: ret x+other } var c = a+5 ;; c gets set to 7

For binary operators defined with two parameters, the first parameter represents the left operand and the second parameter represents the right operand. The parameter list must contain this as at least one paramter, which indicates that the current object is in that operand position.

order defines what operator precedence it has - the operator has the same precedence as the other operators in that order numbers. New orders can't be created.

The keyword backward indicates that the operator is right-to-left associative (like =). Operators are left-to-right associative without that keyword.

Just like for methods, when an operator is redefined on an object, only that definition is used - it doesn't retain inherited operator definitions, even if those definitions could handle arguments that the new definition can't handle. Also, operator definitions are always , so once its defined it can't be redefined. If it is ever ambiguous which object's operator should be used, an exception will be thrown. Ambiguous operator definitions inside the same module will throw an exception as well (even if they aren't called).

a = { var x = 2 operator[+] = fn this b: ret x+2 } b = { var x = 5 oprator[+] = fn a this: ret x+a-1 } var c = a+b ;; throws an error because it doesn't know which code to use var d = b+a ;; throws an error because neither operator definition can be used

The following operators can't be overloaded:

  • ~
  • ??
  • |
  • ~>

Creating chaining operators

The keyword chained indicates that the operator will take two extra parameters on the end (three or four parameters total, depending on what form you choose). The first extra parameter holds a string containing the previous operator in the chain, and the second extra parameter holds the value sent from the previous chain. If the operation is the first in the chain, both extra parameters will be nil. An operator will only chain to operators in the same order as it.

The operator should return a list where the first element is the normal return value, and the second element is the value to pass to the next chained operator (if there is one).

var Comparable = { var value make val: value = val operator[< chained] = fn this Comparable! x prevOp chainedValue: ret lessThan[this.value x.value prevOp chainedValue] Comparable! x this prevOp chainedValue: ret lessThan[x.value this.value prevOp chainedValue] this x prevOp chainedValue: ret lessThan[this.value x prevOp chainedValue] x this prevOp chainedValue: ret lessThan[x this.value prevOp chainedValue] private: lessThan = fn left right prevOp chainedValue: if '<' != op $ chainedValue == nil: ret {left<right right} else: ret {chainedValue<right right} } var a = Comparable[10] a < 5 ;; returns false a < 100 < 1000 ;; returns true 5 < a < 9 ;; returns false

Overloading the equals operator

The = operator is a special case. Defining this operator with no parameters defines how to copy the object when it is the . Defining it with one parameter defines how to overwrite the object (when the current object is in the ) - the parameter contains the . When defining overwriting, note that using this = x would cause a circular reference, so if you need a way to set this, you can use meta[{}].operators['='][this x].

var object = { operator[ = ] = fn: ret this ;; normal copy operator, copies itself normally value: self = value ;; normal assignment, overwrites the object itself with the new value } ;; object2 hijacks the normal use of the equals operator var object2 = { var x = 5 operator[ = ] = fn: ret {1 2 3} ;; returns a list instead of itself as its copy value: x+=value ;; modifies the state of the object ;; does not actually overwrite the object }

Overloading the reference operator

Overloading ~> is very similar to overloading the equals operator.

Overloading the dot operator

The dot operator is special case in a couple different ways:

  • Tho it's a binary operator, it cannot be defined for anything except this being on the left side
  • It has access to the name on the right side, if the right side is a simple name

Overloading the dot operator would be done like this:

operator[ . ] = fn name value: ;; statements
Where name contains the name on the right side, or nil if the right side isn't a simple name, and value contains the value of the right side, or nil if the right side is a name but not a defined variable.
var someObject = { operator[ . ] = fn name value: wout['name: 'name', value: 'value] } var x = 5 someObject.x ;; prints "name: x, value: 5" someObject.(1+2) ;; prints "name: , value: 3" - the blank being nil someObject.y ;; prints "name: y, value: "
Note that custom functions passed into the dot operator don't need to use the reference-access operator because the name of the "member" is passed to it, and the value is grabbed based on the name.
var someObject = { operator[ . ] = fn name value: wout['name: 'name', value: 'value] } var z ~> 5 someObject.if ;; prints "name: if, value: if" someObject.z ;; prints "name: z, value: 5" ;; prints "name: int, value: int"

Overloading the bracket operators

Bracket operators are special in that they don't have the same flexibility that other operators have. A bracket operator can either be defined as a single-bracket operator ([) or double-bracket operator ([[), and may have an arbitrary operator attached to the single- or double- bracket operator. The function it is set to can take any number of arguments.

var someObject = { operator[ [[ ] = fn key: ret key+5 ;; double bracket operator operator[ [ ] = fn ;; bracket operator taking different numbers of arguments key: ret key+5 a b c: ret a+b+c operator[ [[! ] = fn key: ;; double bracket operator with an exclamation mark ret key+5 operator[ [[#@& ] = fn x: ;; some crazy operator ret x*10 operator[ [[#@-*- ] = fn: ;; another crazy operator ret 1 } someObject[4] ;; returns 9 someObject[[4]] ;; returns 9 someObject[[1 2 3]] ;; returns 6 someObject[[!4]] ;; returns 9 someObject[[#@&2]] ;; returns 20 someObject[[#@-*-]] ;; takes no arguments, returns 1

If there is ever ambiguity as to whether the operator within brackets is part of the bracket-operator itself, or whether it is a unary prefix operator, an exception will be thrown. In these cases, whitespace between the operator and the value is enough to disambiguate.

var object = { operator [ [! ] = fn bool x y: ret x & !y } object[! true false] ;; returns true object[!true false] ;; throws exception

The bracket operators are the only operators that can be overloaded with custom functions. The custom function will only get its first argument - the characters between the brackets (whatever type of brackets they may be). The second argument of a normal custom function will be nil.

var object = { operator [ [** ] = custom input: ret input.split[','].map[ int.parse[v[[c: !{' ' ''@}.has[c] ]]] ].sort[v] } object[** 4,2,34,5 ] ;; returns {3 4 5 34}

preOperator[ level ] = function postOperator[ level ] = function

Defining or overloading unary operators. Must not have any parameters.

operator[else] = function preOperator[else] = function postOperator[else] = function

Defines a catch-all operator handler. The function should take one parameter - the operator potentially being operated with. If the operator is intended to be applicable to the object, the function should return an object with two members that looks like {int level fn! operation} where level is the operator precedence, and operation is a function of the form that would normally be used to overload an operator. The function should return nothing (nil) if the operator is not applicable.

The function creation function. It can create functions/methods and define function-types. creates a new scope.
:   [ ] fn is a custom-function that returns a function immediate (aka function object / nameless function / closure). This is like nameless functions in javascript and is pretty similar to functions in C, java, or php.

The function's are defined as a - an expression that either ends with a colon and a newline, or is surrounded by parens that end in a colon and newline. For a function, the expression inside the is very similar to an object's .

var w = fn[ a b c: ;; a b and c are taken as params ] var x = fn [ (a b c): ;; a b and c are taken as params again ] ;; this is a function that can take either no arguments, or ;; two arguments (b and c) ;; it does *not* have the parameter 'a' var x = fn [ a b c: ]
If variables in are initialized, it means that it has a default value that can be overridden if an argument is passed in for that parameter. Default parameters don't have to appear at the end of the parameter list. The only constraint is that all parameters with defaults be consecutive (all in a line), and if a splat is used, it comes after all the default parameters. Note that, like parameter types, default values are evaluated on every function execution (unless final is used, of course).
fn! x = fn a='myDefault' b: wout[a ': ' b] x['moo'] ;; prints "myDefault: moo" ;; variable defaults var z = 'default1' fn! x = fn a=z: wout[a] x[] ;; prints 'default1' z = 'toooo' x[] ;; prints 'toooo' var w = fn a=b b: ret a*10+b w[1] ;; returns 11 w[2 3] ;; returns 23 ;; how parameters are filled with arguments fn! y = fn a b=11 c=12 d=13 e: ret a+b+c+d+e wout[y[1 2 3]] ;; returns 31 (1+2+12+13+3)

In addition to what you can do in an object's , you can also write an object's members as parameters using , which denotes that the argument passed in is directly assigned to the member in the parameter space.

x = { a b func = fn .a .b: } x.func[2 3] wout[x.a x.b] ;; outputs 23
Destructuring assignment can also be done in a function:
var f = fn {a b} c: return a+b-c var x = {10 20} f[x 3] ;; returns 27

Variables defined in a function may not conflict with any variable already in scope - i.e. you can't shadow variables in lima functions.

Functions use multiple dispatch. If multiple signitures for a function are ambiguous for any inputs, an exception is thrown.

fn! A = fn int[x] int[y]: ret x+y int[x] string[y]: ret[y] x[3 5] ;; returns 8 x[3 'five'] ;; returns "3five" ;; this definition throws an exception, because the signatures conflict ;; for example in the case B[3 0] was called fn! B = fn int[x] int[y]: ret x+y fra[x] bool[y]: ret[y]

All functions are members of some object (even top-level functions are module methods). Therefore, and can be used inside functions. However, and variables accessed via are upvalues (variables closed over by a closure), and so remain pointing to the original object they were defined in when that function is copied elsewhere. This means that you can copy an object's method to somewhere else, and when you run the function, it may mutate the object you copied it from. To transfer the object referenced by and , an object must inherit the function using the or .

Inside a function definition, variables are accessible anywhere inside the function, even to lines that come before it (as long as its value is resolvable at that point - see the definition of for details). This is useful for giving more flexbility to the placement of nested functions and is necessary for allowing forward-jumps to continuations defined later in the program.

Function values have the following operators defined for them:


A function call. arguments are either a simple ordered list of values, a named list of values, or combination of both.

If arguments is a simple list (ordered parameters), then each of the function gets a reference to each corresponding in order. If has parameters that are assigned values (named parameters), then the parameter corresponding to the being assigned is set. You can use both ordered and named parameters as long as all the ordered parameters come first.

function[1 2 3] ;; passing 3 arguments in a simple list function[paramX=2 paramY=5] ;; passing 2 arguments with named parameters function[1 2 parameterB=3 parameterC=9] ;; using ordered and named parameters function[1 2 5:3 6:9] ;; using ordered and explicitly-ordered parameters (parameters 2 3 and 4 get nil)

Note: arguments are passed by reference. If a parameter is modified in a function, it will be changed outside the function as well. If a statement or literal is passed, conceptually a hidden variable is created that holds the value (so that methods don't have to worry about whether or not an actual variable/reference was passed).

The IDE should give the programmer insight into if a function mutates the state of passed in variables (as well as insight into how it can affect the module- or global- state). Of course after optimizing, the program might end up passing by value under the hood if that is more efficient.

Applicative order for function parameters are left to right - in the order they appear. So func[A[] B[]] will execute A first, then B.

Returns a type that matches a function that can take all the combinations of parameters (type, name, and order) that the operand takes.
var add2 = fn int x: ret x+2 var appendOrAdd2 = fn string x: ret['2'] int x: ret x+2 appendOrAdd2! newAppendOrAdd2 = fn ;; matches the interface of appendOrAdd2 string x: ret 'hi' int x: ret 99 add2! newAdd2 = fn ;; matches the interface of add2, since it can handle an int parameter x string x: ret 'hi' int x: ret 99 add2! funcA = fn x: ret x-500 ;; matches the interface of add2 appendOrAdd2! funcB = fn x: ret x-500 ;; throws an error, since the it can't handle a string add2! funcC = fn h: ret h-500 ;; errors because the parameter name doesn't match
== Returns true if the functions do exactly the same things (same return values, exceptions, and side-effects for the same input and program state).

Function values have the following methods defined for them:


Returns a function that produces no side effects without changing the output behavior (return value). Input variables (including the calling object) may still be changed.


Returns a function that produces no side effects and does not mutate input variables. Does not change output behavior (return value).


Isolates the function from modifying any external state (exactly like functional) and returns an object representing all the state it would change if it weren't isolated. The object returns contains the following members:

ret The return value of the function.
effects An object where each member describes a variable the function would have modified if it weren't isolated and what its new value would have been. The structure of the object is the same as the output of dif.


Returns the inverse of the function - a function that will return the inputs of the original function given an output of the original function, if possible. Takes one parameter - the output of the original function.

Running the inverse function will return a the full inverse of the function if possible (ie. when there is a one-to-one mapping from outputs to inputs of the original function for a given input to the inverse-function). If the function cannot be inversed for a given input, an exception will be thrown.

Special members only available inside functions:

is a custom function that returns a value from a function. To return nothing, call with nil.

var cow = fn: ret 'cow' wout[cow[]] ;; outputs 'cow'

Functions return by value. If a function wants to allow a returned variable to be used and modified, either a reference variable pointing to it must be returned (using the ~ postfix operator), or you can use ref's bracket operator to return a variable by reference.

fn!x = fn a: a+=1 ref! r ~> a ret r~ fn!x = fn a: a+=1 ret ref[a]

Because is a custom function, it can be passed to other functions so a function is able to return from a different function higher on the stack.

var f = fn returnFunction: returnFunction 5 var f2 = fn: f[ret~] ;; causes f2 to return 5

has the following members:

contin The continuation to which the function will return to when it returns. This may be in the middle of an expression.
result Holds the result that the function will return to the calling context. This allows the programmer to separate the act of deciding the result of the function from when it returns to the calling context. Conceptually, ret used normally first sets ret.result then jumps to ret.contin.

thisfn References the current function itself. Calling this will act just like using the function outside itself.

Returns an object representing the calling scope. This allows you to manipulate values in the calling scope and create new variables in that scope. Note that callingScope.callingScope accesses two calling scopes up - the scope that called the function that called the current function. This extends arbitrarily up the calls.

Not your mothers dynamic scoping

Dynamic Scoping would have been better named "Global Scoping" since all the variables are in the global scope. Lima gives unprecedented control over scoping, allowing you to create functions that can declare variables in a calling scope. This gives true dynamic control over variables declaration and calling scopes, although it has very little in common with what is commonly referred to as "Dynamic Scoping".

's postfix operator returns a type that only matches functions (not specifying parameters or return values).
that returns a type that can only take on a function that returns values the type given after the dot can take on. x = fn x: ret x+5
It also has a bracket operator defined for it:
Returns a type that matches a function who's arguments-list is constrained to the type passed in. The return value constraints that its calling type-object imposes still applies.[ cond[v.length==2] $ cond[v.has['a']&v.has['b']] ] x = fn string a int b: ret[b.english]
parse varList paramList codeString Parses the codeString into a function immediate. The varList (optional) is an object who's members will be in-scope variables the new function immediate has access to. The paramList (optional) is a list of parameter names the returned function will take.
var i = 5 var theString = 'i++ ret x-1' fn! f = fn.parse[{i~>i} {'x'} theString] f[8] ;; i becomes 6 and returns 7 fn! f2 = fn.parse[{} {'x'} theString] f[10] ;; throws an exception because i is undeclared var y=0 fn! f2 = fn.parse[{i~>y} {'x'} theString] f[99] ;; y becomes 1 and returns 98
fn.parse and var.parse greatly increase safety of running external code over the normal exec function in most programing languages (which exectutes statements from the string in-inline).
charsInside charsBefore: [ ]

A function definition. Custom functions must expect two string arguments that each get code passed into the custom function call. charsInside gets the characters that occur between the outer brackets of the function definition (for bracket-blocks) or between the colon and the first non-whitespace character that does not have a greater indentation then the line the custom function call begins (for indentation-blocks). charsBefore gets the characters found after the function name and before the first bracket (for bracket-blocks) or colon (for indentation-blocks) - whichever comes first. Inside the function declaration, the programmer must define how to translate a function call into "real" lima - probably using Lima's function.

A custom function needs to be followed by the ~ operator if it needs to be used without being called.

customName...[...] customName...:... ... Custom function calls. Must begin with the function-name, must have a right-bracket ([) or colon (:) sometime after the function name, and must end with a left-bracket (]) or . The ... here is denoting any amount of characters, as long as the first character in the first argument isn't a letter or number (because then it would add to the name of the variable).

characters: [ ]

Just like a normal function, except that it has no constraints on what characters are part of its call. It simply gets the string of all the characters that come after it as a parameter. If the definition of a function comes after its use, the compiler or interpreter may have to ask the programmer where the function call ends (if it can't figure it out itself - which it should be able to do most of the time).

realCustomName... A Real custom function call.

A type is a special kind of custom function that defines variables and constrains the values a variable can take on. Unlike most programming languages, types in Lima are basically contracts, otherwise known as guards. As contracts, the type of a variable almost never affects normal program execution. Exceptions to this rule are:

Like anything else in Lima, types don't have to be checked at compile-time, but will be if possible.

Operators available to all types:
a & b Type conjunction. Returns a type that has only the values both a and b have.
a $ b Type union. Returns a type that has the values of a and b.
a !$ b Exclusive type disjunction. Returns a type that has the values of a and b, but not the values they have in common.
? Returns a type that can hold nil, along with the values the type indicates it can hold. This is equivalent to type $ type.set[nil].
var x = {int? a} x.a = nil wout x.a ;; prints 'nil' var y = {int a} y.a = nil ;; throws exception
Members of all types:
values Returns a list of all the values a type can take on. This list may be infinite, but you can always use it to check if a value matches a type by seeing if it is in this list.

Lima's interfaces are very much like "traits" in SmallTalk, or "roles" in Perl 6 in that, since normal objects can be used as interfaces, the interfaces can define implementations of the members and methods it requires and are fully composable, allowing symmetric sum (via mixing in multiple objects), aliasing (via normal use and mix aliasing), and exclusion (also via exclusion allowed with the normal use and mix constructs).

A type may define an explicit interface - members and methods that a value must have to match a given type. Usually this is done using the ! operator for objects - which returns a type object that defines an interface according to what members, methods, and operators that object has. This can be used for requiring that values a variable takes on has certain members with certain types - a variable declared with an interface type must contain all the public members, methods, and operators that object has.

var x = {a=5 b=6} x! x1 = {a=0 b=0} ;; x1 must have the members defined in x (a and b) x1 = {a=5} ;; throws an exception because the value doesn't have a member 'b'

Its also used for multiple disptach. For methods with parameters declared with interface types, a value matching that type must be passed into that parameter and the function can perform structural dispatch (or structural typing) to decide which function-path to execute for that value. This means that an object doesn't have to explicitly implement/inherit the interface, but can simply have the same structure the interface requires. This is similar to whats refered to as duck typing and more similar to what Dart calls implicit interfaces. When an object inherits (mixes in or uses) an object, it automatically implements that object's interface and any interface that object implements (unless any members are excluded from the inheritance - explained more below).

var Animal = { fn! talk ;; implicitly abstract } Animal! Cat = { talk = fn: wout['moo'] } ;; functions with multiple dispatch using types fn! func = fn Animal! obj:[] obj: wout['obj is: 'obj] ;; uses object's str method func[Cat] ;; prints 'moo'
Note that Animal itself is not a value that an Animal! typed variable can take on. This makes sense since an interface doesn't have to implement itself.

Multiple interfaces can be inherited, even if member names conflict. Functions that take interface-typed parameters will "promote" an object that implements that interface to a form that directly conforms with the interface. The identity of an interface depends on both the original object it came from and the structure of that object. Unlike the simple duck-typing model, this allows two identically structured objects to still be treated differently.

When a multiple dispatch is being decided, first it looks to see which interfaces an object implements that has the same interface identity (the object the interface was created from), and selects based on that if possible. If its not possible, then it does structure typing (matches just the members, and not the interface identity)

var i1 = {var v} var i2 = {var v} var w = {use[i1] v=1} // implements i1's identity var x = {use[i2] v=2} // implements i2's identity var y = {v=3} // implements neither's identity, but still matches both their interfaces var z = {use[i1 i2:[v:v2]] v=4 v2=5} // implements both var f = fn i1! a: wout[a.v] i2! a: wout[a.v*1000] f[w] // outputs 1 because w matches both structure and identity of i1 f[x] // outputs 2000 because x matches both structure and identity of i2 f[y] // outputs 3 because y matches the structure of i1 (and i1 comes before i2, so has first priority) f[z] // errors as ambiguous - both interfaces match fully (structure and identity)

If an object inherits an interface, but a child-object omits one of the interface members, that child no longer implements the interface - even if it defines a new member of the same name.

var i = {int a} fn print = fn i! v: wout[v.a] v: wout[0] var x = {use[i] a=1337} var y = {use[i:[!a]] a=900} print[x] ;; prints 1337 because x implements i print[y] ;; prints 0 because y doesn't implement i

Keep in mind that, because an object's interface depends on its identity as well as its structure, functions that generate and return interfaces may need to use memoization to preserve the ability to use type promotion for interfaces defined in the same way.

var iGenA = fn type T: ;; interface generator ret {T a list[T] b} var memberSum = fn iGenA[int~] x: wout[x.a + b.join[a+b]] var a = {use[iGenA[int~][a:a2]} ;; a inherits from iGenA[int~] but renames a to a2 memberSum[a] ;; throws an exception
The call memberSum[a] throws an exception because a doesn't have the same structure as iGenA[int~] and can't be promoted due to the fact that each call of iGenA generates a different object (with its own unique identity).

Methods available all interface types:
cast[object] Promotes an object to having a particular interface. Returns an object where its members are mapped to match the calling interface, just like how function arguments are promoted.
var i = {var v} var x = {use[i:[v:vee]] v=5} var xCast = i!.cast[x] wout[x.vee] ;; prints 5 wout[x.v] ;; prints nil wout[xCast.vee] ;; prints nil wout[xCast.v] ;; prints 5
The call memberSum[a] throws an exception because a doesn't have the same structure as iGenA[int~] and can't be promoted due to the fact that each call of iGenA generates a different object (with its own unique identity).

Attributes are custom functions that set a value for a statement as a whole (and any underlying calls that statement makes). Attributes have similarities to dynamic scoping, since they don't need to be passed to a function to be used by it. An attribute is semi-constant in that it isn't mutable in a given scope, for an attribute to change its value it must be applied to a sub-statement. Doing an action that mutates the value of an attribute causes an exception to be thrown. Declaring a variable with the same name as an in-scope attribute causes an exception.

attribute red A[] ;; prints "Its not red" red A[] ;; prints "Its red" B[] ;; prints "Its not red" red: ;; attributes can use colon syntax B[] ;; prints "Its not red" var A = fn: printColor[] var B = fn: !red printColor[] var printColor = fn: if red!: wout['Its red'] else: wout['Its not red']
In almost every (if not every) other language, these would be keywords. In Lima they're first-class constructs.

attributeName = default

Attribute declaration. If no default is given, it is false.

attributeName attributeName[ statements]

Sets an attribute as the value true on a statement or set of statements. Brackets are optional if only adding the attribute to one statement.

!attributeName !attributeName[ statements]

Sets the attribute to its default for a statement or set of statements.[value][value][ statements]

Sets an attribute as an explicit value on a statement or set of statements. Brackets are optional if only adding the attribute to one statement.


Returns the value of the attribute.

Number postfixes are special functions written on the end of numeric literals. They can take up to two arguments: the number being prefixed and a second argument that is a string of characters a-z, A-Z, 0-9, and _ (underscore).

Example: var x = 16xF63A

numberPostfix name = function

Defines a new number postfix named name. function should take at least one argument, the first being a number. It can also take a second string argument.

Ambiguities can arise when defining postfixes with more than two characters. For exampe, if the postfix xa is defined, a value like 8xac9b is ambiguous (because of the postfix x). In these cases an exception is thrown and your code should be refactored to resolve the ambiguity.

nil is a reference to nothing. This is not the same as the value 0. Only nullable types (types with a question mark after them) can hold .

Like everything else in Lima, nil is an object, and has operators:


Puts the value of into . = doesn't return a value (attempting to use the value of an assignment is a syntax error), but can be chained.

x = 5 ;; sets x to 5 a=b=c=d= 0 ;; sets a, b, c, and d all to 0


Points a variable at a another variable or value - making a reference.

var x = 5 var y ~> x ;; y now points to x wout[y] ;; prints 5 y = 4 ;; x is now 4 wout[x] ;; prints 4 wout[y] ;; also prints 4

~> doesn't return a value, but can be chained. E.g. a~>b~>c~>d~> x ;; sets a, b, c, and d all to point to x.


Safe navigation operator (postfix operator). Returns an empty object with the following properties that distinguish it from a normal empty object:

  • It has a dot operator that always return nil (instead of throwing an exception, which is what usually happens when the dot operator attempts to get a member that doesn't exist).
  • If copied, a normal empty object will be returned. Note that most of the default methods on objects (app, sort, cat, etc) create copies of the object.
  • It has an IterList that returns an empty object, so iterating through the result will be the same as doing that for an empty list.

This can help you safely access nested members of an object without intermediate nil-checking.
x = { a={f='hi' g='lo'} } x.a.f ;; returns 'hi' x.b?.f ;; returns nil x.b.f ;; would throw an error x.b?[] ;; returns nil x.b[] ;; would throw an error
a | b
a ?? b Returns true if a and b represent the same data (they hold the same piece of data in memory). Examples of this are when the same value is passed into two different parameters in a function, or if you're comparing what two references point to.
a !?? b Same thing as !(a ?? b)
a == b Returns true if both values are the same (ie they're both nil).
a != b

Same thing as !(a == b). If == is overridden, but != is not, != will return the complement of the value returned from ==.

{ }

An object literal. Can be used for making objects, lists, associative arrays, matricies, etc. There are three ways to add members in an object liberal:

  1. Adding values with implicit keys. E.g. {'a' 'b' 'c'}
  2. Adding values with explicit values as keys. E.g. var x = {14:'hello' "b":4 5.9:0.1}. This is the same as setting values with the default bracket operator, e.g. that last object is the same as x={} x[14]='hello' x['b']=4 x[5.9]=0.1.
  3. Adding values with variable names as keys. E.g. {a=1 b=2 c=3}. Creates a member. This is the only way private members can be created and is also the only way a type can be ascribed to a key in an object literal.
    var x = {a=4} ;; a is privileged here and can't be accessed via the default bracket operator ;; are different var z = {"a":4} ;; a here is not privileged
Any value can be a key, and keys are always differentiated using the equality operator (==). Members defined using option 3 above have the type var by default (if no type is given). Members defined using option 1 or 2 above have the type var? (they can be nil).

Note that setting a member of an object with : is like using the bracket operator. Private members must be set with the equals sign (=). Value initializations set in the never reference the value of any member of that object (variable names always refer to variables in the scope in which the object is being created).

var a = 5 var object = { a = 'a' b = a ;; sets b to 5 (*not* 'a') } ;; two ways to write the same list: {10 15 20 25} ;; a list {0:10 1:15 2:20 3:25} ;; the same list { x=2 ;; an object/associative array var y=89 } {'r':5 'u':2 34:'falcon'} ;; an object/associative array { {1 2 3} ;; a matrix (list of lists, or more accurately, nested objects) {4 5 6} {9 9 3} }
All three ways to add members can be combined, as long as all the values with implicit keys come first.
{ 1 2 3 'a':b x=5 y = fn: ret 'y' }

Object members are public by default. Also, objects in the same (same file) can freely access eachothers' private members (despite their private qualification). Therefore, the concept of "friend classes" is unneccessary in Lima.

members can't be accessed via the bracket operator and won't show up in the object's IterList (and so won't appear as a member when iterating through the object in a loop). All private variables are .

var x = { a=5 b=6 C=7 } x['a'] ;; 5 x['c'] ;; nil x['C'] ;; also nil ;; will print '56' leaving out the 7 df x v: wout[v]

Note that any member defined in the can be defined by another member defined in the , even if it is not defined before the other (as long as there are no circular definitions).

;; these is a valid definition { a = 5 b = a c = {x=5 y=0} d = c.x 'x': {'a':this[{'g':5}] 'b': z} {'g':5} : 10 z = 'moo' }

Objects inherit from nil an so get all the operators defined for nil. The following three operators are overridden:

Binary operators defined for objects:

Accesses a member of the object. memberName will be interpreted as a member name (rather than using its value if it happens to also be the name of an variable in scope).

members take precedence over other members of an object (e.g. if an object has a member A and key 'A' set, object.A will access the member, not the key's mapped value).

The dot operator can also be used to access values at numeric keys using immediates.

var x = {'a' 'b' 'd'} wout x.1 ;; prints 'b'

For non-privileged members, the operation will throw an exception if the member doesn't exist (in comparison, the bracket operator will return nil). For privileged members, the operation will return nil unless its type prevents that, in which case an exception will be thrown.

a + b Matrix and vector addition (adds elements with the same index together). Equivalent to[v+b[k]]. For matrix/vector addition and subtraction, and the vector dot product, a and b must have the same dimensions and size. An exception is thrown if they don't.
a - b Matrix and vector subtraction (subtracts elements of b from a that have the same index). Equivalent to[v-b[k]].
a * b

Matrix product. Only defined for rectangular matricies when the number of rows in a is the same as the number of columns in b and vice versa.

This operator is also used for duplication of lists. It is only defined when a list is being multiplied by an int value. For example, 3*'moo' would return "moomoomoo", and 3*{3 4} would return {3 4 3 4 3 4}

a / b Product of inverse matricies. Defined only where b is an invertible matrix. Also defined only where either the matrix product of a on the inverse of b is defined, or where a is 1, in which case the operation indicates the inverse of b.

a & b Set intersection (conjunction). Returns a that contains the member-values that exist in both a and b. Keys are not preserved.
a $ b Set union (disjunction). Returns a that contains the member-values that exist in either a and b or both. Keys are not preserved.
a !$ b Set exclusive disjunction. Returns a that contains member-values that exist in only a and member-values that exist only in b but not member-values that exist in both. Keys are not preserved.
a && b Object member intersection (conjunction). Returns a copy of b that only has keys that exist in both a and b. This essentially returns b with keys removed if they don't exist in a.
a $$ b Object member union (disjunction). Returns an object that merges b into a. Any members that exist in both objects will contain the value that exists in b.
a !$$ b Object member exclusive disjunction. Returns an object that contains member that only exist in a and members that only exist in b but not member-values that exist in both.

a == b

True equality. Returns true if both a and b contain all the same keys, and if the values at those keys are all the same.
a <> b Set equality. Returns true if all the values contained in a are also contained in b. Since types are considered sets in Lima, this can also be used to compare equality of types (meaning comparing two variables containing types, not comparing the types of two variables).
a !<> b Same thing as !(a <> b)
a < b Proper subset of s (returns true if a is a proper subset of b).
a > b Proper superset of s (returns true if a is a proper superset of b).
a <= b Improper subset of s (returns true if a is an improper subset of b).
a >= b Improper superset of s (returns true if a is an improper superset of b).
x << >> <<= >>= o

Key-aware comparisons. Just like their counterparts <, >, <=, and >= but compares keys as well as values.

{a=1 b=2} << {a=1} ;; true {b=1} < {a=1} ;; true {b=2} << {a=1} ;; false

a | b

a || b Combines two lists by interleaving their elements by the time they were added. Only works on lists that are only appended to.
+= -= *= /= Sets a variable to some operation of itself with an expression. For example, a+=b is equivalent to a = a + b). Can't be chained.
.= Same idea ^. The result of the expression following the operator is asigned to the calling object. Also can't be chained - you can't have two .= operators in the same expression. x.=y.=z will throw an exception.
someObject.=x ;; same as someObject = someObject.x someObject.=function[3] ;; same as someObject = someObject.function[3] someObject.=x.y.z[4].f ;; same as someObject = someObject.x.y.z[4].f someObject.y.z.=help.ok[5].go ;; same as var temp ~> someObject.y.z temp =[5].go
[= ] [[= ]] Same idea as the dot-equals operator. The result of the expression following the operator is asigned to the calling object. Also can't be chained.
someObject[=x] ;; same as someObject = someObject[x] someObject.y.z[=help].ok[5].go ;; same as someObject.y.z = someObject.y.z[help].ok[5].go

Unary operators defined for objects:

Safe navigation operator (postfix operator). A no-op (is intended to only have an affect on nil).


Reference access operator. For normal objects, simply passes the value right back.

The splat operator (inspired by cofeescript) is used to splay values in an object out as arguments to a function or values in an object literal. It is also used as syntax for varargs in functions (variadic functions).

A () can be used to pass an object's members in as function arguments.

x = fn a b c: wout[a b c] y = {1 2 3} x[y...] ;; outputs "123" z = {b=1 a=2 c=3} x[z...] ;; outputs "213" using z's members as named parameters

Only one splat can appear in an argument list, but it can be written between arguments if need be. Splats must come after any variables with default values.

fn a b=5 c splat... d e f: wout splat[[cat]]

Splats can be used in objects (and destructuring assignments).

x = {1 2 3 x=4 34:'k'} y = { 0 -3 {}:'t' x... 3 3:3 'j':{'a' 'b' 'c' 'd' 'e' 'f'} } {j={var[a b otherLetters... f]}} = y ;; the results are a='a', b='b', f='f', and otherLetters={'c' 'd' 'e'} {var[one two other...]} = x ;; the results are one=1, two=2, three=3, and other={2:3 'x':4 34:'k'}

One might ask how using splats in an object literal compare to and . The answer is that splats only splay out normal (non-) members, while and include members as well.

Interface operator. Creates an interface type that has all the members that the object has. Returns a type (defined by the object's interface).

Members available by default for any object:
str The default to-string method returns a string of code for an object-literal containing each non-privileged member in the object.
Len This returns the number of non- public members in the object. For example, the list {0:'a' 1:'b' 2:'c' 9:'d' 'red':'no'} has a len of 5. This member cannot be set(written to).
Len The number of elements in the list. This member cannot be set(written to). Is undefined for objects that aren't lists (any object that has had keys modified or added that aren't between 0 and list.len inclusive).
Peeklen The number of elements in the list at the time the code is called. This allows you to get the current length of a list even if its still being populated or is infinitely long.
Keys This returns a list containing all the keys of the non- public members in the object. One way this can be used is to test if an object contains a certain member. For example, object.keys.has[34] returns true if the object has a member/element 34. The order of the keys in this member determines the order that the object is looped through. For lists, the order of the keys is automatically sorted by increasing numeric value. This member can be modified to rearrange the keys, but cannot be used to add or remove keys.
IterList This returns this by default. Since calls IterList, df will just iterate through the object as one would expect - unless this member is overridden.
Special members only available inside object literals:

The following two members reference aspects about the object literal they're written in. They are special in that they break the rule of not allowing duplicate aliasing in Lima. This is one of the very few cases where Lima has a construct that can't be duplicated by the programmer.


Not a true member, is a special construct that can be used to access members of the object that alias members in an upper scope (it must be used to access those members, since an unqualified reference to that variable name would be ambiguous). It must always be used with the dot operator.

var n = 5 var x = { n=99 func = fn: ret n ;; errors because n is an ambiguous variable name at this point func2 = fn: ret self.n ;; returns this object's n (with the value 99) nref ~> n ;; sets up an alias to the outside n func3 = fn: ret nref ;; returns the outside n (with value 5) }

If the object is inherited by another object that overrides a member, or does not inherit that member, an access of that member using will reference a hidden copy of that member, just like an unqualified access would. Renaming an inherited member (with or ) does not change the fact that the renamed member points to the same member as the function that used its originally-named counterpart. In fact, if an object inherits two functions that use the same variable name, those functions will in reality use two different variables (hidden if they aren't inherited, referenced if they're renamed).

var x = { n=0 func = fn: n++ ret n ;; always accesses the copy of n as defined by this object (x) func2 = fn: n++ ret self.n ;; same as above } var y = { mix[x] n = 'hi' } wout[x.func[]] ;; prints 1 wout[y.func[]] ;; also prints 2

Note that you cannot copy or set a reference to self, and so it cannot be returned from a function. If you need to return the object itself, use this.

this Read-only member that references the object itself. This will act just like using the object outside itself. Private members can't be accessesed using this.
var x = { n=0 func2 = fn: n++ ret this.n ;; will access whatever public n is available on the object inheriting this method } var y = { mix[x] n = 'hi' } wout[x.func[]] ;; prints 1 wout[y.func[]] ;; prints 'hi'
Methods available by default for any object:
key1 key2

Accesses a member of an object/list. With only a single input key, it simply accesses that member. Multiple keys can be input, which would result in accessing a member of a member. For example, object['a' 'b' 'etc'] is the same as object['a']['b' 'etc'].

Keep in mind that if one of the sub-members has an overloaded bracket operator, this equality still holds.
obj = { a = fn x y: ret {x y} } obj['a' 'b' 'c'] ;; returns {'b' 'c'}

The bracket operator cannot acceess (read/write/create/etc) members of an object. When the bracket operator is used to set a key that is already a member of the object, a new member will be created that can only be accessed through the bracket operator.

var x = { Funco = "Ok!" } wout x['Funco'] ;; errors because the key 'Funco' has never been set, and the bracket operator can't access read-only members wout x.Funco ;; outputs Ok! x['Funco'] = 4 ;; sets the 'Funco' key wout x['Funco'] ;; outputs 4 wout x.Funco ;; still outputs Ok!

Also note that the bracket operator's keys are case sensitive (for string keys). For example,

var x = { theCar = "deLorean" } wout x.theCar ;; outputs deLorean wout x.thecar ;; also outputs deLorean wout x["theCar"] ;; again outputs deLorean ;;wout x["thecar"] ;; would throw an error because the key "thecar" has not been set x["thecar"] = "acura" wout x.theCar ;; outputs deLorean wout x.thecar ;; also outputs deLorean wout x["theCar"] ;; again outputs deLorean wout x["thecar"] ;; outputs acura

This also works for lists and matricies (mult-dimensional arrays).

matrix = { {1 2 3} {4 5 6} }
matrix[1 2] accesses the that has the value 6.

Note that the hashcode used to map keys to values is automatically done based on the object's == operator, so the programmer doesn't have to worry about creating a "good" hash function for their objects.

has x Tests if the object contains x, where x is some lima expression. obj.has[x] is essentially is the same as {x} <= obj, but is more readible.
Ins index sequence...

Returns an object with the sequence inserted starting at the int key index. If any values are already at indexes that will be written with values in the sequence, they will be removed and reinserted in the next key after the sequence (index+sequence.len). This causes all the values at consecutive indecies at or higher than index to be "moved over" to make room for the new value. If index it is inserting at isn't an integer, an exception will be thrown.

var a = {9 8 7 6} var b = a.ins[4 4] ;; returns {9 8 7 4 6} b.ins[3 1 2 3] ;; returns {9 8 7 1 2 3 4 6}
If the parameter index is omitted (and there's just one parameter - a single value to insert), that single-value sequence will be appended to the list (inserted at the "end" - the first unoccupied positive integer index closest to 0 in the object).
var a = {} var b = a.ins[1] ;; returns {1} b.=ins[4] ;; 4 is appended to b - b is now {1 4}

cat list lists Concatinates lists. It returns a data structure, where the variables and constants are concatinated in the order they're listed.
{1 2 3}.cat[{4 5 6}] ;; returns {1 2 3 4 5 6} 'I'.cat["'m a mo" 'ose'] ;; returns "I'm a moose"
rm indecies..

Returns a list with the selected member removed and the list rekeyed. Selection works just like the .

var x = {1 2 3 4 {5 6}}; x.rm[3] ;; returns {1 2 3 {5 6}} x.rm[4 1] ;; returns {1 2 3 {5}} var y = {59:10 69:20 79:{34:2 35:3 37:4} 89:40} y.rm[79 34] ;; returns {59:10 69:20 79:{3 4} 89:40}

Find sequence

Returns a list of s where each member in the list is a non-overlapping of the object with the same sequence of elements as sequence.

{1 2 3 4 5}.find[{3 4}] ;; returns { {2:3 3:4} } "What are you looking at?".find["at"] ;; returns two string slices that looks like { {2:'a' 3:'t'} {21:'a' 22:'t'} }

split sequence includeSplitPoints

Splits the list into a list of s that are separated by one of the passed in sequences in the original object. includeSplitPoints is optional (default false), but if true, the elements matching the sequence are included in the begginning of the next .

{1 2 3 4 5}.split[{3 4}] ;; returns { {0:1 1:2} {4:5} } "What are you looking at?".split[" "].map[v.sort[k]] ;; returns {'What' 'are' 'you' 'looking' 'at?'} "What are you looking at?".split[" " true].map[v.sort[k]] ;; returns {'What' ' are' ' you' ' looking' ' at?'}

replace sequence replacement This function returns a new object where every occurrence of sequence is replaced with the replacement.
var y1 = {1 2 3 4}.replace[{2 3} {90 91 93}] ;; y1 gets {1 90 91 93 4} ;; is equivalent to: var y2 = x var sequence = y2.find[{2 3}] ;; find the sequence y2.=rm[[sequence.keys.has[k]]] ;; remove it df {90 91 93} value key: y2.ins[key value] ;; insert the new sequence wout["What's going on in here?".replace["in" "down"]] ;; prints "What's godowng on down here?"
tslice start end

Returns the list elements of list that were created at or after the time represented by start and before (but not at) the time represented by start. The parameters start and end can either be time objects or futures (or a mix). If an argument is a future, the time it represents is the time the future is resolved (when its wait function would be called).

This is useful for implementing event-like code (see "Event handling in Lima" under Concepts for more info).

Defined as the following:
tslice = fn startTime endTime: var start = snapshot[this startTime] var end = snapshot[this endTime] var d = dif[start end] if d.change != nil $ d.set != nil ;; can't have been set or have other inner changes $ d.elements[[v.type=='move' $ v.type=='remove']].len > 0 ;; can't have had elements moved or removed $ d.elements.all[v.index >= start.len]: ;; elements should all have been appended throw "tslice can only work on a list that is only appended to between the given times" var indexes =[v.index] return end[[indexes.has[k]]] ;; returns slice
tslice end Same as:
var now = time.before: list.tslice[now end]
dot x Dot product. Only defined for vectors (lists of numbers).[x] is equivalent to[v+b[k]].join[v v2: v+v2].
cross x Cross product (of vectors). Only defined for vectors (lists of numbers) that have 3 dimensions.
Custom methods available by default for any object:

Object slicing based on conditions. Also known as a filter (e.g. in python, javascript, haskell) or where (e.g. C#, sql). Returns a showing the members that match the condition. A is an object whose members are references to the corresponding members in the original object (this is explained more below). Keys preserve.

In the , k and v are s unless different aliases are declared.

  • v refers to each value in the list, and
  • k refers to each key in the list.
Different aliases (than v or k) can be declared by writing one or two variable names and a colon right after the opening bracket (e.g. list[[value key: key%2 == 1]] and list[[value: value < 1]]).
{1 50 4 99}[[k<50]] ;; returns a slice with all members who's keys are less than 50 (a slice that looks like {0:1 2:4}). wout 'Delorean Motor Company'[[{0 9 15}.has[k]]] ;; prints 'DMC' ;; the following returns a slice with all members whos third element is less than 50 ;; (a that looks like {0:{1 2 3} 2:{6 7 8}}). {{1 2 3}{4 5 50}{6 7 8}}[[ v[3] < 50 ]] ;; the following returns a that could represent a matrix where the columns (the first order members e.g. {1 2 3}) ;; only contain the rows where the element in the third column of that row is less than 50 (resulting in a that looks like {{1 2}{4 5}{7 8}}. x = {{1 2 3}{4 5 6}{7 8 50}} x[[v[3] < 50]]

Note that if any aliases are declared (if only one or two names are written before the colon), then the undeclared aliases are not available. If any of the variable names v or k are already defined in scope, then you have to declare at least one alias (and it can't conflict with any other name in scope).

var[k={'a' 'b' 'c' 'd'}] ;; gets every other character in the list k the value alias must be defined here because otherwise k would be implicitly declared ;; and would error because of duplicate variable names k[[val: val.ascii%2 == 'a'.ascii]]

Similar to the bracket operator , multiple sliceConditionals can be written to access deeper dimensions of an object.

var x = {a=1 b="hi" c={1 2 3} d={40 51 60}} x[['c'<=k<='d' v%2==0]] ;; returns {c={1:2}, d={40 2:60}} x[['c'<=k<='d']].map[ v[[val: val%2==0 ]] ] ;; this does the same thing in a less elegant way


As mentioned above, a is an object whose members are references to the corresponding (non-privileged) members in the object the slice was taken from. This is a conceptual extension of the single-bracket-operator (single member access) to sets of members. If you modify a member of the , the corresponding member in the original list will also be modified, and vice versa. As with normal references, setting a variable (with the = operator) to a slice will give that variable a copy of those members (not references to them). However, if you make a variable reference the slice instead (or directly use the result of functions/operators that return slices), you can modify the original object with that reference.

var x = {1 2 3 4 5} var y = x[[v > 3]] ;; y gets a copy of the slice containing the first two members (1 and 2) y[3] = 99 ;; x remains the same because y got a copy y ~> x[[v > 3]] ;; y is referencing the slice y[3] = 99 ;; x is now {1 2 3 99 5} df x[[v<3]] v: v++ ;; after that loop, x is now {2 3 3 99 5} - the first two members were incremented
While the values in a slice are references to the original values, the keys are copies and so can be manipulated however.
var x = 0..100 ;; sets the last value greater than 50 and less than 80 to 'hi' x[[v>50]].sort[k][0] = 'hi' ;; after this, x[51] holds 'hi', but is otherwise unmodified
Note that not only can changing a list-slice affect the original object, but since the values in a slice are references, changing values in the original object can affect the slice.

A slice inherits members from the object it is a slice of, meaning that it will have all the same methods and operators as the original object. Most standard methods and operators can't affect the original object the slice was taken from , but some can.

. [] [[]] [? ] ... All of these operators return references to the object the slice was taken from, and thus members of the results can modify the original object.
var x = {a=1 b=2 c=3 d=4} x[[v<3]].b = 5 ;; x.b is set to 5 var y = {1 2 3 4} y[[v>=2 & v<5]][2] = 99 ;; y now holds {1 2 3 99} var f = fn a b c: a++ b++ c++ var z = {1 2 3 4 5} f[ z[[k>=2]]... ] ;; after this executes, z contains {1 2 4 5 6}

map memberStatment

Returns the result of performing some statement on every member (value or key/value pair) in a list-object. The memberStatment tells how to modify the value. The s v (for value), k (for key), and c (for count) are declared by default, but can be aliased just like with the double-bracket operator.

{1 2 3}.map[v*5] ;; will return {5 10 15} {1 2 3}.map[{2 3 4 5}.has[v]].join[a&b] ;; asks if every member in the list is either 2, 3, 4, or 5 {false true true}.join[a&b] ;; is equivalent to the above (thus it will return false, since 1 isn't in the list). {1 2 3}.map[{2 3 4}.has[v]].join[a$b] ;; similarly, this asks if any member in the list is either 2,3,4, or 5 (so this will return true).

map keyStatment memberStatment

Just like map with one statement except that it also modifies the key-value. The first parameter, keyStatment, modifies the key, and the second parameter memberStatment modifies the member's value.

{1 2 3}.map[v v^2] ;; returns an object keying the squares of the values by the original values: {1:1 2:4 3:9} {a=1 b=2 c=3}.map[c k] ;; basically, returns {0:'a' 1:'b' 2:'c'} (basically the same thing as {a=1 b=2 c=3}.keys)

join expression

AKA reduce or fold, join combines all the values in an object into one value. The expression determines how to combine the elements of the list. The s in the expression are a and b. In the first combination, a holds the first elements and b holds the second element. After the first combination, a holds the current aggregate, and b holds the next element to aggregate. This means that the aggregate should be a similar type of value as each element (as is the case with sums or concatenating strings). Throws an exception if theres an empty list.

{1 2 3}.join[a+b] ;; returns 6 {}.join[a+b] ;; throws exception {{x=1} {x=2} {x=3}}.join[a + b.x] ;; errors because in the expression {x=1} + {x=2}.x, {x=1} does not have a + operator {{x=1} {x=2} {x=3}}.join[a.x + b.x] ;; errors because in the expression 3.x+{x=3}.x, 3 does not have the member 'x'

join init expression

Similar to join with one argument, except a always holds the current aggregate (which is initialized to init) and b holds the next element to aggregate. Empty list returns the init value

{1 2 3}.join[0 a+b] ;; returns 6 {}.join[0 a+b] ;; returns 0 {{x=1} {x=2} {x=3}}.join[0 a+b.x] ;; returns 6

scan expression

Like join with one argument, but returns a list of all intermediate values in the join. x.join[exp] could be implemented as x.scan[exp].sort[-k][0] - getting the last value in the list returned by scan.

{1 2 3}.scan[a+b] ;; returns {3 6}

scan init expression

Again, similar to join of this form (two arguments), but returns a list of all intermediate combinations in the join.

{1 2 3}.join[0 a+b] ;; returns {1 3 6} because the first combination is 0+1 {}.join[0 a+b] ;; returns {0} {{x=1} {x=2} {x=3}}.scan[0 a+b.x] ;; returns {1 3 6}

split[[ ]] expression

Returns a list of s split at points where the expression is true. The s v (for value), k (for key), and c (for count) are declared by default, but can be aliased just like with the double-bracket operator. The elements where the expression is true are left out.

{1 9 10 3 1}.split[[v<10]] ;; returns {{1 9} {3:3 4:1}}

split[[ ]] expression includeSplitPoints

Just like split with one parameter, except if includeSplitPoints is true, the elements where the expression is true (the split points) are left *in* at the beginning of every in the resulting list except the first.

{1 9 10 3 1}.split[[v<10 true]] ;; returns {{1 9} {2:10 3:3 4:1}} aList.split[[v!=aList[k-1] true]] ;; splits when value changes

all expression

Returns if expression is true for all members. Uses the same s as map. Returns true for an empty object.

obj.all[expression] ;; equivalent to[expression].join[true v&v2]
Group [expression]

Returns a map of sub-objects where each member in a given sub-object evaluated the expression to the same value. Each key in the map is the value that the expression was evaluated to Keys of lists preserve.

Group uses s just like map and the slice operator.

{ 1 2 3 4 5 4 3 2 }.group[v] ;; returns { 1:{1} 2:{1:2 7:2} 3:{2:3 6:3} 4:{3:4 5:4} 5:{4:5} } var x = {x={a=1 b=2} y={a=3 b=5} z={a=1 b=9}} var y =[v.a] ;; sets y to { 1:{x:{a=1 b=2} z:{a=1 b=9}} 3:{y:{a=3 b=5}} } var x = { {1 2 3 4} {1 5 8} {9 10 11} {9 11 15} {20 18 17} }[v[0]] ;; returns { 1:{0:{1 2 3 4} 1:{1 5 8}} 9:{2:{9 10 11} 3:{9 11 15}} 4:{4:{20 18 17}} }

rm[[ ]]

Returns a list with the members matching the removed and the list rekeyed. The works just like .

var x = {1 2 3 4 5 6}; x.rm[[2<v<6]] ;; returns {1 2 6}

Ins[[ ]] value testExpression

Returns a new list where for every set of elements, value has been inserted between them as long as testExpression returns true for those elements. testExpression is optional, defaulting to true.

{1 2 3}.ins[[0]] ;; returns {1 0 2 0 3}

a, b, ak, and bk are s, and they are aliased in that order. a is the value that comes first in the list, and ak is its index. b is the value that comes second in the list, and bk is its index.

var x = {1 2 3} x.ins[[a+b-ak]] ;; returns {1 3 2 4 3} x.ins[[prev next key1: prev+next-key1]] ;; the same thing using different aliases x.ins[['a' a+b<4]] ;; returns {1 'a' 2 3} x.=ins[['hi' ak==0]] ;; x gets changed to {1 'hi' 2 3}

Sort[ ] objectMembers

Stable value sort, ascending. Returns an object where the members are sorted by some values inside the brackets. v and k are s.

It will be primarily sorted by the first value, secondariliy sorted by the second value, etc. Order is preserved when values are equal. The keys will be changed so that all the values are elements (their keys all start from 0 and increment from there).

list x = {8 3 9 4 87 1} x.sort[v] ;; returns {1 3 4 8 9 87} var obj = {a=1 b=-2 c=3} obj.sort[val key: obj.len-key-1] ;; returns {3 -2 1} {{a=5 b={x=9} c='c'} {a=5 b={x=7} c='d'}}.sort[v.a v.b.x] ;; returns {{a=5 b={x=7} c='d'} {a=5 b={x=9} c='c'}}

Sortus[ ] objectMembers

Unstable sort. Exactly like Sort[ ], but doesn't ensure that order is preserved when values are equal (the 'us' of 'sortus' stands for "unstable").

Sort[[ ]] objectMembers

Stable expression sort, ascending. Returns an object where the members are sorted based on comparison expressions inside the brackets. The aliases a, b, ak, bk refer to the keys and values of the two items being compared by the sorting method. The expressions should indicate whether a comes before b.

Order is preserved when values are equal. It will be sorted primarily using the first comparison expression, secondariliy using the second comparison expression, etc.

list x = {8 3 9 4 87 1} x.sort[[a<b]] ;; returns {1 3 4 8 9 87} obj.sort[[val1 val2 key1 key2: val1<val2 ]] ;; also returns {1 3 4 8 9 87}

Sortus[[ ]] objectMembers

Unstable expression sort. Exactly like Sort[[ ]], but doesn't ensure that order is preserved when values are equal (the 'us' of 'sortus' stands for "unstable").

Special constructs that can be used inside the of an object:
:  parameters [:  parameters ]

Constructor. Implicitly creates a copy of the this, runs the statements in make for that new object, and also returns it implicitly.

var x = { var[a b] make self.a x: b = 5*x } x[1 2] ;; returns a copy of x that looks like {a=1 b=10 fn! make}
The method, however, may explictly return, in which case it returns that alternate object instead (and the implicitly created object is discarded - or really [under the hood] is never created in the first place).
var x = { make a x: ret {a=a x=x} } x[1 2] ;; returns {a=1 b=2}

make overrides the bracket operator for the object it is written, but any object created via the constructor retains whatever other bracket operator may be defined and does *not* have the constructor.

var x = { operator[ [ ] = fn val: ret val+1 make: ret {a=3 b=5} } var newX = x[1 2] ;; returns {a=3 b=5} newX[99] ;; returns 100

[ ]

Destructor/finalizer. Declares code that is run when all of the following happen:

  • the object goes out of scope or the is explicitly deleted (by setting the variable holding it to nil)
  • it has no strong-references pointed to it (note that closures may implicitly have strong-references to the object)
Cannot be called directly.

An object's unmake is guaranteed to be run (unlike in languages like python) as long as the program isn't forcibly killed. In the case of circular references, an order is arbitrarily chosen and the uncaughtExceptionHandler is called with an exception describing which finalizers were involved in the circular reference (so it can be corrected). If two or more destructors modify the same shared variable, one of the final results is arbitrarily picked.

After unmake runs, any weak-references are set to nil An exception is thrown if the object is resurrected and any new strong-reference to the object is set to nil.

[ ]

Lazy destructor/finalizer. Exactly the same as unmake except the object will go through the destruction process sometime after the conditions for destruction have been met (see unmake above), but not necessarily as soon as they're met (e.g. when the garbage collector wants to). This basically allows some potential garbage collection optimization, at the expense of having a non-deterministic latency between when an object is no longer in use and when the object's destructor is run. The destructor is still guaranteed to always be called as long as the program isn't forcibly killed. An object may either have unmake or lazyUnmake, not both.

init cancelAncestors: init [ ]

Initializer. The are executed on creation of the object. This is like the static block in Java and C++.

Init is run:

  • after any initializations in the object's declaration block
  • after any initializers in inherited objects
  • after objects inherited before it
  • before make if the object was created via a constructor
  • before any program entry-point

If cancelAncestors is written after init, then the initializers of inherited objects are not run. Without it, the initializers of its ancestors, except for ones canceled by younger initializers, are run.

A = { init: wout["A"] } B = { mix[A] init cancelAncestors: wout["B"] } C = { mix[B] init: wout["C"] }
The above code prints out "ABBC", because A's initialization was canceled by B (and so was skipped when both B and C were initialized).

For modules, init can be used to modify the context in which it's used, like adding time standards or string encodings. For normal object, init might be used to do actions that you want to happen even if the object is inherited in an object that overrides its constructor.

[:  parameters ]

Overloads operators ==, !=, <, >, <=, and >= all at the same time. The should only have one parameter (the parameter to compare the object to). Should return true if the value is greater than the object, and false otherwise. It will infer everything else from this return value.

Note that this implicitly creates overloaded operators in the same scope as the object the is defined in, and Lima will complain if the creates a overloaded operator whos parameters conflict with an existing operator.

[ targetObjects]

Declares the platforms and formats the object is intended for. If the object is compiled, it will be compiled into files of different formats based on its targets (keep in mind that modules are themselves objects).

Platforms and formats include optimized code for different languages, readable code for different languages, assembly code output for different architectures, and executable native or byte code in different forms (executable, static library, or dynamic library) for different platforms. Examples of targets: ppc.mac, x86.win32, x86.linux, x86.mac, x86.win32.o, mips, java, llvm.

Is ignored unless the object is the entrypoint being compiled.

[ ;; object-inheritance statement :[ ;; member statement memberName : memberAlias member statements ] ;; object-inheritance statement object2 :[! memberName : memberAlias memberNames ] object-inheritance statements ] : : memberName : memberAlias member statements object2 :! memberName : memberAlias memberNames object-inheritance statements

Makes the privileged members of a previously defined object (or ) available for use in the object (or ) being defined. It has mixin-like semantics: the object gets a copy of the private and public privileged functions and variables of each object passed in the parameters. An object with multiple inheritance involving name collisions must explicitly declare which object it will get its member from.

  • should be a lima expression that returns an object to inherit from.
  • Everything inside the normal colon section ( :[ ] or just v:), including the colon-bracket, is optional. Indicates that only the listed members will be inherited from the .
  • Everything inside the not-colon section ( :[! ] or just v:!), including the not-colon-bracket, is optional. Indicates that all members of the will be inherited, except for the ones listed.
  • For a given object-inheritance statement, there may be up to one colon-bracket operator (either the normal colon-bracket, the non-colon-bracket operator, or neither), defaulting to inheriting all the members when neither colon-bracket operator is used.
  • memberName is the name of the member to inherit (or not inherit in the case of the not-colon-bracket operator). This can use the special operator construct (operator[op]) to indicate the symbols for an operator (whether refering to the original symbol, or aliasing an operator with a different symbol).
  • memberAlias is a name in which the calling object will inherit the member given by memberName. These are optional, defaulting to calling the member by its original name.
  • memberName and memberAlias can use dot syntax to describe how sub-members should be inherited.
    use: someObject[a.x] ;; inherits someObject.a.x (but not any of a's other members) someObject[!b.x] ;; inherits all of someObject, including b, except b.x someObject[! c.x : c.theXX ] ;; inherits all of someObject, including c, except c.x ;; and aliases c.x as c.theXX
Note that colon and bracket syntax can be mixed and matched.

You can use the result of an expression in this custom function by prefixing the expression with the @ sign.

use[ redrobin ;; all members of redrobin are inherited someObj2:[! drogon puff.p ] ;; all members are inherited except drogon someObj3:[! dang yucky ] ;; all members are inherited except dang and yucky someObj4: ;; members inherited under aliases a : memberA b : @"memberB" ;; using the result of a string as the alias name @"c" : memberC ;; using the result of a string as the member name d.x : f.z ;; aliasing a submember onto another member entirely (f) @"e.x" : @"e.xx" ;; string member names for submembers use dot syntax someObj5:! operator[<] ;; inherits all members except the < operator garth : garthAgain ;; and garth is inherited under the alias garthAgain someObj5: operator[<] : operator[<<<] ;; inherits an operator as a different operator symbol ;; inherit members of other modules load["/C/src/lib/aFile2.lima"] ;; absolute path load["../lib/aFile.o"] ;; path relative to the current file load["../lib/aFile3.lima"]:[specialMethod fancyMethod] ]
Aliasing and selective inheritance is used to make inhertiance and module inclusions completely unambiguous.

Since modules are themselves objects, (in conjunction with lib) is used like #include / import in C/Java respectively, extends in java, and for linking to static libraries. does not paste in files verbatim like C includes - only taking code that you need and does not use the rest (preventing program bloat). Also, because of this mechanism, modules that are used in multiple parts of the same program are independantly inherited.

For example, if both modules "A.lima" and "B.lima" use "C.lima", and module "main.lima" uses both "A.lima" and "b.lima", "A.lima" can modify the module variables of "C.lima" as much as it wants, and "b.lima" won't see those changes - it will see the original values or values it has changed them to itself. In addition, "main.lima" won't see any of the module variables of "C.lima" if it didn't use it. In this way, dependencies can be fully separated, and while you can use module variables similarly to global variables, it doesn't pollute the namespace of other modules.

doesn't nest. This means that if you objectA in objectB, and objectB in objectC, it won't be available in objectC unless objectC also uses file A. Also, any modifications to module variables inherited by use will not be seen by other modules/object. This ensures that a module always knows what its dependencies are, and can't be affected in out-of-band ways by code unless a module explicitly inherits it.

[ objects]

This is exactly like , but nests. If the object doesn't use or mix any objects, it mixes in the empty object ({}) by default.

A = {x y} B = { mix[A] } C = { mix[A B] ;; throws an exception because of conflicting members (since B has already inherited members from A) }

Base-10 integer. For example:
var x = 34 var y = 10000003459999
x Integer written in base . This can be used for binary or hex, or any (integer) base from base-2 up to base-36. For bases greater than 10, letters are used for digits higher than 9 (case insensitive). Bases higher than 36 are not supported in this literal format.
var w = 2x101 ;; binary - x gets 5 var x = 16x1A ;; hex - x gets 26 var y = 20xD3A0F495B ;; base-20 var z = 36xzf8943lifpwe ;; base-36
Base-10 real number.
var x = 1.235 var y = .0845
x Real number written in base .
var x = 2x1.1 ;; 1.5 in binary var y = 16x1.8 ;; 1.5 in hex
Stands for infinity. This can be used for creating infinite lists or testing mathmatical operations.
var x = 0..00 ;; returns an infinite list starting with 0

Numbers are objects, but they don't share many members with default object literals. They have more number-specific members. The following operators are overridden:

And the following operators are not inherited.

Members available to all numbers:
str The to-string method returns the number in simple base-10 format.
bits This returns a bits object. This is essentially the way to write a bit-array immediate, for example 16xF041.bits returns a the bits with the bits 1111000001000001. The length of the bit array is the minimum length to store that number. Only defined for positive integers (use ones or twos for negative numbers, or use another encoding).
sign This returns -1 if the number is negative, and 1 otherwise.
sci This returns the number as a scientific notation string (e.g. '4.334*10^6')
engr This returns the number as an engineering notation string. Uses the character 'e' to represent base-10 exponentiation (e.g. '4.334e6').
roman This returns the number as a roman numeral string.
english This returns the number as a plain english string.
engOrdinal This returns the number as an english ordinal string.
error Returns a probability object describing the probabilities that the number has accumulated calculation error from calculations done on the original errorless values it derives from. The calculation error is in absolute deviation from the true value. This can be used to specify the intended precision of numeric operations.
fra x = 5.43 df 10^4..10^5 n: x *= 1+1/n assert[x.error.sum[[.99 < k/k < 1.01]] == 1] ;; assert that the resulting approximate number is always within 1% of the number's true value
Without any assertions like this, the program will calculate exact values, so asserting things about the error will usually make the program less precise (which may allow it to be optimized in some way). However, other assertions later can constrain the error of a value in such a way that it will force a value it derives from to be more precise than it would otherwise need to be.
Methods available to all numbers:
ones length Returns a bit array representing the (potentially signed) number in one's complement representation for a bit-length of length. Will throw an exception if the number can't fit into the given length.
4.ones[5] ;; returns 2x00100 -4.ones[5] ;; returns 2x11011
twos length Returns a bit array representing the (potentially signed) number in two's complement representation for a bit-length of length. Will throw an exception if the number can't fit into the given length.
4.twos[5] ;; returns 2x00100 -4.twos[5] ;; returns 2x11100
sep num character Returns the number modified so that its string representation has separaters every num digits. character is optional, but if included sets the separater character. The default character is a comma. For example: wout 1000000.sep[3] would print 1,000,000 and wout 11110001.sep[4 "'"] would print 1111'0001.
base num Returns the number modified so that its string representation is in base num. For example, wout 7.base[2] prints 111
places num Returns the number modified so that its string representation has a maximum of num decimal places.
fpad num "Front padding". Returns the number modified so that its string representation has at least num digits, padded with 0s if neccessary.
bpad num "Back padding". Returns the number modified so that its string representation has at least num digits, padded with 0s if neccessary.
mod num True modulus (x.mod[n] is the absolute value of x%n)
Binary operators defined for numbers:
a + b Addition.
a - b Subtraction.
a * b Multiplication.
a / b Division. Note that divide by zero throws an exception (it does not return any kind of NaN-type value).
a // b Discrete division (a / b rounded down to the nearest integer)
a % b Modulus, returns the remainder of a//b. This works just as well with rational numbers - it returns a (possibly fractional) remainder after integer division is performed. For example, 4.4%0.8 returns 0.4
a ^ b Exponential power (a to the power of b). This operator is one of the few that are left associative (ie 2^3^4 does not mean the same thing as (2^3)^4)
a b Returns a list of all integer values from a through b (inclusive). For example, 2..5 returns {2 3 4 5}
a ... b

Creates a list representing the fractional range from a to b (inclusive). The list is essentially an uncoutable set. The list is conceptually continuous and so it's length will always be 00. Attempting to access any other index other than 0 will throw an exception (accessing index zero gives you the first value in the range).

While fractional ranges can't be written discretely, they can still be operated on. For example,
var range = 2...5 2 < range ;; returns true 2.34656 < range ;; returns true 1 < range ;; returns false (3...4).all[v < range] ;; returns true (3..4).all[v<range] ;; also returns true .4 ... .3 < 0...1 ;; returns true .4 ... .3 < 0..1 ;; returns false since 0..1 only contains 0 and 1
a & b Logical and. Only defined for values 0, 1, true, and false. The operators &, $, and !$ don't do short circuit evaluation like they do in most Fortran-family languages (although short-circuit evaluation may happen under the hood as an optimization).
a $ b Logical or.
a !$ b Logical xor (exclusive or) returns a&!b $ !a&b.
a == b Returns true if a represents the same numeric value as b.
a < b Less than.
a > b Greater than.
a <= b Less than or equal to.
a >= b Greater than or equal to.
//= %= ^= Like the operators += -= *= /= , sets a variable to some operation of itself with an expression. Can't be chained.

Note: The 12 comparison operators (> < >> << >= <= >>= <<= == <> != !<>) can be chained like in Python, CoffeeScript, and Perl. When the above ten comparison operators are followed by another comparison operator, it will treat the expression as a compound operator. For example, a<b<c will be treated like a<b & b<c, and a>=b == c != d will be treated like a>=b & b==c & c!=d (tho it will make sure if a, b, c, or d are function calls, that they will only be executed only as many times as it was written [once each for this example].)

Unary operators defined for numbers:
- Negative. Returns the negative of a number.
++ Same as +=1.
-- Same as -=1.
! Factorial.

A string is, conceptually, a list of characters.

Lima is designed to operate with any characters, from any encoding. Unlike Python, Lima does not choose Unicode as the be-all end-all standard to convert everything to and from. Unicode, while being a giant leap forward in character encoding, has its own pitfalls and downsides. And unlike Ruby, Lima does not force all strings to carry around their encoding around with them. Instead, Lima does a mix of both: it uses its own abstract character encoding derived from Unicode, but extended to allow representations of characters from any other encoding inside the same string.

Strings are abstract entities containing "characters" that are conceptually encoding independant. In practice, like anything else in Lima, the internals will likely choose different encodings for different situations, using the advantages of different encoding when it makes sense. See the description of LimaEncoding-8 (in the Predefined Variables: Character Encodings section) for description of how this abstract idea is implemented as a real encoding.

Lima defines a character using the encodings the system knows how to operate between. Two characters Character-A from Encoding-A is defined to be the same as Character-B from another Encoding-B if and only if the configured encodings transcode Character A to Character B and vice versa.


A single quoted string literal. String literals can span multiple lines, and use whitespace indentation similar to how white-space indented blocks do - each new line starts at the same vertical position that the first character of the string started on.

var w = 'this is a multi-line string' ;; same as 'this is a'@,'multi-line string' var y = 'this is another multi-line string' ;; same as 'this is another'@,' multi-line string' var y = 'this is not a valid multi-line string' ;; errors because of improper indentation var z = 'this is also not valid' ;; also errors, because the 'n' in "not" is vertically aligned with the quote rather than the 't' in "this"


  • Lima doesn't use escape codes. Refer to the special-character operators for how to write special characters.
  • The "grave accent" ( ` ) is an alias for the apostrophe ( ' ) when being used to surround strings
  • Characters are represented as a string of length 1 - there is no separate character construct. This also means that characters are recursive lists, in a way. Eg. 'a' == 'a'[0] == 'a'[0 0 0 0 0 0 0]
    - its characters all the way down ; )
  • There are no characters for horizontal or vertical tab, delete, bells, or other non-printable characters. Tabs should be encoded as either multiple spaces, or an extended space character (using LimaEncoding's space-exstension marker), or the text should be split up into a list of strings in the case the tab character is being used as a column or list separator. Similarly, there is no difference between a line-separator and paragraph-separator - there is only a new-line character. These concepts can still, of course, be encoded in a byte-array, but are not (and have never been) expressible as (unique) characters.

"" A double quoted string literal. The same as a single quoted string except double-quotes can be part of the string and single-quotes can't.
"""""" A triple quoted string literal. The same as a single quoted string except double-quotes and single-quotes can be part of the string. Also note that if a triple-quoted string ends in more than 3 triple-quotes in a row, the *last* three are the end of the triple quote. For example, """x""""" is the same thing as 'x""'.

Pseudo-operator that creates a string literal with a quote (single, double, or triple) at the front. This is used for creating string literals with a quote at the front. Whether the quote is a double-, single-, or triple- quote depends on what quotes are used to create the string literal. The number of hash marks indicate how many of the quote to write.

#"what" ;; same as '"what' #'what' ;; same as "'what" #"""what""" ;; same as '"""what' ###"what" ;; also same thing as '"""what'

Note that this only has meaning when creating string literals. This operator has no meaning when applied to variables. Really #", #', "#, and '# are core syntax that create a string literal with a newline at the front. But they follow the described pattern.

If a # precedes or follows a quote (of any kind) and is next to other operator-characters, an exception will be thrown because of the ambiguity.

A! + B ;; not the same as A !+ B ##'hi'## @ ##'hi'# #@ ##'hi''##@ ;; This throws an exception


Returns the string with a quote (single, double, or triple) at the end. Whether the quote is a double-, single-, or triple- quote depends on what quotes are used to create the string literal.

"what"# ;; same as 'what"' 'what'# ;; same as "what'" 'what'### ;; same thing as "what'''"

Strings have all the members regular objects (object literals) have, but they also have a couple additional methods over pure object literals, and so they are not interchangable with lists of characters. For example, "hi" is different from {'h' 'i'} in that the former has string operators and methods defined for it, while the latter doesn't.

Special-character operators

Instead of using escape sequences, Lima uses operators. The following operators exist to add special characters onto a string. This summarizes their meanings:


Returns the string with a newline at the front. @"string" is like "/nstring" in C.

Really @" and @' are additional custom-operators that create a string literal with a newline at the front. But they follow the same pattern.

string@ Returns the string with a newline at the end.

Returns the string with an extended space character at the end. The width of the extended space is indicated by number. For example, ""%3 is a single space character with the width of 3 single-spaces. Corresponds to LimaEncodings space-extension codepoint.

string![hexNumber hexNumbers]

Returns the string with a sequence of LimaEncoding characters at the end. Each character is represented by a hexNumber indicating the LimaEncoding code-point or code-points the character is made up from.

"A character: "![5B] "Some characters: "![51 A7 3E2 E6] "character made from multiple codepoints: "![B4F5FC93]

Note that the true operative operators here are ! and a custom bracket-operator.

stringLiteral![base:number numbers]

Just like the normal ![ operator, but the numbers are in base base.

"A character: "![8:2703] "Some characters: "![10: 45 67 42 39]

Additional methods available to all strings:
cat list ... Just like normal cat, but each argument is automatically converted to a string using their str member.
dgt string s Returns true if the caller object is higher order than s based on collation alone (simple order). dgt stands for "dumb greater than". This is the traditional default way strings have been compared in programming.
dlt string s Returns true if the caller object is lower order than s based on collation alone (simple order). dlt stands for "dumb less than". This is the traditional default way strings have been compared in programming.
enc encoding Encodes the string into a bits using the encoding string.encoding[encoding].

Additional members available to all strings:

Returns the string. In the case the string has missing indexes (like if it's a slice), it will print characters in key order (ie. you don't need to do this: aSlice.sort[k].str).
code Returns a string containing the code needed to write the string literal. E.g. for "'someString'"@."and "#."anotherString"#.code returns #"'someString'"#."@."#."and "#."#."#"anotherString"#."#").
lower Lowers the case of all the characters in the string.
upper Capitalizes all the characters in the string.
name Only defined for single characters (strings that are one character long). The name of the character.
Additional operators available to all strings:

Concatenates two strings. This is in large part used for creating string literals with special-characters.

"String"," and another" #"quote"#,", said Tom." ;; literal with quotation marks "Line"@,"Another Line"@ ;; two lines

Note that in addition to the concatentation operator , the related operator @, does the same thing as @ and , separately (eg. "a"@ , "b").

a < b

Returns true if a is a lower order than b in a "natural" ordering.

Natural ordering is where characters that are not digits are compared as normal, but when digits are encountered at the same place in both strings, it parses as many of them as possible into an integer and compares the integers between the two strings. The programmer can use any collation.

a > b

Returns true if a is a higher order than b in a "natural" ordering.

a <= b

Like < but matches for equal strings as well.

a >= b

Like > but matches for equal strings as well.

a..b Returns a list of strings of the same length and less length between a through b (inclusive).
collate[{'a''b''c''d''e''f''g'}]: var x = 'b'..'f' ;; x gets {'b' 'c' 'd' 'e' 'f'} var y = 'cf'..'de' ;; x gets {'cf' 'cg' 'd' 'da' 'db' 'dc' 'dd' 'de'}
a..b Returns a list of strings of the same length and less length between a through b (inclusive).
collate[{'a''b''c''d''e''f''g'}]: var x = 'b'..'f' ;; x gets {'b' 'c' 'd' 'e' 'f'} var y = 'cf'..'de' ;; x gets {'cf' 'cg' 'd' 'da' 'db' 'dc' 'dd' 'de'}

Note that for any of the above comparison operators and the range operator, a collation must be defined for any of them to work.

Any value except nil. A variable of this type can take on a value of any type except nil. Has the following method:
parse varList codeString Parses the codeString into an object. The varList (optional) is an object who's members will be in-scope variables the new function immediate has access to.
var o1 = var.parse['1 2 3'] o1[0] ;; returns 1 var i = 5 var theString = 'a = 5 b = i i = 9 c = fn: wout[self.i]' var obj = var.parse[{i=i} theString] obj.a ;; 5 obj.b ;; 5 obj.i ;; 9 obj.c[] ;; prints 9 var x = 5 var obj2 = var.parse[{a~>x} " p~>a f = fn: p++ " ] obj2.f[] ;; x becomes 6
Boolean values (0 and 1).
Signed integers. Represents all integers from - to positive . Has the following methods:
parse string s Attempts to parse the string as an integer and return the value. Throws an error if the value isn't an integer.
roman string s Attempts to parse the string as a roman numeral and return the value. Throws an error if the value isn't a roman numeral.
english string s Attempts to parse the string as plain english and return the value. Recognizes numbers ignoring spaces and dashes between word-parts. Throws an error if the value isn't an english number.
engOrdinal string s Attempts to parse the string as an english ordinal and return the value. Throws an error if the value isn't an english ordinal.
To define an unsigned integer: type.set[0..00] unsignedIntVariable
bits A an array of boolean values, that also has special methods and operators for bit operations. Bit arrays are structured in little-endian order (least-significant digits first - so 2x0001.bits[0] == 1).
var x = 2x110001.bits x.ins[0 0 0 0] ;; left-shift 3 times, like stack pushes, returns 2x110001000 x.=rm[[[k<2]].sort[k] ;; logical right-shift twice, like stack pops, x becomes 2x1100 ;; most likely, you'll never need to write an arithmetic right-shift: var signBit = x[x.len] var signBitTwoTimes =[signBit] {signBitThreeTimes... x[[v<2]].sort[k]...} ;; returns the number arithmetically right-shifted twice[v&y[k]] ;; retuns x's bits anded with y's (to work properly, x and y should be the same length)[!v] ;; one's complement of the bit array

bits objects have the following members:

num Returns the positive numeric value of the bit array. Errors if the array is empty.
ones Returns the signed numeric value of the bit array interpreted as the one's complement number representation. Errors if the array is empty.
twos Returns the signed numeric value of the bit array interpreted as the two's complement number representation. Errors if the array is empty.
none Returns an empty bits object.

bits objects have the following methods:

dec encoding Decodes the bits into a string with the encoding string.encoding[encoding].
chunk chunkSize Returns an array of integers made from chunks of the bit array. Equivalent to[k//chunkSize].map[v.join[0 v+v2]]
dechunk intList Returns a bits object made from the passed list of integers. Equivalent to bytes.join[bits.none v.ins[v2.bits]]

Signed fractional number. Represents all rational numbers. Has the following methods:
parse string s Attempts to parse the string as a real number in decimal format (eg "1.334") or fraction format (eg "3 3/4) and return the value. Throws an error if the value isn't a real number in decimal format.
sci string s Attempts to parse the string as scientific notation or engineering notation and return the value. Throws an error if the value can't be parsed.
Variables of this type hold a lima string. Has the following members:
chars The list of every canonical character the system knows about. Characters that are equivalent are not both listed, one is chosen arbitrarily.
encodings The list of supported string encodings.

string.encodings has the following additional method:

add name chars Adds an encoding named name. chars is a list of all the characters that encoding can encode, in LimaEncoding (string) form.

encoding objects

encoding objects have the following members:

name Unique canonical name for the encoding. Basic (non-parameterized) encoding names can contain any character except brackets ('[' and ']'), and are case-insensitive ("UTF-8" is the same as "utf-8"). The names of parameterized encodings pass LiON formatted parameters in brackets. For example, a little endian utf-16 string with a tab-width of 5 would have the name 'utf-16[5 "little"]'.
chars A list of all the characters that encoding can encode, in LimaEncoding (string) form.
aliases List of aliases for this encoding. Does not include its canonical name. Adding aliases to this list will allow this encoding to be accessible from string.encodings at that key. Adding an alias who's name is already in string.encodings causes an exception.

List of transcoders keyed by the canonical name of the encoding transcoded to. Only lists original transcoders, does not list deduced (chained) transcoders.

You can add or change transcoders by modifying the list. Adding a transcoder allows it to be used (directly or chained) by other related encodings. Adding a transcoder can also change the chars list (of the encoding or others) to minimize the total number of characters in the system. An exception will be thrown if a key of an unknown encoding is set.

If an added transcoder conflicts with (contradicts) another added transcoder, and exception is thrown. If any character can be transcoded to a different character, by transcoding from LimaEncoding through any chain of transcoders, then some transcoder conflicts with one or more other transcoders. This means that every character in an encoding's chars list must be a unique character.

The transcoder functions that can be added may take two parameters:

  1. A bits object containing the encoded string to transcode.
  2. An object containing the encoding's param member
The transcorder functions must return bits objects.


This member will only exist if the encoding has been passed parameters.

encoding objects have the following methods:


Like a normal bracket operator for accessing object members, but recognizes encoding parmaeters. Encoding parameters are described in the section for the name member of encoding objects. These additional arguments are added into the params member of the returned encoding object. For example, string.encodings['someEncoding[2 "hi" {test="testing"}]'], would return an encoding object with params set to {2 'hi' {test='testing'}}.

fits character Checks if the calling encoding can encode the passed character.
fits encoding Checks if the calling encoding can always be converted into the passed encoding - if that encoding "fits into" the other one (if all characters can be converted from one to the other). encodingA.fits[encodingB] is the same as[encodingB.fits[v]].join[v&v2]
enc theString

Encodes theString into binary. Returns a bits object.

If a character in the string can't be encoded by this encoding, it should call the function contained in the attribute encodingFail. The enc method should *never* throw an exception when a character can't be encoded, although encodingFail can decide to throw an exception.

dec bitsObject

Decodes bitsObject into a string.

If a character in the string can't be encoded by this encoding, it should call the function contained in the attribute decodingFail.

Represents a variable type. Things like templates in C++ or generics in Java would instead be made using variables of this type.
Type methods:
type.set Creates a type from a set of values. Variables defined as that type can only take on those values.
type.set[0 1 2 3 4 5 6 7 8 9] digits ;; a type 'digits' that represents the values 0 through 9
type.cond Creates a type from a condition. Has the implicitly-declared variable v representing the value to be tested (for whether it's included in the type).
type.cond[v>0] ;; all values that are greater than 0 (or otherwise have that opration defined to be true)
An .

A probability map type. Provides a way to query information about probabilities.

probability has the following methods:
mass probabilityMassFunction Returns a probability object derived from the function probabilityMassFunction. probabilityMassFunction must be inversible function where the parameter represents the value, and the result represents the probability at that discrete value.
var p = probability.mass[fn int[x] y: if 0<x<=5: ret 1/20 5<x<=10: ret 3/20 ]]
density probabilityDensityFunction Returns a probability object derived from the function probabilityDensityFunction. probabilityMassFunction must be inversible function where the parameter represents the value, and the result represents the probability density at that value.
var p = probability.density[fn x: ret e^(-x^2/2)/(2*pi)^.5 ]]

The above probabilityMassFunction and probabilityMassFunction parameters must be inversible functions where the parameter represents the value, and the result represents the probability at that discrete value.

probability objects

probability objects (ones returned by probability.mass or probability.density) have the following methods:
value The normal bracket operator. Accesses the probability of a discrete value.
slicingStatment The normal object-slicing operator. Can accesses values and their probabilities that match certain conditions. Using this operator on a probabiliy defined by a density function, this will always return 0. If Lima ever introduces infintesimal numbers, this will change to returning an infintesimal.
sum slicingStatment

Returns the sum of probabilities for the given slice. This is like the double-bracket operator, but can't use the implicitly-declared variable v.

This is shorthand for pmap[[slicingStatement]].join[0 v+v2].

percentile percentileNumber Returns the value at a given percentileNumber through the distribution. Percentile is given as a value from 0 to 1. Throws an exception if the value-set is not ordered.
pmap.percentile[0] ;; minimum value pmap.percentile[1] ;; maximum value pmap.percentile[.6] ;; 60th percentile

Parameterized types are functions that return a type.


Type that can only take a list (an object with consecutive integer keys starting from key 0).

list! x = {4 5 6}

For an object to match the list! (or list[]) type, it must also still have all the default operators, methods, and members defined (they can be redefined but not removed).


Returns a list type that can contain members of the type given in the brackets.

list[int] x = {4 5 6} list[int$string] = {5 6 'hi'} list[list[int]] y = {{1 2} {5 9} {100 101}}
Note that when a type is passed into list via the , it does not need to be appended with the reference access operator (~).

An alias for 0, meant to represent boolean values. The only difference between and 0 is that 's str method returns 'false' instead of '0'. 0 and compare as equal with ==.
An alias for 1, meant to represent boolean values. The only difference between and 1 is that 's str method returns 'true' instead of '1'. 1 and compare as equal with ==.
standardLibrary This object represents the entire standard library of Lima. This automatically used in every module, but if it is explicitly used, you can alias or exclude certain parts of the standard library.
;; includes the whole standard library except wout, and aliases depend to buildDependency ;; (which frees that name up for something else) use[standardLibrary:[! wout depend:buildDependency]

An object for creating continuations that can be ed to. When copied, returns the current continuation (the line after the line executing the copy of contin). This is similar to a jump-label in C.

Returns a type that can only hold a contination object.

See the section on jump for details.

Object containing methods related to references.

Because of this, if you want to pass a reference into a function, you need to use the reference access operator (~).

var x = 5 var p ~> n wout[p] ;; prints 5 p = 6 ;; n is now 6 var y = 90 p ~> y wout[p] ;; prints 90

Has the following operators:

Returns a reference type that can reference anything except nil.
? Returns a ref type that can also point to nil.
ref? ;; reference to anything, including nil.

Returns a reference type that can reference the type given after the dot. Arguments to it are pseudoMembers. If the type is contained in a variable, the variable name can be used. If you want to create a type with an expression, you need to surround it by parens. x ;; reference to an integer ref.(int) y ;; also a reference to an integer ref.(int$fra) z ;; reference to an int or fra

The resulting type also has a dot operator that can further specify the types the variable can point to. It also has the ? operator that can specify that its allowed to point to nil. ;; reference to a reference to an integer ref.( ;; same thing ;; reference to an int or nil

Returns a value that, when copied, copies to a reference of the input value. This can be useful for returning references from a function, creating lists of references, and can be important in cases where function arguments are mutated and may be either references or non-references.
var a = 5 var b = 6 var x ~> a ;; x points to a var y = ref[b] ;; y points to b var z = {ref[a] ref[b]} ;; z gets a list of references var func = fn one two three: one ~> b two = ref[b] ret ref[three] ;; returns a reference ;; here, result gets a reference to a, ;; x gets pointed to b instead of a, and ;; z[0] doesn't change, but a gets pointed to b var result = func[x z[0] a]

Reference objects

Reference objects pass all accesses through to the object it points to, except for the following operators:


Reference access operator. An operator that accesses a reference itself, as opposed to as what it points to (what usually happens). You would use this to return a reference from a function, put a reference into an object immediate, or point one reference to another.

X = fn: int r=5 ref! x x ~> r ;; x points to r var A = {reference: x~} ;; an object that contains a reference wout[A.reference] ;; outputs 5 ret A.reference~ ;; returns the reference to r

The more ~'s in the operator, the farther down in to the reference chain it accesses.

x~~ would access the reference x points to (like *ptr in C). x~~~ would mean to use the reference that the reference x points to, points to (like **ptr in C), etc. More examples:
int: r=5 t=36 ref!: x y x ~> r ;; x points to r y ~> x~ ;; y points to x y ~~> t ;; x is repointed to t var A = {x~ y~ y~~} ;; A is a list that contains the three references (x, y, and x again [what y points to]) ret A[1]~~ ;; returns the reference to t (y holds a reference to a reference to t)

This is also used to indicate that you want to use an or custom function rather than call it on the proceding code. For example,

{int~ fra~ var~} ;; building an object that contains type attributes function[if~ df~] ;; passing the core custom functions if and df into a method

Variables can be set to references, but as usual it creates a copy.

var[x=5 y=6] var ptr ~> x var a = ptr~ ;; a now also points to x, but it is a copy ptr ~> y ;; ptr points to y, but a still points to x wout['ptr: 'ptr', and a: 'a] ;; prints 'ptr: 6, y: 5'

Using the ~ operator on an object that isn't a reference, , or custom function returns the object itself (ie a no-op) except that it no longer has the ~ operator. This means that while 5~ is legal, 5~~ will throw an exception.


Sets the reference that a ref points to. The number of tildes (~'s) tell you how "deep" the assigning goes. a[b c] int[x y] b ~> x ;; b now points to x a ~> b ;; a now points to x (since b points to x) a ~~> y ;; this sets whatever a points to (i.e. b), to now point to y c ~> b~ ;; c now points to what b points to (i.e. y).

Note that multiple tildes in this operator works the same way as the unary ~ operator.

a ~~> b ;; means the same as: a~ ~> b a ~~~> b ;; means the same as: a~ ~~> b ;; and a~~ ~> b
Also note that x~ = y~ is the same as x ~> y~~ if x and y are both normal references.

f = fn int x: ret x+1 g = fn int x: x++ h = fn x: x++ main = fn: int i = 10 p1 p2 p1~ ;; exception (p1 doesn't have a value yet) p2 ~> p1 ;; type mismatch exception (p2 can't point to an int) p2 ~> nil ;; type mismatch exception (p2 can't point to nil) p2 ~> p1~ wout[p2] ;; undefined variable exception p1 ~> nil wout[p2] ;; type mismatch exception (p2 can't point to a reference containing nil) p1 ~> i f[i] ;; returns 11 f[p1] ;; returns 11 f[p2] ;; returns 11 g[i] ;; i is now 11 g[p1] ;; i is now 12 g[p2] ;; i is now 13 h[i] ;; type mismatch exception (h doesn't take an int value) h[p1] ;; type mismatch exception (h doesn't take an int value) h[p2] ;; type mismatch exception (h doesn't take an int value) h[p1~] ;; i is now 14 h[p2~] ;; type mismatch exception (h doesn't take a value) h[p2~~] ;; i is now 15

weakRef Just like ref, but is a weak reference. A variable will implicitly get a strong-reference if it isn't declared as a weak reference. All weak references are nullable, and so the ? operator doesn't exist for weakRef.
var x = 5 var p ~> x ;; strong reference ref p2 ~> x ;; another strong reference weakRef p3 ~> x ;; weak reference weakRef p4 ~> nil
When an object that a weak-reference is destroyed, it is automatically set to nil
meta Object with methods for reflection and meta programming. This follows the design principle of mirrors.
Returns a type that can only hold a meta object (an object returned by meta's bracket operator).
? Returns type that can hold a meta object or nil.
object Returns an object containing meta data about the passed object. The returned object has the following properties:
  • operator - an object mapping operators to their functions
  • privilaged - a list of public, privilaged members
  • public - a list of normal public members
  • private - a list of private members
  • hidden - a list of hidden members
Note: accessing an operator function is a good way to work around objects with operator conflicts. You can specify which operator function to use. Eg: meta[obj1].operator[+][obj1 obj2]
snapshot[object time] Returns an object that represents the state of the object at a particular time in the program. The time can be in the future. If the time is before the variable was created, it returns nil.
var t1 = time.before: var x = 5 var t2 = time.after: x = 8 var oldx = snapshot[x t1] ;; returns 5
set Returns a random value in the set using a random number generator chosen by default criteria. Returns the same distribution of values the following generator would return:
rand.generator[fn properties: var p = properties[10'000] ;; indicates that the probability maps should be consistent for data-set ranging from 1 byte to 10 thousand bytes long ret p.entropy[[k>.99]] > .99 ;; hard to guess values & p.mean[[k<.5]]-p.mean[[k>=.5]] < .05 ;; relatively symmetric averages & p.chiSquare[[0.8<k<1.2]] > .95 ;; reasonable normalized chiSquare values & properties.cycleLen > 10^6 ;; long cycle length & properties.seedable ]
generator accept

Returns a random-number generator with specific properties described by the arguments. seedable is a boolean value describing whether the random number generator needs to be seedable (default is true). accept is a function that should return true if a random-number generator's attributes meets it's requirements. accept takes one parameter of the same form that rand.addGenerator takes.

The random-number generator that rand returns has the following method:

set Returns a random value from the list set. set may not be an infinite list.
seed The seed used to seed the random number generator. It is a const variable (after being set once). If rand's bracket operator is used before rand.seed is set, it will be seeded automatically by the selected random number generator. Setting the random seed really should only be used for debugging purposes. This member only exists for random number generators that are reproducible.

rand[1..800] ;; returns a random value between 1 and 800. ;; does the same thing, but ensures that the random number generator used ;; doesn't restart its cycle until after 9 million numbers have been generated, ;; and at least 99.5% of the time has a normalized entropy of greater than .99 ;; for generated data with a length less than or equal to 1000 bytes. rand.generator[fn properties: if properties[1000].entropy[[k>0.99]] > .995 & properties.cycleLen[[k>10^9]] == 1: ret true else ret false ][1..800]
addGenerator name properties Adds a random-number generator named name to the list of available generators. properties describes the properties of the generator and has the following members and operators:
cycleLen A probaility map for how many numbers it generates before its pattern repeats
seedable True if the random-number generator is seedable, false if not.
sequenceLength Returns an object containing attributes of a particular random number generator. Each member is a probability map, mapping a particular measurement to its probability, that is consistent for all byte-sequences generated by the random-number generator with lengths less than or equal to sequenceLength. The members are:
  • cycleLen - how many numbers it generates before its pattern starts over again
  • entropy in entropy per bit (a value between 0 and 1) - describes how easy it is to guess any given generated value.
  • chiSquare - the normalized Chi-square value (Chi-square per degree of freedom)
  • mean - the arithmetic mean of the bits generated normalized by dividing by the number of bits (so that the value is between 0 and 1).
  • monteCarlo - the arithmetic mean normalized by dividing by the number of bits .
  • serialCorrelation - the serial correlation coefficient (a value between 0 and 1). Roughly describes the extent to which each value depends on the previous value.
  • kComplexity - the Kolmogorov complexity divided by the number of bits (also a value between 0 and 1). Roughly describes how compressible generated values are.
See for more info on randomness tests.
generators Pseudo member that returns a list of available random number generators, keyed by name. The list can't be directly modified (its modified through addGenerator). The values associated with those keys are objects - containing properties of the random-number generator - of the same form that rand.addGenerator takes as an argument.

indicates that the programmer doesn't care which value is chosen - ie. it is arbitrary as far as the programmer is concerned. This might be used for example when you want to obtain one item out of a list being used as a set, but don't care which one it is. You could ask for the first of the set, but if you don't need that one specifically, you should ask for an arbitrary one: arb[aSet].

Similar to rand, arb takes a list of values that may be returned. Like the concept of a don't-care in digital logic design, the compiler will attempt to use values in the list that optimize the program's speed or memory usage.

Returns an arbitrary value that is in the .

can allow you to do things that almost no other programming language can: performance-based behavior. For example, you could choose between three different algorithms for text differencing: private: algorithms = { minimal: fn[ ... ], ;; produces the smallest output light: fn[ ... ], ;; uses the least memory quick: fn[ ... ] ;; runs the fastest } textDiff = arb[algorithms] And in your main program: optimize[5*cputime + 2*memory]: var textDiff = load['textDiff'].textDiff var diff = textDiff[file["someFile.txt"]] wout[diff] ;; output on the console Each of those algorithms would produce different output (otherwise there would be no need to use ), but the appropriate one would be used for the system its running on based on the optimize statment.

con con represents the console (stdin, stdout, stderr). Returns a chan object where in represents stdin and out represents stdout. Does not inherit the close method. The conObject attribute can be used to override console functionality.
Members of con:
err Operates just like the out member except for stderr instead of stdout.
enc Returns a string containing the standard name of the encoding used by the console.
Methods of con:
cr A "real carriage return" (returns to the beginning of the line).
bs Backspace. Note that the affect of this function depends on the state of the console. For example, you can't usually backspace a newline when writing to the console.
name Name of the command used to invoke the program. This is the value you get in main's argv[0] in C.
file Name of the file the currently running line of code originated in.
line Line number of the line of code in the original source file.
character Character at which the current statment started in the original source file. If the currently running line has more than one statement, this would identify which statement by its starting position.
pid Process ID of the program.
start Time the program started running.
end Time the program will end (used for describing things that should last til the end of the process).
vendor Name of the maker of the compiler or interpreter.
version Version of the compiler or interpreter
environ Holds a list of the environment variables given to the process. process.environ members can be added and modified, but this will not affect the rest of the system.
rmSigtermHandler[] Removes the default SIGTERM handler, which terminates the process.
rmSigintHandler[] Removes the default SIGTERM handler, which terminates the process.
rmSighupHandler[] Removes the default SIGTERM handler, which terminates the process.
executablePath args input cwd env

Starts a process, who's path or name are held in executablePath, with the command-line arguments args, held in a list of strings. input (optional) is the stdin input piped into the process. cwd (optional) sets the current working-directory for the new process, the default is the directory it lives in. env (also optional) sets the environment variables for the new process.

Returns a process-object (which implements the interface of future):

Process-objects have the following properties:
pid The processes id.
wait: A custom function that waits until the process is complete before running the passed statements. See the section on future for more info.
out A string object representing the final state of the process's stdout.
err A string object representing the final state of the process's stderr.
code The exit code of the process.
state Returns
  • 'active' if the process is still running,
  • 'done' if the process has completed, or
  • 'dead' if the process has been killed.
signal[signalName] Sends a signal to the process. The signal can be one of the following
SIGTERM Not supported on Windows
SIGTERM and SIGINT have default handlers on non-Windows platforms that resets the terminal mode before exiting with code 128 + signal number. This default behaviour can be removed by calling process.rmSigtermHandler[] and process.rmSigintHandler[].
SIGINT Can usually be generated with CTRL+C (that may be configurable).
SIGHUP Generated on Windows when the console window is closed, and on other platforms under various similar conditions, see signal(7). The signal can be handled, but the process will be unconditionally terminated by Windows about 10 seconds later. On non-Windows platforms, the default behaviour of SIGHUP is to terminate the process, but this default behaviour can be removed by calling process.rmSighupHandler[].
SIGBREAK On Windows, delivered when CTRL+BREAK is pressed. On non-Windows platforms there is no way to send or generate it.
SIGWINCH Delivered when the console has been resized. On Windows, this will only happen on write to the console when the cursor is being moved, or when a readable tty is used in raw mode.
SIGKILL Not a handlable event (will not create an event in the signals list), it will unconditionally terminate the process on all platforms.
SIGSTOP Is not handleable (will not create an event in the signals list).
kill Kills the process immediately. Calls with wait will throw an exception.

raw systemCommand Executes the systemCommand string as a raw system call.

A chan object is a construct that can facilitate communication between processes. They are similar to channels in Go and in Stackless Python. They're also related to the concept of a pipe (e.g. in unix).

A channel consists of an input list and and output list. Reading from the output list will block until the value asked for is known. The copy operator of chan creates a new empty channel, so all you need to do to create a new channel is var newChannel = chan.

var c = chan thread: list.sort[] c.out.=ins["done"] var a =[0] ;; waits for any input on the channel to arrive, and a gets "done" when it does

Channels can be used to communicate between thread blocks, but channels are also used as the conceptual basis for network communication - because you usually don't know exactly when information might arrive on a socket.

chan objects

Objects created by copying chan have the following members:

in Conceptually, the list of all items that have been and will be recieved on the channel. Copying the in member creates a new object containing the same list of items and will receive all the same items that the original in member gets. This ensures that multiple receivers are guaranteed to see all items sent, even if the original in member removes items (e.g. if it uses it is a queue, cutting items from the front).
out The list of items that have been sent on the channel. Sending on the channel is done by appending to this list. Copying the out member creates a new object containing the same list of items, but when modified only shows modifications to the copy, not the original. This allows multiple senders to send items and keep track of just the items they sent. This object can only be appended to - elements, once written, can't be changed or removed. Also, creating values at keys that are not the next position in the list will throw an exception.

Objects created by copying chan have the following pseudo-members:

close Closes the channel - at this point the length of the in member is known.
unmake On unmake, the channel's close method is automatically called.

Objects created by copying chan have the following methods:

|| otherChannel Combines two channels. Sending on a combined channel will send on both (multicast), and receiving from a combined channel will receive the first message that comes in on either channel (multiplex).
var channels = c1 || c2[0] ;; gets the first item that comes in on either of the channels
Object containing methods for dealing with files. The behavior of this object can be overridden using the fileObject attribute.

Opens the file that has the given path. Creates the file if it doesn't exist and creates any directories needed for the path. filePath is a platform-independant path string in the style of unix filepaths. The bracket-operator will concatenate any path parts passed (just like wout). It is opened for reading and/or writing (depending on how the program will use the file).

Since filePath is in unix-style, the folder separator is '/'. The root folder is '/' and paths that don't begin with a '/' character are relative paths. Also, '..' represents the previous directory. For example, the windows path "C:\someFolder\someFile.txt" would be represented as "/C/someFolder/someFile.txt" in Lima. In a system without a root, like windows, '/' can be represented, but can't have anything written to it in the normal way (you'd have to mount a new drive to add something at the root). Filepaths must be transcodable to the encoding dir.enc, and if not, an exception will be thrown.

Returns a file! object representing the bits inside the file (bits representing the raw bytes, without OS specific conversions). The file can be written to as if it was a and has all the methods and operators a normal bits-object has. Can throw some errors, including: "unable to write", and "unable to overwrite", and "unable to open".

The returned object contains the following pseudo-members:
name Holds the file's name.
dir Holds a dir object representing the directory the file is in.
type Holds the type of file this object represents. The values this can take on is filesystem dependent, but will generally be one of the following:
  • file
  • blockDevice
  • characterDevice
  • symLink
  • fifo
  • socket
close This pseudomember closes the file. Attempting to read from or write to the file will reopen it.
rm This deletes the file.
flush This flushes the buffer for the file. Should only be used if something absolutely needs that file to be updated immediately.
stats Contains filesystem specific information about the file. An example of stats info for a file in a usual linux file-system:
{ dev= 2114, ino= 48064969, mode= 33188, nlink= 1, uid= 85, gid= 100, rdev= 0, size= 527, blksize= 4096, blocks= 8, atime= time[2011-08-10 23:24:11], mtime= time[2011-08-10 23:24:11], ctime= time[2011-08-10 23:24:11] }

This is a associative array that allows access to the file's metadata.

If the filesystem doesn't natively support arbitrary metadata, lima will create a hidden file in the same directory named ".metadata" and store the metadata there in LiON format encoded in UTF-8. The top level value will be an object that contains the metadata keyed with the name of each file or directory within the directory the ".metadata" is in. The value at each file/directory key will be an object containing the metadata.

filesystem Returns the filesystem in which the file resides. Has the same members and methods that the filesystem member of a dir object has.
The returned object contains the following methods:
move stringPath Renames/moves the file to the new path and name.
unmake The file destructor flushes and closes the file.
dec encoding Returns a similar file object, except that it contains string characters instead of bits. encoding should be the string-name of the encoding. If encoding is not passed, this will use the string at metadata key "fileEncoding" as the encoding, if available. If not available for whatever reason, an exception will be thrown.
filePathencoding Returns a string-file obtained by decoding the file. This is the same thing as:
;; var x = file[path encoding] var x = file[path].dec[encoding] x.meta["fileEncoding"] = encoding
This construction ensures that the file will record its encoding for later use.
Returns a type that can only take on a file - an object returned by this object's bracket operator described above.
Object with methods for dealing with directories in the filesystem. The behavior of this object can be overridden using the dirObject attribute. Methods:
pathString Returns a directory literal. If the directory does not yet exist, it does not create the directory, and the object's methods treat it as if it was an existant, but empty, directory. pathString contains the directory path. pathString should have the same system-independant unix-style path that file takes as a file path. Directory paths must be transcodable to the encoding dir.enc, and if not, an exception will be thrown.
The returned object contains the following pseudo-members:
path Returns the system-independant path string of the directory. Always contains a '/' at the end.
make Creates the directory and any directories it needs to create its path. If a directory already exists, nothing will be done. If something exists with that name that is not a directory, an exception will be thrown.
dirs A list of child directories keyed by their names.
files A list of files in the directory keyed by their names. This list includes any file-system entity that isn't a directory.
up Returns the parent directory if the directory object isn't the root. Returns nil if the directory object is the root.
rm Deletes the current directory and any containing files and folders.
str Its to-string method, prints the directory path.

Returns an object that has information about the filesystem and functions for converting to and from filesystem-specific values.

It has the following members:

enc The encoding used by the filesystem (how it stores filenames, etc).

It has the following methods:

toNativePath limaPath Returns a native path for this filesystem from the given system-independent path.
fromNativePath fileSystemPath Returns a system-independent path given the given filesystem path.

The returned object contains the following methods:
move stringPath Renames/moves the directory and all of its contents to the new relative or absolute path. Will throw an exception if something already exists at that path.
Members and unary operators:
Returns a type which constrains an object to only take on a directory.
cur A representing the current working directory of the program.
home A representing the directory the executable lives in.
system Methods for making system calls. The encoding used for this is the same as con.enc. The behavior of this object can be overridden using the systemObject attribute.
name The name of the operating system.
version The release version of the operating system.
machine The machine-name/hosname of the OS.
platform The platform of the OS (ie win32, etc).
arch The architecture of the OS (ie x64, etc).
tempdir Returns a dir object representing the system's default directgory for temporary files.
endian The endianness of the cpu. Either "BE" or "LE".
uptime Returns a dur object representing the amount of time the system has been on for.
mem An object containing the following members
  • free - number of free bytes of memory in the system
  • total - number of total bytes of memory in the system
hosts An object where each key is a hostname defined by the OS (/etc/hosts and the like), and each value is an adr! representing the address given to that hostname.
cpus A list of objects with info about each available cpu/core. Each object has the following structure:
  • model - the name of the cpu
  • speed - the speed of the core in MHz
  • times - contains an object of the time in milliseconds the core spent in various categories:
    • user
    • nice
    • idle
    • irq
environ Holds a list of the environment variables set for the operating system (or current user). When system.osEnviron members are be added or modified, this will make changes to the whole system as well as automatically change the corresponding value in process.environ.
processes Returns the list of process objects for all the processes the user is allowed to see on the system.
process pid Returns a process object representing the process with the given pid.
host network hostname Returns an adr! object representing the primary address for the hostname. This takes into account the system's defined hostname overrides (in /etc/hosts and the like).
shape An object that has methods for representing and operating on shapes of any kind including volumes, areas, lines, and points.
Returns a type that enforces variables to hold shape objects.
shape! a = shape[3 4]
coordinate1 coordinate2 coordinate3 coordinates Creates a shape object representing a point in n-dimensional space. Any number of coordinates can be used; coordinates default to 0 when unspecified.
var a = shape[1 4 0 6]
coordinateList1 coordinateList2 coordinateLists Creates an shape object representing a line-segment, surface, or other n-dimensional shape. Every point on the surface of any line connecting two sets of coordinates is included in the shape, as is any point contained within the closed object created that way.
var a = shape[{1 4 0} {1 4 0}] ;; a line-segment var b = shape[{1 4 0} {1 4 0} {0 4 3}] ;; a area/surface made from 3 points var c = shape[{1 4 0} {1 4 0} {0 4 3} {1 4 3}] ;; a volume made from 4 points var d = shape[{1 4 0 9} {1 4 0 3}] ;; 4-dimensional line-segment
relation Creates an shape object represented by the equation.
var a = shape[relation[x y: x^2=5*y^2]]
shape1 shape2 Creates an shape object created from two regions where all the space between the regions is filled in, just like in the bracket-operator with coordinate-lists.
var a = shape[ shape[{1 4 0 6}] shape[{1 4 0 9}] ] ;; a line-segment var b = shape[ shape[{0 0} {1 2}] shape[{3 0} {2 2}] ] ;; a trapezoid
shape objects have the following binary operators:
a & b Set intersection (conjunction). Returns a shape that contains the regions that exist in both a and b.
a $ b Set union (disjunction). Returns a shape that contains the regions that exist in either a and b or both.
a !$ b Set exclusive disjunction. Returns a shape that contains regions that exist in only a and regions that exist only in b but not regions that exist in both.
a == b Equality. Returns true if all the regions contained in a are also contained in b.
a <> b Same as ==
a !<> b Same thing as !(a <> b)
a < b Proper subset of shapes (returns true if a is a proper subset of b).
a > b Proper superset of shapes (returns true if a is a proper superset of b).
a <= b Improper subset of shapes (returns true if a is an improper subset of b).
a >= b Improper superset of shapes (returns true if a is an improper superset of b).
shape objects have the following methods:
dist otherShape Returns a real number distance between the closest points from each shape.
fra x = shape[3 1].distance[shape[1 1]] ;; returns 2
shape objects have the following members:
center Returns a shape's center point (as a shape).
regions Returns a list of regions where each region is continuous.
surface Returns a shape that describes only the surface of the object (and gets rid of any region within that surface).
zone An object that has methods for representing a region of the world. Coordinates used for this are latitude and longitude (latitude has first ordinality) and are expressed in degrees. The region is not required to be continuous. Inherits from shape.
Additional members of zone
Additional methods of zone
own Returns the default locale set on the computer, or nil if there is none.
time timezoneString timezoneStrings Returns a zone representing the region of the timezone named in the parameters, each of which should be in Olson timezone format.
var z = zone.time["America/Los_Angeles" "America/New_York"] ;; returns a zone containing both the PST and EST time zones.
language languageCode languageCodes Returns a zone representing the region where the languages given are official languages. The languages should be in ISO 639-2 format. Throws an exception if a language doesn't exist.
var z = zone.language["eng" "fre"] ;; the zones where english and french are official languages
language timeObject languageCode languageCodes Just like the above except it takes into account the time given.
var z = zone.language[time[-500-01-01] "eng"] ;; the regions where english was spoken at 500 BCE
country countryCode countryCodes Returns a zone representing the region where the countries given are (at the time the function is called). The countries should be in ISO 3166 format (all representations are accepted and case doesn't matter). Throws an exception if a country doesn't exist.
var z =["arm" "cn"] ;; a zone containing the regions of China and Armenia
country timeObject countryCode countryCodes Just like the above, except it takes into account the time given.
var z =[time[-500-01-01] "arm" "cn"] ;; a zone containing the regions of China and Armenia
Additional members of zone! objects
timezones A list of tz database timezones intersecting the zone (in Olson timezone format).
Additional methods of zone! objects
languages timeObject A list of langauges spoken within a zone in order of popularity (how many people speak it) during a given time. If the time is omitted, the current time (when the method is called) is assumed.
countries timeObject A list of countries within a zone during a given time.
collations timeObject A list of collations within a zone during a given time.
An object that has methods for representing and operating on date and time. The internal representation is intended to be continuous and unambiguous (without oddities introduced by time zones, daylight savings time, or leap-seconds). The unambiguous internal representation can, of course, be converted to any time format.
after: statements A custom function that returns a time object representing the current time directly after the statements have run.
Time capturing is still allowed on machines that don't have the actual date or time availble to it, as long as the times are only used to create durations.
In Lima, there is no "now". Because statements can be reordered to optimize performance, there would be no way to guarantee that a time is captured both after its preceding statements and before its following statements without destroying lima's ability to reorder statements optimally. So instead, Lima allows you to choose to either bind a time before some given statements or before them, but not both. If you need both, you need to create two separate time objects.
;; the following to sets of statements can be run completely in parallel ;; which means that ta1 may be the exact same time as tb1 ;; and also means that tb2 might even be earlier than ta2 (if 'B.txt' takes less time to download and output) var ta1 = time.before: var ta2 = time.after: var x = downloadFile['A.txt'] outputFile['some/path/A.txt', x] var tb1 = time.before: var tb2 = time.after: var y = downloadFile['B.txt'] outputFile['some/path/b.txt', y] ;; the following statement guarantees that the time is as close the console output as possible var t = time.before: wout['The time is: 't] ;; the following statement throws an exception because t isn't declared until after the wout statement var t = time.after: wout['The time was: 't]
before: statements A custom function that returns a time object representing the current time directly before the statements have run.
resolution A duration object representing the time between "ticks" (between value changes of the clock - ie values of now and ).
accuracy A probability map describing how accurate the clock is likely to be at the moment this member is accessed. The object maps the likely difference, in seconds, to its probability (value from 0 to 1).
;; if there's at least a 90% likelyhood the absolute deviation is less than half a second if time.accuracy[[-.5<k.5]] < .9: resyncClock[] ;; made up function that might resync the clock from the internet
tai Same thing as time.ts['tai'].
utc Same thing as time.ts['utc'].
unix Same thing as time.ts['unix'].
tdb Same thing as time.ts['tdb'].
Returns a type that enforces variables to hold time objects.
time! t = time[2012/04/04 3:00']
ts timeStandardCodename Returns an object with methods for handling dates for a particular time standard. A list of time-standards is below this section. When used with this method, the time standard names are case insensitive.
The same bracket operator time.ts['tai'] has.
p pattern

Custom function that creates a time-pattern that can be used to both parse and format time objects. Keywords and strings are concatenated to describe the pattern. The $ operator can be used to indicate that more than one pattern can be used. Parts that are surrounded by parentheses are optional (the whole thing in parens should match, or none of it). Expressions with parens are nestable.

time.p[M ' - ' d] ;; parses something like "April - 2" time.p[h ':' min (':' s)] ;; can parse both "8:30" and "8:45:20" time.p[h (':') min ((':') s)] ;; can also parse "830", "8:4520", "845:30", and "84520" time.p[M '-'$' - ' d] ;; can parse both "April-2" and "April - 2" time.p[h$hp ':' min] ;; can parse both "8:30" and "08:30"

To use the value of an expression inside time.p, prefix the expression with the @ sign. Time patterns can be used with this method as well as stringable values.

var x = ":" var timeOfDay = time.p[h @x min @cat["|"x"|"] s] time.p[y'.'m'.'d' ' @timeOfDay] ;; matches '2000.04.03 3:05|:|00'

Any keyword can also be explitly set with the equals sign. If such an assignment is done, that keyword is ignored when parsing

time['20:00' time.p[M='April' h min] ]

The available keywords and their meanings depend on the particular time-standard being used.

addSimilar originalStandard newStandard convertTo convertFrom Adds a time standard named newStandardbased on originalStandard. The new time-standard and the result of a timeObjects' ts method for the new standard will have all the same members and methods as the originalStandard.
add name staticMembers standardObject Adds a time standard from scratch, named name. staticMembers should be an object containing the members you'll get when you access time.ts[name]. standardObject should be a function that takes a time! object and returns the object you'll get when you access timeObject.ts[name].

Time objects

Time objects represent a non-ambiguous date and time. Time objects do not carry information about any particular location or zone, they represent the absolute time. A time-object can be converted to any representation, even ambiguous representations, for any zone or area.

time objects have the following methods:
format timePattern timeZone translator The same format method timeObject.ts['tai'] has.
ts timeStandardCodename Returns an object with methods for creating modified time objects from the current one related to the given time-standard.
time objects have the following members:
tai Same thing as timeObject.ts['tai'].
utc Same thing as timeObject.ts['utc'].
st Same thing as timeObject.ts['st'].
unix Same thing as timeObject.ts['unix'].
tdb Same thing as timeObject.ts['tdb'].
time objects have the following binary operators:
a - b Difference of times and subtractions of durations.
time[2012/04/05 20:00] - time[2012/04/05 17:00] ;; returns an object equal to dur[3:00] time[2012/04/05 20:00] - dur[3:00] ;; returns an object equal to time[2012/04/05 17:00]
a < b Time less than.
time[2012/04/05] < time[2012/04/01] ;; returns false
a <= b Time less than or equal to.
a > b Time greater than.
time[2012/04/05] > time[2012/04/01] ;; returns true
a >= b Time greater than or equal to.

Time standards

Time standards in Lima include both a proper time-standard (a definition of a second under various circumstances) and a calendar standard. A particular time standard will have different but similar methods for parsing dates Standards should usually have a default pattern so the pattern need not be more fully specified, but some standards have the ability to further define a particular date pattern.

tai International Atomic Time (TAI) is a coordinate time standard.

Members available to time.ts['tai']

years A set of dates each representing the beggining of a year in TAI in ascending order.
wout[time.tai.years[0]] ;; prints "0001-01-01 00:00:00" df time.tai.years[[time[1999] < v < time[2020]]] y: wout["It was a dark and storymy night, in the year "y.tai.y"...."@]
months A set of dates each representing the beggining of a month in TAI in ascending order.
weeks A set of dates each representing the beggining of a week in TAI in ascending order. Days of the week start on monday.
wout["Lets say all the days of the week. Together now:"@] var someWeek = time.tai.weeks[0] df (1..7).map[someWeek+ v*dur[/1]] v wout[v.tai.format[time.p["DW"]]@]
days A set of dates each representing the beggining of a day in TAI in ascending order.
hours A set of dates each representing the beggining of an hour in TAI in ascending order.
minutes A set of dates each representing the beggining of a minute in TAI in ascending order.
seconds A set of dates each representing the beggining of a second in TAI in ascending order.
Note: In all the above members (years, months, weeks, days, hours, minutes, and seconds), key 0 maps to the first of that time-unit starting in year 1 CE, and negative keys map to earlier time-units.

Methods and operators available to time.ts['tai']

dateString timePattern translator

Returns a time object of the dateString as parsed with timePattern. If the string can't be parsed, an exception is thrown. If no pattern is passed in, the default pattern is
time.p[(y('.'m$mp('.'d$dp))) (h$hp':'min(':'sr)) (' 'era)]. translator (optional) is a function that takes in one of the english date-words and returns a word from another language.

time.ts['TAI']["2012/06/04 15:00"] ;; June 4th, 2012 at 3:00pm TAI time.ts['TAI']["20120604" time.p[ympdp]] ;; June 4th, 2012 (at 0:00, ie 12:00am) TAI var translator = fn x: if x.lower=='april': ret 'shigatsu' else ret x time.ts['TAI']["20120403" time.p[ympdp] translator] ;; shigatsu 4th, 2012 (at 0:00, ie 12:00am) TAI
Will throw a parse exception if the string can't be parsed properly.

Will throw a contradictory-date exception if the string is parsed properly, but certain redundant information is inconsistent.

;; the following throws an exception, since April 3rd 2012 was a Tuesday time.ts['TAI']["Saturday, April 3, 2012" time.p[DW', 'M' 'd', 'y]]

timePattern should be a value returned from time.p.
dateText Like the bracket operator above, but instead of being passed a string, is instead passed literal text. Always uses the default for timePattern from the bracket operator with two parameters and doesn't use a translator.
time.ts['TAI'][2012.06.04 15:00] ;; June 4th, 2012 at 3:00pm TAI

Time pattern keywords available to time.ts['tai']

The following keywords can be used in the pattern. Keywords are either all uppercase or all lowercase. Uppercase keywords always describe a part of time as a word (e.g. 'April' or 'Saturday').

era The era (either 'CE' or 'BCE').
cent The century (eg. time['1994'].cent would return 20)
y The absolute value of the year (for use with era eg. "300 BCE").
yp Shortened year or "year, pivoted" (eg. "96" instead of "1996"). Must be accompanied by the py keyword.
py Pivot year - two digit years of this number or lower are interpreted as in the current century. Two-digit years above this number are interpreted as in the previous century.
;; usually, this should be explicitly set rather than matched in a formatted string time['04/03/50' time.p[py=50 mp'/'dp'/'yp]] ;; same as time[2050.4.3] time['04/03/51' time.p[py=50 mp'/'dp'/'yp]] ;; same as time[1951.4.3]
dy The numeric day of the year (e.g. 51 for time[1986/02/20])

m The numeric month where 1 is Januray and 12 is December.
mp The numeric month padded with 0 for months less than 10 (eg. 08 instead of 8).
M The month as a word (eg. February).
MS The month as an abbreviation (eg. Feb).
d The numeric day of the month (eg. 20 for time[1993/04/20]).
dp The numeric day of the month padded with 0 for days less than 10 (eg. 08 instead of 8).

dw The numeric day of the week where 1 is Monday and 7 is Sunday
DW The day of the week as a word (eg. 'Tuesday')
DWS The day of the week as a short word (eg. 'Tue')
h Hour in 24-hour time (eg. 15 for 3:00 pm)
hp Hour in 24-hour time padded with 0 for hours less than 10 (eg. 08 instead of 8).
hh Hour in 12-hour time (eg. 3 for 3:00 pm)
hhp Hour in 12-hour time padded with 0 for hours less than 10 (eg. 08 instead of 8).
MER Meridian indicator (eg. "am").
min Minute.
s Integer second.
sr Real number second, includes fractional seconds in the decimal of the number (eg. "8:01:23.54").

TAI members of time-objects: timeObject.ts['tai']

Have the following members:

era The era as a string.
y The number of years.
m The number of months.
d The number of days.
h The number of hours.
min The number of minutes.
s The number of seconds, possibly fractional.
time Returns a time object representing the (now possibly modified) tai time.
The members era, y, m, d, h, min, s can be modified, and modifying them mutates the tai object. Also, for the numeric members (all except era), setting the member to a negative number will roll back that many extra time-units before the next highest time-unit, and setting the member to a number greater than is allowed inside the next highest time-unit adds that many extra time-units.
var x = time[2008/04/03 2:30:00].tai x.s -= 40 ;; x now represents time[2008/04/03 14:29:20] x.min += 100 ;; x now represents time[2008/04/03 15:09:20] x.y -=1 ;; x now represents time[2007/04/03 15:09:20] x.time ;; returns time[2007/04/03 15:09:20] ;; note that subtracting a year as a duration is different from subtracting it as a part of a TAI time ;; since things like leap years need to be taken into account var y = time[2008/04/03 3:09:20] - dur[1/] ;; time[2007/04/04 9:09:20]

Has the following methods:

format timePattern translator Returns a string representation of the time according to the stringFormat. If no pattern is passed in, the default is time.p[y'/'m'/'d' 'h':'min':'s' 'tz]. translator should be able to translate from the english forms of each non-numeric keyword (era M MS DW DWS MER).

st Standard Time. A locality specific time-standard. Has all the members and time-pattern keywords that time.ts['tai'] has, as well as the following additional ones:

Members available to time.ts['st']

jumps A list of time objects each representing the time after a non-linear time jump (usually daylight-savings time jumps). The list is sorted by increasing time.
var now = time.before: var nextTimeJump =[[v>now]][0] var timeOneSecondBeforeJump = nextTimeJump -= 1

Methods and operators available to time.ts['st']

dateString timePattern translator Exactly like the bracket operator(s) of time.ts['tai'], except supports time-zones. If no time zone keyword is used to parse the string (tz, TZN, tzo, or tzom), UTC is assumed. If no pattern is passed in, the default pattern is
time.p[(y('/'m$mp('/'d$dp))) (h$hp':'min(':'sr)(' 'tz))].
time.ts['st']["2012/06/04 15:00"] ;; June 4th, 2012 at 3:00pm UTC time.ts['st']["2012/06/04 15:00 America/Los_Angeles"] ;; June 4th, 2012 at 3:00pm PST time.ts['st']["20120604" time.p[ympdp]] ;; June 4th, 2012 (at 0:00, ie 12:00am) UTC time.ts['st'][2012/06/04 15:00] ;; June 4th, 2012 at 3:00pm UTC time.ts['st'][3:00] ;; 3 hours in to the common era (usually the meaning is as a duration of 3 hours)

Time pattern keywords available to time.ts['st']

Has all the time-pattern keywords that time.ts['tai'] has, with the following additions:
tz Time zone as a local time-zone id (eg. "America/Los_Angeles").
TZN Time zone as a name (e.g. "Pacific Standard Time").
tzo Time zone offset - whole number of hours offset from UTC, including sign (e.g. "-3" in "April 3 2401 8:00 Z-3:30").
tzop Time zone offset, padded. (e.g. "-03" in "April 3 2401 8:00 Z-03:30").
tzom Time zone offset minutes - whole number absolute value of minutes offset from UTC (e.g. "30" in "April 3 2401 8:00 Z-3:30").
Note about time zones: time-zone abbreviations are non-standard and in some cases ambiguous, so Lima doesn't support them.

UTC members of time-objects: timeObject.ts['st']

Has all the members timeObject.ts['tai'] has with the following addition:
TZN Time zone as a name.

Has all the methods timeObject.ts['tai'] has. Note that any translater used should also be able to translate time zone names and abbreviations.

Has the following methods:

format timePattern timezone translator Just like the format methods for tai time-objects (timeObject.tai), except a timezone can be passed. If no timeZone is passed in, the default is UTC.

utc ut0 ut1 ut1r ut2 ut2r Versions of Universal Time. These have all the same methods, operators, and time-pattern keywords time.ts['tai'] has.
GPS Global Positioning System Time
TT Terrestrial Time
TDB Barycentric Dynamical Time - a relativistic dynamical time.
UNIX Unix timestamp, the number of TAI seconds since Jan 1, 1970 00:00 UTC.

Methods and operators available to time.ts['unix']

unixTimestamp Returns a time object representing the time indicated by unixTimestamp. unixTimestamp must be a number (can be fractional).
var x = int.parse[915148801.25] time.ts['unix'][x] ;; 1999/01/01 00:00:33.25 TAI time.ts['unix'][1095379200] ;; 2004/09/17 00:00:32 TAI

Members available to timeObject.ts['unix'] for a given time-object

stamp Returns the unix timestamp as a number.
var t = time.ts['unix'][1095379200.54] ;; 2004/09/17 00:00:32.54 TAI t.ts['unix'].stamp ;; returns 1095379200.54
s The integer timestamp rounded to the nearest second (standard unix timestamp).
var t = time.ts['unix'][1095379200.54] ;; 2004/09/17 00:00:32.54 TAI t.unix.s ;; returns 1095379200
ms The integer timestamp rounded to the nearest millisecond.
Note that when dealing with a date format with ambiguous dates for a particular time-standard, the earliest possible time is assumed to be the one meant.

dur An object that has methods for representing and operating on durations. Uses a constant definition for date parts:
  • y: year = 365.25 days (A Julian Year) or 12 months
  • m: month = 30.4375 days (Based on the Julian Year)
  • week = 7 days (168 hours)
  • d: day = 24 hours (86,400 seconds)
  • h: hour = 60 minutes
  • min: minute = 60 seconds
  • s: second = an SI second
Returns a type that enforces variables to hold dur objects.
dur! t = dur[3:00]
duractionText Returns a dur object of the duractionText parsed as a duration. The pattern it parses off of is
time.p[(y'/')$((y)'/'m('/'d))$('/'d) s$((h)':'min':'(s))$(h':'(min))].
dur[15] ;; 15 seconds dur[15.45] ;; 15.45 seconds dur[150] ;; 150 seconds dur[:15:0] ;; 15 minutes dur[:15:] ;; 15 minutes dur[15:0] ;; 15 hours dur[-7:00] ;; negative 7 hours dur[0:30.5] ;; 30 minutes and a half (30 minutes and 30 seconds) dur[:30:29.56] ;; 30 minutes, 29 seconds, and 560 milliseconds dur[/5/1] ;; 5 months and 1 day dur[23/9] ;; 23 years and 9 months dur[10/] ;; 10 years dur[-23/10/1] ;; negative 23 years, 10 months, and 1 day dur[5/1] ;; 5 years and 1 month dur[2/8/23 3:00:00.05] ;; 2 years, 8 months, 23 days, 3 hours, and 50 milliseconds

dur["00"] is a special case indicating an infinite amount of time. Note that dur[00].time == time[00].

duractionString Like the bracket operator above, but instead of writing literal duractionText, a string is passed instead. Uses the same pattern from the bracket operator with the duractionText parameter. If the string can't be parsed, an exception is thrown.
dur["15:00"] dur[":30:29.56"] dur["-23/10/1"] ;; same as dur[-23/10/1]

Duration objects

Duration objects represent a length of time.

dur objects have the following members:
str The to-string member. The pattern output is: time.p[y'/'m'/'d' 'h':'min':'sr]
dur objects have the following methods:
timePatternKeywords Returns an object that counts the time only in terms of the parts given in the timePatternKeywords. timePatternKeywords are similar to the input into time.p. The resulting object will have members (of the same name as the time pattern part) representing the amount of time where, all added up, they equal the original duration. The smallest time unit can have a fractional value, all others will be integers.
dur[15:00][d h s] ;; returns {d=0 h=15 s=0} dur[34 4:34:23.4][h min] ;; returns {h=820 min=34.39} var pattern = time.p[y'-'m'-'d] dur[3/4/20][@pattern] ;; you can use time.p objects like that: returns {y=3 m=4 d=20}
y The number of years.
m The number of months.
w The number of weeks.
d The number of days.
h The number of hours.
min The number of minutes.
s The number of seconds.
dur objects have the following binary operators:
a + b Addition of durations, or addition of a time and a duration.
time[2012.04.05 20:00] + dur[3:00] ;; returns an object equal to time[2012/04/05 23:00] dur[3:00] + dur[..24] ;; returns a dur object representing 24 days and 3 hours
a - b Subtraction of a duration.
time[2012.04.05 20:00] - dur[3:00] ;; returns an object equal to time[2012/04/05 17:00] dur[..24] - dur[3:00] ;; returns a dur object representing 23 days and 21 hours
a * b Duration multiplication. Multiplies a duration. One of the operands must be a dur object, and the other must be a number.
3*dur[2:20] ;; returns an object equal to dur[7:00]
a / b Duration division. Divides a duration. The first operand must be a dur object and the second must be a number.
dur[1:00]/4 ;; returns an object equal to time[0:25]
a < b Less than comparison between durations.
dur[1:00] < dur[4:00] ;; returns true
a <= b Less than or equal to comparison between durations.
a > b Greater than comparison between durations.
dur[1:00] > dur[4:00] ;; returns false
a >= b Greater than or equal to comparison between durations.
time objects have the following unary operators:
-a Negative. Returns the negative of the duration.
-dur[1:00] ;; returns negative 1 hour var x = dur[-2:00] ;; negative 2 hours -x ;; 2 hours

Handles facilities for IP addresses and ports. Has the following methods.

addressFormat Custom operator that creates an address including IP-address, port, and scope. Takes in a few different formats that it detects automatically:
  • IPv4 address or IPv6 address.
  • Address plus port.
  • Address plus port.
  • Address, port, and scope id (only for ipv6 addresses, defaults to global scope if not given)
  • String of one of the previous three formats
  • adr object plus port (errors if adr object already has a port)
  • string ip address plus port (errors if adr object already has a port)
  • adr object, port, and scope (errors if adr object already has a port or scope)
var address = adr[] var fullAddress = adr[] var ipv6Address = adr[2001:db8:85a3:8d3:1319:8a2e:370:7348] var fullIpv6Address = adr[[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443] var s1 = "" ;; ipv4 address var s2 = "" ;; ipv4 address plus port var s3 = "2001:db8:85a3:8d3:1319:8a2e:370:7348" ;; ipv6 address var s4 = "[2001:db8:85a3:8d3:1319:8a2e:370:7348]" ;; ipv6 address (with brackets) var s5 = "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443" ;; ipv6 address and port var s6 = "[2001:db8:85a3:8d3:1319:8a2e:370:7348%3]:443" ;; ipv6 address, scope, and port var ipaddress = adr[s1] var ipaddressWithPort = adr[s2] var ipv6Address = adr[s3] var ipv6AddressToo = adr[s4] var ipv6AddressWithPort = adr[s5] var ipv6AddressWithPort = adr[s6] var addrAndPort = adr[ipaddress 5000] var addrAndPort = adr["" 5000] var addrAndPortAndScope = adr[ipaddress 5001 3]
adr! The unary exclamation mark returns a type that can only take on an IP address object.
4 number port Returns an adr object containing the IPv4 address number represents.
var ipv4Addr = adr.4[500049000] var ipv4Addr2 = adr.4[500049000 4000]
6 number port scope Returns an adr object containing the IPv6 address number represents.
var ipv6Addr = adr.6[3000298888837700 6000 3]

Also has the following pseudo-member:
own An object where each key is the name of the network interface/adapter (human readable, not necessarily standard), and each value is a list of adr! objects each containing the address for the network adapter.

The object returned by the bracket-operator has the following members:

type Returns the name of the address family (usually 'ipv4' or 'ipv6').
str Returns the string form of the address (eg. "").
num Returns the address as an integer.
port The port number (nil if not set).
scope The scope value. Throws an exception if not an ipv6 address.

dns Object containing methods for dns address lookup. Note that this ignores any system-defined names (/etc/hosts and the like), so if you want those to be included, use The behavior of this object can be overridden using the dnsObject attribute. Has the following methods:
[] name type class edns=false deviceAddress=adr.own[0]

Returns the dns answer to the query. name is the name of the resource you want to query about (usually either a hostname or an ip address). type is the the dns rrtype ('A', 'AAAA', 'MX', 'TXT', 'SRV', 'PTR', 'NS', 'CNAME', 'SOA', 'NAPTR', etc). class is the numerical representation of the class of service (usually 1 for internet). deviceAddress (optional) is the address of the device to query from.

The answer is an object that looks like:

{ header= { qr=<query response> opcode=___ aa=<authoratative answer> tc=<truncation bit> rd=<recursion desired> ra=<recursion available> rcode=<response code> } answer=___ authority=___ additional=___ }
where answer, authority, and additional are lists of resource record objects that look like this:
{ type=<dns rrtype> ttl=___ ... ;; additional fields }
The additional fields in resource record objects are the following (by type)
type properties
  • primary
  • admin
  • serial
  • refresh
  • retry
  • expiration
  • minimum
A and AAAA
  • address - an address (adr! objects) for the host hostString.
  • priority
  • exchange - the hostname of the mail exchange
  • data - the string text record
  • priority
  • weight
  • port
  • target
  • data - a name server hostname
  • data - a canonical name record
  • data
  • order
  • preference
  • flags
  • service
  • regexp
  • replacement

ips string hostString adr! deviceAddress Resolves a domain name to a list of IP addresses. Returns a list of adr! objects with the addresses. deviceAddress (optional) is the interface do request from. This is the same thing as:
dns[hostString 'A' 1 deviceAddress].map[v.answer] & dns[hostString 'AAAA' 1 deviceAddress].map[v.answer]
hosts adr! address adr! deviceAddress Reverse resolves an ip address to a list of domain names. Returns a list of hostnames corresponding to that address. deviceAddress is optional. This is the same thing as:
dns[address.str 'PTR' 1].map[v.answer]
alternate adr! address string type Returns an object that also has the ips and hosts, and can also be called as a function, but calls the dns server specified by address with the protocol specified by type ('udp' or 'tcp'), rather than your router's default dns server.
servers A member that returns the list of DNS servers the operating system uses.
socket Object for handling raw internet packets. The behavior of this object can be overridden using the socketObject attribute. Has the following method:
make deviceAddress

Initializes and returns a raw packet stream with the given destination. Returns a chan object that receives IP packets of any version. deviceAddress is the device to listen from and is optional (defaulting to adr.own[0]). Outputting on out sends a packet, and new packets will come in on in (if its listening). The in property always has the encoding "bits". On call of close the socket will be closed.

listen deviceAddress

Starts listening to the socket. Can restart listening on a socket that was previously closed. Returns the calling object (for chaining).

;; processes all the packets that come through (infinite loop) var server = socket.listen[] df packet: if packet.version == 4: ;; ipv4 var p = ipv4[] var ipv4Packet = ipv4[ ttl = 30 protocol = 17 destination = p.source data = udp[dest=8000 data="hi"].serialize ] server.out.=ins[ipv4Packet] ;; sends the IPv4 packet with a UDP message

override overrideFunction

Globally overrides the socket. overrideFunction is a function that takes one argument (the object socket before it was overridden) and returns a socket! object. After being overridden, the original object socket accessed anywhere in the program (ie not just in the current module) will be the result of calling the overrideFunction with the appropriate argument.

This override could be used for selectively mocking external connections - for testing purposes.

overide overrideFunction Alias of override.

ipv4 IPv4 packet object. The behavior of this object can be overridden using the ipv4Object attribute. Has the following methods:
make ttl protocol destination data

Creates an ipv4 packet.

make bitList

Creates an ipv4 packet from bits.

And has the following members:

version The IP protocol version (4 for this).
ttl Time to live.
protocol Transport layer protocol number.
destination An object containing an adr! object representing the address the packet was addressed to.
bits Contains the packet's bits serialization.

ipv6 Just like IPv4 except with IPv6 members instead. The behavior of this object can be overridden using the ipv6Object attribute.

Object for handling UDP packets. The behavior of this object can be overridden using the udpObject attribute. Has the following methods:

listen port deviceAddress Starts listening on the given port, giving the program an exclusive lock on that port. deviceAddress is the device to listen from and is optional (defaulting to adr.own[0]). Returns an object that contains all the packets that arrive on the socket from the time listen was called.
init destinationAddress port deviceAddress Initializes and returns a UDP packet stream with the given destination and optional source port (defaulting to an ephemeral port). deviceAddress is the device to listen from and is optional (defaulting to adr.own[0]). The returned object will send to the (destination) address and port given.
make source dest dataByteList Creates a UDP packet. This isn't needed for normal UDP handling (as packets are constructed automatically), but it can be useful if creating packets for direct socket use.
make bitList Unserializes a bits bit-list object into a udp packet.

UDP sockets
The objects returned by udp.listen and udp.init have the following members:
in Contains a list of all the UDP packets that will arrived on the udp socket from the time listen was called to the time the udp port is closed.
out Appending a packet to this list sends that packet out on the socket. Strings, bits, or UDP packets can be appended to out. The UDP packet's source and destination will be used, if different from the UDP socket's.
source An object containing an adr! object representing the source address - the address from which packets will be sent. Holds nil if not listening.
dest An object containing a list of adr! objects representing the addresses to which messages will be sent (regardless of which address messages are received from, if the object is listening on a port). Holds an empty list if not initialized.
open Contains true if listening, false if closed.
close Pseudo-member that causes the object to stop listening to the port, and returns the lock on that port. Note that a properly running Lima runtime will properly close all connections even if the program itself unexpectedly fails.
They also have the following methods:
listen port deviceAddress Just like udp.listen, except causes this object to listen on that port. If the object was already listening on a port, it stops listening to that port before listening on the new one.
init address port deviceAddress Atomically sets or resets the address and port the object is configured to send to.

Note: Copies of udp, tcp, http, and raw socket objects that were listening/connected when copied are also listening/connected.

UDP packets

The UDP packet objects returned by udp's constructor and read from the out list of a UDP socket have the following members and methods:

source An object containing an adr! object representing the source address.
dest An object containing an adr! object representing the address the packet was addressed to.
bits Contains the packet's bits serialization.

var udpSocket = udp.init[adr[].listen[8080] var message =[0] ;; gets the first packet if message == 'hi': udpSocket.out.ins['hi 'message.source] ;; send message back else: udpSocket.out.=ins["Go away, I don't like you"]

Object for handling TCP connections. The behavior of this object can be overridden using the tcpObject attribute. Has the following methods:

listen port encodingName connections deviceAddress

Listens for connections on the given code. encodingName is required and defines how to decode incoming bytes and encode outgoing bytes. connections is optional. If connections isn't given, listen will connect to the first incomming computer and return a single tcp stream object. If connections is given, listen connects to that number of connections and returns a list of those connections. deviceAddress is the device to listen from and is optional (defaulting to adr.own[0]).

Lazily executes - doesn't block execution. Execution won't be blocked until the program needs information it is waiting for. Note that this means your program can use and process the first connection, even if listen is still listening for more connections.

connect address encodingName sourcePort deviceAddress Attempts to connect to an address from the sourcePort. encodingName is required and defines how to decode incoming bytes and encode outgoing bytes. sourcePort is optional and if not given, an arbitrary free port (ephemeral port) will be used - basically a port tcp.ports[arb[0..ports.len]]. deviceAddress is the device to listen from and is optional (defaulting to adr.own[0]). Returns an open connection.
make source dest seq data ackNumber flags windowSize urgentPointer options Creates a TCP packet. This isn't needed for normal TCP handling (as packets are constructed automatically), but it can be useful if creating packets for direct socket use.
Has the following member:
ports List of open ports.

TCP sockets
The objects returned by tcp.listen and tcp.connect have the following members:
in A sequence (or bits object, depending on the encoding) of characters that will arrive on the TCP socket from the time the connection was established to the time it will close.
out Appending strings to this list sends the characters of that string out on the TCP socket. Strings or bits can be appended to out.
open Contains true if the connection is open, false if closed.
close Psuedo-member that closes the connection. Does nothing if connection is already closed.
source An object containing an adr! object representing the source address.
dest An object containing an adr! object representing the destination address.

var connection = tcp.listen[8080] ;; connects to another computer var messages =[''@] ;; messages delimited by newlines if messages[0] == 'hi': connection.out.=cat['hi'] ;; send a message back else: wout[messages[0]] ;; print message

Object for handling HTTP requests. The behavior of this object can be overridden using the httpObject attribute. Has the following methods:

listen port encodingName deviceAddress Listens for incomming http requests. encodingName is optional, and defines how to decode the HTTP request and encode the response. If encodingName is not defined, Lima will attempt to get the encoding from the request's "Content-Type" header, using "bits" if none is found. deviceAddress is optional. Returns a list containing all the HTTP requests that will arrive on the HTTP socket from the time listen was called to when the socket is closed.

make host method resource query headers body deviceAddress Creates an http request object. deviceAddress is optional. query should be an object that will be built into a query string.
makeQuery objectMap Makes a query-string from an object. Shouldn't usually be needed since this is done automatically by http's constructor and the query parameter of http objects
urlDecode s Decodes a string passed as a url parameter.
urlEncode s Encodes a string for use as a url parameter.
cookie name value expire Creates a cookie. The following members can also be explicitly set: domain, maxAge, path, secure, httponly. Note that maxAge will delete the 'expire' member if set (and vice versa).
var listener = http.listen[8080] ;; connects to another computer df request: request.respond[503 nil "we're not on speaking terms"] var messages = connection.split[''@] ;; messages delimited by newlines if messages[0] == 'hi':['hi'] ;; send message back else: wout[messages[0]] ;; print message

HTTP request objects
The objects returned by http's constructor and http.listen have the following members:
method The http method.
resource The requested resource.
version The http version (eg. 1.1)
source An object containing an adr! object representing the source address. This is only populated on the computer receiving the request.
open Contains true if the connection is open, false if closed.
close Psuedo-member that closes the connection. Does nothing if connection is already closed.
query The query string. Also has the member:
map The query parameter map (from parsing the query string). Throws an exception if not able to parse the query string.[0] ;; get the first userid parameter in the query string
headers A list of header values keyed by their name.
body The http body (as a string). Also has the following members:
map The http body parsed as a parameter map ("post parameters"). Attempting to access map throws an exception if the body is not able to parsed as a query string.
str Returns the request as a string. This can be used to send the packet out through a TCP socket.

HTTP request objects also have the following methods:

respond status headers response keepalive Responds to the request. Closes the connection after the request is sent unless keepalive is passed in as true. Note that keepalive will keep the connection open forever until explicitly closed by one side or the other.
send address sourcePort deviceAddress Sends the HTTP request to the given address and returns an HTTP response. address can either be an adr! object, or it can be a string host. If address is a string, the host header will automatically be set if it hasn't been already.

HTTP response objects
HTTP response object have the following members:
status The http method.
version The http version (eg. 1.1)
open Contains true if the connection is open, false if closed.
close Psuedo-member that closes the connection. Does nothing if connection is already closed.
headers A list of header values keyed by their name.
body The http body (as a string). Also has the following members:
map The http body parsed as a parameter map ("post parameters"). Attempting to access map throws an exception if the body is not able to parsed as a query string.
str Returns the response as a string. This can be used to send the packet out through a TCP socket.

Object for handling HTTPS requests. Has the same interface as http. The behavior of this object can be overridden using the httpsObject attribute. HTTPS request objects also have the following member:

auth Returns the auth scheme used.


A list of objects representing the state of input devices like keyboards and controllers. Each object in the list of inputs will have a number of keys, and each value at a key represents the state of that key at a given time. Many keys only have two states, and will then usually be represented as true or false. Some other keys represent inputs with multiple states, like dials or scrollers, and will usually have some number representing its state.

Note that the state of inputs are accessible regardless of whether the window is on top or in focus.

The behavior of this object can be overridden using the inputsObject attribute.

Inputs are intended to be used with event semantics:

change[inputs[0].a]: if inputs[0].a: ;; keydown else: ;; keyup

Like any input object, this can have more or less members depending on the device. The members representing button states can be written to programatically for any device. This value remains valid until the real device changes to a different state (note that if the real device changes to the state the device was already changed to programatically, it is as if the change never happened).


An object representing the state of the keyboard.

The members of the object are named after the keys they attach to.

if keyboard.f: wout['f is down'] if keyboard['=']: wout['equals is down'] ;; non-letter keys are accessed as string keys if keyboard.f3: wout['f3 is down'] if keyboard.shift && keyboard.3: wout['shift is down *and* 3 is down, is # down?'] keyboard['$'] ;; always false for most keyboards, since there is no dedicated $ key (its a different state of the 4 key)
Keyboards also have a combo sub-object (which is an empty input object if a keyboard doesn't exist). These differentiate between keyboard modes that change when certain keys (like shift, fn, caps lock, ctrl, or alt) are activated or held down, if the keyboard has specific meanings for combinations of state.
if keyboard.combo['#'] && ! keyboard.combo['3']: wout['# is down, but 3 is not'] keyboard.combo['#'] && ! keyboard['3']: ;; for most keyboards, this is always false, because 3 and # are the same key

The keyboard object always has the following member:

active This is true if a keyboard exists, false if not. This member is read-only.

This object always exists, even if there is no mouse or keyboard. The touch and keyboard objects are also in the list of inputs, but this is shorthand.

Keyboard and touch objects are typed such that all members are allowed to be nil. This way, if an input button doesn't exist, any code can still access the button member and will not throw exceptions - likely simply ignoring the related code since the state will never change. This promotes cross-system compatibility and graceful failure.


A list object representing the state of the mouse, mice, and/or touch-events - any device that has a position on the screen.

The list object itself has the following member:

max The maximum number of cursors it supports. If this number is 0, it means there is no mouse/touch device.

The objects the list holds may have the following true/false members:

p A shape object containing the point at which the mouse/touch cursor is on the screen.
pri State of the primary mouse button. Usually points to either L or R (usually L), but may also be its own member in touch-screen cases (which don't have L or R).
sec State of the secondary mouse button. Usually points to either L or R (usually R).
L State of the left mouse button.
R State of the right mouse button.
M State of the middle mouse button.
The objects the list holds can have the following integer member:
scroll State of the scroller.

when touch.any.p < p[{0 0}{0 10}{10 0}{10 10}]: ;; do something when any mouse cursor hovers over the given square area

round value valueSet towardValue Returns the value rounded toward towardValue in the set valueSet. valueSet is optional (defaulted to the set of integers - since numbers are what are usually rounded). towardValue is optional, without it rounds to the nearest value in valueSet, with it rounds toward that towardValue. Throws an exception if the value is not comparable with items in valueSet.

;; all of these are true: ;; numbers round[34.245] == 34 round[34.5] == 35 round[34.8[v*.5]] == 35 round[34.7[v*.5]] == 34.5 round[34.7 int.set 0] == 34 round[34.7[[v*5]] 0] == 30 round[-34.7 int.set 0] == -34 round[34.7 int.set 0] == 34 round[34.7 int.set 00] == 35 round[34.7[[v*6]] 00] == 36 ;; 6*6 == 36 and 6*5 == 30 (so 36 is closer) round[-34.7 int.set -00] == -35 round[19.8 int.set 15] == 19 round[14.8 int.set 15] == 15] ;; time var t = time[2012-04-03 12:00] ;; rounds to the nearest week (weeks here defined as starting on monday) round[t time.tai.weeks] == time[2012-04-02] ;; rounds to the nearest wednesday round[t[v+dur[/0/2]]] == time[2012-04-04] t.tai.round[t time.utc.years] == time[2012-01-01] ;; midday counts as nearest to the next day (like .5 being rounded as closest to 1) t.tai.round[t time.utc.days] == time[2012-04-04] ;; rounds to the nearest day that is the 9th of a month t.tai.round[t[v+dur[/0/9]]] == time[2012-04-09] ;; rounds to the nearest 1/3 of a day (again rounding up in this case) t.tai.round[t[v - dur[/1]/3]] == time[2012-04-05 16:00] ;; rounds toward time 0 - in other words time[1/] or time[0001/01/01 00:00:00] t.tai.round[t time.months dur.0.time] == time[2012-04-01] ;; rounds toward time infinity (rounds up) t.tai.round[t time.months dur.00.time] == time[2012-05-01]

mag value Returns the magnitude of a value. The value can be any number (int, flo, fra, complex, etc), or vector of numbers. It should be noted that this function is equivalent to the absolute value when used on non-vector numbers.
sir radians Sine for radians
cor radians Cosine for radians
deg Converts degrees to radians. For example, [90] would be /2 (radians).
Gets the sign of a non-complex number. Returns either 1 or -1.
.base value
access get set

Returns an accessor object. The parameter get is the getter function for the value, and set is the setter function. All other operations on the object are performed on the result of the getter function.

This is useful for defining getters and setters for an object.
var o = { internalX = 5 x = access: get=fn: return internalX set=fn v: internalX = v }
load module type

Loads a resource from a library. Usually this is loading a module (as a static or dynamic library). module can either be a module-path (explained below) to the file containing the module, or it can be a file! object that contains the file. type is optional (default='module') and describes whether to load the resource as a file or as a module. If type is "file", it will return a file! object containing the file. Otherwise, load will return an object representing the module in the file.

A module-path is similar to how node.js require takes paths. Module resolution is done as follows:

  1. If the path starts with "./" or "../", the file is looked for at the given path relative to the loading module
  2. If not, a "modules" folder is looked for in the directory containing the loading module, and
    1. If found, the file is looked for at the path relative to that "modules" folder.
    2. If not found, step B is repeated for the parent directory
    3. If still not found, step C is repeated until there are no more accessible parent directories
  3. If the module still isn't found, it is looked for in the "dependencies" list in the "package.lima" file
    1. If found and the module-path is just the module's name, the file at the path listed in the property "main" in that module's "package.lima" file, is loaded
    2. If found and the module-path is more than just the module's name, the file at that path relative to that module's folder, is loaded
    3. If still not found, the program attempts to find the module in the system's (current computer's) module repository. If not found there, the program attempts to download the module and install its dependencies in the system's module repository. The module then has a hardlink to the module in the system's module repository created in the 'modules' folder in the same directory as the entry-point file being run. On systems that don't support hardlinks, a copy is made.
    4. If still not found, the program attempts to download the module and install its dependencies. The module is downloaded to a 'modules' folder in the same directory as the entry-point file being run.
    5. If downloading the module or installing it doesn't work, load throws an exception

use[load['./crypto-v1.0.0.lib']] ;; loads crypto methods in the current scope md5['something'] ;; hypothetically in the crypto library customLibraryFunction[45 'do something!'] ;; hypothetically from the customLibrary module ;; you can also use it as a standalone object var websocket = load['websocket'] websocket.connect['ws://localhost'] ;; calls the connect function from that library

Examples of types of packages you would use with this: lima source files (uncompiled libraries), compiled static libraries, shared libraries (DLLs, so files), executables (loads it like a shared library). If one of the filenames a string ending in ".lima", ".o", ".dll", ".so", etc, it loads the code at the given path and uses the entire file as an object.

"package.lima" is a file, much like node.js's "package.json", containing information about the current package, and the dependencies of that package. It can have the following properties (unless marked, a property is optional)

  • name - (required)
  • version - (required)
  • description
  • keywords
  • recommended - a list of optimization packages that will likely work well with the module.
  • homepage
  • bugs
  • repository
  • license
  • author
  • contributors
  • ignore - files and folder to ignore when publishing the package
  • dependencies - a list/object of modules a package depends on. Each item in the list/object is one of the following:
    • <name>@<version> - downloads the specified <version> of the package <name> from lime (lima's package manager - like gems for ruby, cpan for perl, or npm for node.js). <version> should be of the form N.N.N where each N is an integer.
    • <name>: <url> - downloads the package <name> from the given url. It expects the downloaded file to be an archived tarball.
    • <name> - Downloads the latest version of the package <name> from lime. After downloading, the package.lima file will be updated with the specific version downloaded (turning it into the <name>@<version> format).
  • private
  • postinstall - a list of dependencies, how to check if they're installed, and how to install them if they're not.

    Each item in the list should have the following format:

    { name = name ;; name of the dependency check = checkFunction ;; returns true if the dependencies it is responsible for are met build = buildFunction ;; builds a dependency }
    If a build member isn't given for a particular dependency, it indicates that the dependency can't be met automatically. If a dependency isn't met and can't be met automatically, an exception will be thrown containing a list of the names of the dependencies that can't be fulfilled.

  • location - a path to where external modules should be installed to. Defaults to a standardized location depending on the operating system the program is running on.

Since "package.lima" is a full-fledge lima program, you can programatically define any of the above fields.

dif originalObject newObject A function that compares two objects and returns the difference between them. The return value is an object that can contain any of the following properties:
set If the value was set to a value that has different privileged members, this property contains the new value.
elements Contains a list describing elements that were moved, inserted, or removed. The order of the list is such that if the actions described in each element are done in order on the original object, the list will look like the new object. Each element is an object with one of the following forms:
{type='move'  from=originalIndex  to=newIndex  val=value } This indicates that an element was moved from one starting index to another.
{type='add'  index=index  val=value } This indicates that an elements was inserted at the given index.
{type='remove'  index=index  val=value } This indicates that an element was removed at the given index.
change If set, that indicates that the object's non-privileged members have changes. Each property in 'change' is a key of the original object, and the value is an object with any of the properties 'set', 'elements', and 'change' just like its parent object.

var a = {x={r=3}} var b = {x={r=4}} dif[a b] ;; returns {change={x={set=4}}}

exit code A function that exits the program with the passed exit code. This behavior can be overridden using the exitFn attribute.
Function that writes some information (by default to stdout). wout[x] is short for infoHandler[x]. Can also print one argument without brackets, e.g. wout "hello"
:  conditions  : [ ]

if is lima's if construct. The conditions to an if statement are defined in a (explained in more detail in the section on functions) Any statement can be executed conditionally using if (including variable declarations). This is the only function-like construct allowed in a .

if x != 'something': doSomethingWith[x] if [isRed: wout['Yes its red'] isGreen: wout['Its green instead!'] isBlue: wout["We've got a blue"] else: wout['Its some other hue'] ]

if returns the value of the last statment executed, and so can be used like the tertiary operator ?: in C (and other languages):

var x = if a>b: 'A is bigger' else: 'B is bigger' ;; x gets a string that says which one is bigger var y = 500 + if[one:1 two:2 three:3 else:0] * 20 ;; y gets a number

Note that, like javascript, if does not define a new scope. So variables can, for example, be conditionally defined.

testFunction conditionParameters: conditionParameters : [ ]

is useful for making switch-statement-like constructs and more powerful templatized conditionals.

Each set of conditionParameters are passed to the 's testFunction. If the testFunction returns true, the are executed and the is complete. If not, the next set of conditionParameters are tested. If there is an , the under it are executed if none of the conditionParameters yield a true result from the testFunction.

cond fn[x: ret 50<x.height[]] personA: wout["The first"] personB: wout["The second"] else: wout["Nobody"] ;; you can use else to attach fallback testFunctions cond fn[x: ret 50<x] 34: doSomething[] 12: doSomethingElse[] else: cond fn[x: ret x%10 > 5] 4: doSomething[] else: doSomethingElse[]

See also: "Emulating switch statement fallthrough in Lima" under Concepts.

object value key count : [ ]

Lima's main loop construct (stands for "do for"). Iterates through each item object returned by object's IterList method. value, key, and count will be automatically declared as var variables. On each iteration, value contains the value of the next member in object, key contains the key for that value, and count contains a count of the members that have already been looped over.

Note that when using the default object iterator, the loop gets a reference-copy of the object (a list of references to the object's members - like a list-slice) that has references to the original object's members. This means that removing, adding, or rearranging any elements in the original object won't affect the progression or number of iterations of the loop.

count is optional, and key is optional if there is no count. df called with no parameters (object, value, key, or count) is an infinite loop.

creates a new scope. This means variables declared inside it do not persist outside it.

Custom function that jumps straight out of the loop, skipping any lines of code in the current loop's block. The custom function can be passed around to allow code in nested loops or called functions to break out of the loop as well.

df: ;; infinite loop var outerBreak = break~ var n=0 while true n+=1 : if n > 5: outerBreak ;; when n is 6 it will jump to after the infinite loop

break has one member:

contin The continuation break will break to - ie the line right after the loop.

Skips the rest of the loop and continues with the next iteration of the loop.

continue has one member:

contin The continuation continue will continue at - ie getting the next value, key, and count of the object before executing the statements again from the top.

: [ ]

A more general loop construct. First checks the . If it is true then runs the and , then starts over.

The can be omitted. If both and are omitted, the loop is infinite (it will continue to loop until some code s out of it).

and are available in this form of as well. exits the loop without executing . runs then checks the condition before starting the next iteration.

creates a new scope.

throw object

Throws an exception object. Throwing an exception will interrupt the execution of any following code - execution will continue from where the exception is caught. If the exception is not caught anywhere up the stack, the unhandled-exception handler is called. If there is no unhandled-exception handler, or if an exception occurs in the unhandled-exception handler, the error is considered fatal, and the program prints the exception adn immediately exits.

Upon being thrown,

  • A stack trace will be added to the object as the member trace (overwriting it if it exists)
  • The str member of the object will be set to[@''+obj.trace]
  • The member causes of an exception is overwritten as an empty object on being thrown. causes is used to store a list of related exceptions.

Example of an exception:

Error in the hood:
main /moose/whatever.lima:44:10
obj.y /moose/candy.lima:44:10
func /moose/modules/x.lima:44:10
doSomething /moose/whatever.lima:44:10
a.b.c /moose/whatever.lima:44:10
Caused by:
0. Uncalled For Exception
   main /moose/whatever.lima:44:10
   obj.y /moose/candy.lima:44:10
   func /moose/modules/x.lima:44:10
   doSomething /moose/whatever.lima:44:10
   a.b.c /moose/whatever.lima:44:10
1. Shouting Failure
   main /moose/whatever.lima:44:10
   obj.y /moose/candy.lima:44:10
   func /moose/modules/x.lima:44:10
   doSomething /moose/whatever.lima:44:10
   a.b.c /moose/whatever.lima:44:10   
   Caused by:
   0. Bug In Your Junk
      main /moose/whatever.lima:44:10
      obj.y /moose/candy.lima:44:10
      func /moose/modules/x.lima:44:10
      doSomething /moose/whatever.lima:44:10
      a.b.c /moose/whatever.lima:44:10

: primaryStatements ;; exception: handlingStatements ;; finally: finallyStatements [ ]

Lima's try-catch mechanism. Catches and handles thrown exceptions.

Runs primaryStatements, and if any exception is thrown in those statements, catches and allows handling of the exception in the try-exception block. The handlingStatements in that block are then run (the exception is referenced by the name exception). The always runs after the primaryStatements and handlingStatements are complete, even if the primaryStatements or handlingStatements throw an exception, return from the function, or jump to a continuation. In those cases, the value to return, throw, or jump to is computed before the code enters the finally block, and the finally block finishes before the value to return, throw, or jump to is actually returned, thrown or jumped to.

The and are both optional, but either one or the other must exist. Note that, like if, the try block doesn't define a new scope, so variables declared inside it persist beyond the try block.

When an exception is thrown from the try-exception block that is different from the one that triggered the try-exception block, the exception that triggered the try-exception block is prepended to the causes list of the newly thrown exception. Multiple causes can be attached to an exception when a try block nested inside a try-exception block throws an exception, in which case the outermost cause appears first in the list.

atry: primaryStatements ;; exception: handlingStatements atry [ ]

Asynchrounous try-catch. Catches and handles exceptions thrown from threads (does not catch synchronous exceptions). This is similar to domains in node.js.

Runs primaryStatements, and if any exception is thrown from threads in those statements, it runs the handlingStatements (the exception is referenced by the given name exception).

This construct uses the uncaughtExceptionHandler attribute, and so has the same familiar nesting behavior as normal try. Like try, atry doesn't define a new scope, so variables declared inside it persist beyond the atry block.

When an exception is thrown from an atry block, the exception that triggered the try block is appended to the causes list of the newly thrown exception.

: [ ]

A thread. Runs the asynchronously. This is a similar concept to threads in a language like C or Java, or setTimeout in a language like Javascript. Although a thread can't take parameters, it acts similar to a function in that it can return a return-value (accessible via the wait).

threads are primarily used to indicate outputs that can be reordered. Normally, variable writes (including outputs) are treated as order-matters by default, meaning that no reordering of variable writes can be done to optimize the program unless some of those writes are in a different thread. If order doesn't matter, threads should be used to indicate that.

Most of the time, threads should be very short, and executes some code that outputs something.

wout['hi'@] ;; always written first var data = getData[] thread: wout[data] ;; might be written second or third wout['hello'@] ;; also might be written second or third

creates a new scope.

A thread returns a future object (with properties wait and val) that is resolved when the thread completes. The object has an additional pseudo-method:


Makes the thread weak, meaning it isn't counted in the number of running threads. boolean defaults to false, but if true sets a thread to be non-weak (adding it back to the thread count if it isn't counted already).

The returned future also has a destructor that, when run, instructs any exceptions thrown from the thread to be passed to the uncaughtExceptionHandler only if val or wait have not been called. This means that the uncaughtExceptionHandler will only be called for exceptions that have no chance of being thrown elsewhere.

The statements that run in a thread are all run atomically, meaning that conceptually, once the thread starts running, nothing else is run concurrently. This allows programs to have much more deterministic robust behavior, avoiding an infinity of race conditions that can occur in true concurrent code. As long as two threads don't have conflicting code (eg writes to share variables), the compiler may optimize the code to run truely concurrently. Note that the main thread is *not* run atomically, and so if a thread may modify a variable while it is in an inconsistent state in the main thread, an atomic block should be used in the main thread.

thread.count The number of non-weak threads that are currently running. This number goes to 0 when the main thread is complete and the program should exit soon. This can be used to do things like exit a thread early (like setTimeout.unref in node.js).

rawthread: rawthread [ ]

Just like a thread, but its statements are not executed atomically. This allows for greater flexibility in defining what behavior can be concurrent, at the cost of introducing the complexity of truely asynchronous behavior. This should only be used when you really know what you're doing.

atomic: function functions atomic[ function functions ]

Takes any number of functions. Calls each function in an atomic way - all activity that happens within each function will not be visible to other code until that function is complete. The calls are also mutually-atomic - each function will be run on the program state as it was before the atomic block. Any thread created inside the atomic block will begin once the atomic block ends. If there is a conflicting change, as soon as the conflict is detected, the function that caused the conflict throws an exception from that point.

If an exception happens in one of the functions and is not handled in the atomic block, an exception will be thrown out of the atomic block that has an errors property, containing the list of exceptions that happened in that atomic block. Any state changed within it will be kept (ie not rolled back).

The following pseudo-member is available inside an atomic block:
atomic.rollback Rolls back any changes made in the function it is called in.
atomic.rollbackAll Rolls back any changes made in all functions in the atomic block.

Note that in lima, happens-before relationships are guaranteed for all variables (like volatile variables in Java [but not C]), tho that volatility may be optimized away by the compiler if possible.

var x = 50 thread: var y = x wout['y: 'y] atomic: fn: x -= 1 wout['x-1: 'x] x += 1

In the above code, the atomic block prevents y from ever getting the value 49. Of course, this is a contrived example since you could easily just print out x-1 instead of decrementing x. There can be more complicated cases where you need a function that mutates shared state, but you don't actually want that state to be visible until you fix it somehow. You should always avoid mutating shared state into an inconsistent state, and atomic blocks are one method of doing that.

var x=7 atomic: fn: x += 1 fn: x += 2

The above code throws an exception.

var x = 10 var y = 20 atomic: fn: x = 15 fn: y = 25 atomic.rollback

In the above example, the value of x is changed to 15, but the value of y remains 20.

change[testStatement:oldValue changeStatments]: statements

On-change event statement. The statements are run when one of the given testStatements changes value. For each testStatement, oldValue (and the preceding colon) is optional, but if declared, the name given for oldValue can be used to access the previous value of the testStatement.

Returns an object with the following pseudo-methods

  • stop - stops the listener (the event-handler won't be run for subsequent changes to the testStatement).

The event-handler is triggered synchronously on change of whatever variable caused the testStatement to change. After any triggered event-handlers run, the code that triggered the change is continued from. Any event-handler triggered simultaneously in a change event are executed in parallel atomic blocks.

var x = 5 change[x]: wout['x changed to 'x] x = 6 ;; the event handler prints to the console before the following statement wout['hi'] // keep track of the change in value var delta = 0 change[x<5:lastValue]: delta = lastValue - x // maintain a = 2*b+c^2 var a = 2*b+c^2 change[c b]: a = 2*b+c^2

Note that you have full control over the number of times the statements are executed because one event is handled fully before the next event can be handled. Even though an event is fully handled before the next one is handled, all events are guaranteed to be caught, even if one happens while another one is being handled.

future When run on a statement (or set of statements), returns a future object. Exceptions thrown from the statement will be thrown from accesses to the future's properties rather than from the statement in-line. The returned future object has two properties:
  • val - The value returned by the statement. Throws an exception if the statement throws one. Note that this doesn't (by itself) ensure that the statement has completed after that line. Like most lima code, it really returns a lazily calculated value - meaning that code will only block when it needs a value inside val to continue.
  • wait: statements - A custom function that runs the statements only after all inputs and outputs inside the original statement are complete. Note that the statements inside the wait are *not* asynchronous, statements after the wait that rely on values determined inside the wait still implicitly wait on those values (just like normal lima code). Throws an exception if the statement throws one.
  • done - A pseudo-member that resolves the future (indicate that wait statements can now be run). This can only be run for futures created via the copy operator of future.
;; pretend this is creating a database record and returning its new id ;; and also pretend the id is created on the client (like it usually is with mongo db), ;; so the id is ready before the document is created var x = future: createNewDocument[] ;; store the id somewhere ;; lima is not obligated to wait until the whole file has loaded ;; so this line can run before the document is finished being created outputId[x.val] ;; this might not find the document, since lima isn't waiting for the creation of the document to be complete var document = loadDocument[x.val] ;; here, we wait until the document is done being created before trying to load it x.wait: var document = loadDocument[x.val]
This is used to precisely control the order statements are executed in since, in Lima, out-of-order is the norm.

A future can also be explicitly created and resolved. The copy operator of future returns a new unresolved future.

var f = future thread: var x = 0 while x<100: x+=1 if x == 50: f.val = x f.done ;; resolves f (indicating that things waiting on it can now run) f.wait: wout["its finally done!"]
If the future variable goes out of scope while containing an exception that hasn't been thrown, it will throw the exception (so that the exception isn't lost).

This is like goto in C. It isn't meant to be practically used in everyday code, but exists for the flexibility to write assembly-like code. Jump really does a computed jump (ie a "computed goto").

;; the following loops 5 times using jump int x = 5 var a = contin x -= 1 if x == 0: jump b else: jump a var b = contin
Note that b can be used (ie jumped to) before that line is hit, because the variable is const.

can also be used like longjmp in C's setjmp.h. If a is set and then used in a directly or indirectly calling function, it will restore context as well as jumping.

func = fn [contin back: jump back ;; will jump to the command "wout 'finally back'" wout 'this never prints' ] main = fn [ func[Here] var Here = contin wout 'finally back' ]

Note that once the continuation goes out of scope (when the function its in returns), it is effectively deleted and attempts to jump to it will cause nil pointer exceptions.

Operates on a variable declaration. It makes the declared variables static - which means that any object that inherits from that object, and any object created as a copy of that object will have a reference those same static variables. That also means that the static variables are still "alive" as long as any object references them.

When a static variable is declared with an initializer, the variable will only be initialized once (when the object/function is created), and never again.

The attributes and have no effect if used inside functions at the top level (tho of course they have the expected effect when used inside objects that are created in the function). This differs from many languages that allow static function variables. In Lima, you just need to use a variable in a higher scope.

This is like except that subobjects that or superobjects with a member get a copy of that member, rather than referencing it.

x = { fixed a = 4 static b = 4 } x2 = { use x } var y = x var y2 = x2 y.a = 1 y.b = 2 y2.a == 4 ;; true y2.b == 2 ;; true

Makes a variable unchangable. If the object has any ref variables, the objects they reference can be changed as normal, but they can't be pointed to different objects.

Note that variables are hoisted - they can be accessed before they are defined as soon as the value the variable is set to resolves to the value it will be when it gets to that line. Functions that use non-constant upvalues cause an exception to be thrown if they are called before all their upvalues are declared.

main = fn: wout[f[]] ;; prints 19 try: wout[x] e: ;; an expcetion is thrown because x can't yet be resolved someHandling[e] y = 5 wout[x] ;; prints 5 const var x = y const fn! f = fn: ret 42
Makes a variable not accessible outside its .

Private members of compiled files are not externally available (when statically or dynamically linking to it), while public members are.

Used on functions, causes the closure (the function) to immediately bind its upvalues (the variables outside that function's immediate inner scope), so that changes to the upvalues aren't visible to the function itself. This does something similar to what a do-statement does in coffeescript. Note that it is possible to finalize and variables accessed via .
;; how a normal closure operates var a = 'hi' var f = fn: wout[a] a = 'no' f[] ;; prints 'no' ;; how final changes things: var b = 'hi' var f2 = final fn: wout[b] b = 'no' f[] ;; prints 'hi' ;; that last final function is equaivalent to this: var b = 'hi' const var constB = b ;; copy b to another variable var f2 = final fn: wout[constB] ;; use that copy instead b = 'no' ;; changing the original obviously doesn't affect the copy f[] ;; prints 'hi'
Bracket Operator:
variableName variableNames Returns an attribute like basic that binds only the variables whos names are given in the parameter list.
var[a=5 b=6] var f = final[a] fn: wout[a' and 'b] a = 10 b = 11 f[] ;; prints '5 and 11' because a is bound early, but b is left as a free variable
Note that since the bracket operator is defined for , brackets cannot be used to set this attribute over multiple statements (and so must be written individually).
operator[ operatorSymbol ] Returns a function that will operate on the input parameter(s) exactly like the operators of those objects. For example, operator[+][x y] is exactly the same as x+y.
assert[ statement probability=1 ]

Asserts that the statement is true at a given point in the code with a probability of probability*100 percent (probability must be a number in 0 through 1). It is used to give the compiler the information neccessary for certiain types of optimization. asert is an alias for assert.

Note that assert does not exit the program (like it does in C) or throw an exception. Again, assertions are only used to give optimization information to the compiler. If the assertion fails at run-time, the code continues as normal. This may cause undefined behavior, and so if you want to take some action on incorrect input, also check it in normal code and throw an exception (or take some other action) in that case.

;; you might want to assert that the input to your program will be a list of integers from 1 through 10 main = fn: assert[[''@].join[1<=v<=10]] ;; or you might want to assert something about a function's arguments ;; this kind of thing will be used by the compiler to deduce contraints on the external input to your program somefn = fn x y: assert[x<y]
optimize[ metricStatement ]: statements optimize[cputime memory power: metricStatement ]: statements Creates an optimizer custom-function, which will optimize the code its run on such that the value of the passed metricStatement is minimized. The variables cputime, memory, and power are implicitly defined, but may be renamed by passing names in before a colon (eg optimize[c m: c+4*m+10*power]). If other variables are used in the optimizer, the value they are taken as is the value they will hold at the *end* of the statements the optimizer is run on. Any optimizer found inside the statements is ignored (so only the top-most optimizer is used).
var timeWeight = fn msElapsed: ;; function for determining the weight of running time if time < 300: 2*msElapsed ;; short amount of time is ok else: 600+(msElapsed-600)^1.2 ;; long amount of time isn't - exponentially bad ;; cputime is 10 times as important to optimize as memory ;; and optimize the time it takes to complete it optimize[10*cputime + memory + timeWeight[msElapsed]]: var start = time.before: var end = time.after: doStuff[] var msElapsed = start - end
Denotes that external inputs to the program within a statement are ready immediately (ie at compile time), and so can be grabbed as soon as the program knows how to grab them. Usually this means that if the program knows how to grab the input at compile-time, it'll do it at compile time, but if not, it obviously can't grab it until runtime, and the doesn't mean anything (ie input is grabbed when convenient - the default). However, if the program can determine that some kind of input caching can be done (for example, if the inputs are fairly regular, tho not 100% deterministic), it may grab the input during runtime once, and then have it stored for later executions, essentially making it available at compile-time for those subsequent executions.
uncaughtExceptionHandler[exception] A function that is run in cases of uncaught exceptions. This attribute is used by atry.
infoHandler[obj] A function that is run in cases where a function or object wants to pass information to a higher scope. The default infoHandler prints the passed object to stdout.
collate A parameterized attribute that sets the collation (the order of characters).
Bracket Operator:
characterList Returns an attribute that uses the characterList as the collation.
collate[{'a' 'b' 'c' 'd'}] var w = 'a'..'c' ;; w gets {'a' 'b' 'c'} collate[{'t' 'r' 'd' 'x'}][ var x = 'r'..'x' ;; y gets {'r' 'd' 'x'} var y = "tea" < "dog" ;; z is set to true (since in this collation, 't' comes before 'd') var z = "trd" < "tea" ;; an exception is thrown since the collation doesn't define a relationship between 'r' and 'e' ]
Note that there is no default collation. If a comparison or range depends on a character that isn't included in the active collation, or there is no active collation, an exception will be thrown.
casemap A parameterized attribute that sets the upper-case/lower-case mapping for characters.
Bracket Operator:
characterMap Returns an attribute that uses the characterMap to map between lower and upper case. The keys represent the lower case letter, and the values represent the upper case letter. Any letter that is neither upper case nor lower case should be considered both (and thus be its own key and value).
casemap[{'a':'A' 'b':'B'}] var x = 'a'.upper ;; x gets 'A'
Note that there is no default casemap. If the upper or lower string methods are used for characters that aren't defined in the active casemap, or if there is no active casemap, an exception will be thrown.
encodingFail An attribute that contains a function that is called when a character encoding can't encode a character in a lima-string.
encodingFail! encoding character encodingFail! takes the encoding object and the character that couldn't be encoded as parameters, and the character it returns should be encoded in its place. The default of this attribute is a function that throws an encoding exception. Changing the default of this attribute is used to do things like insert default characters into an encoding, as a graceful failure mode - for example, displaying a default character (like '?' or '◻' in place of unencodable characters.
exitFn[code] This attribute contains the function to be run when exit is called, and can be used to override it.
fileObject This attribute contains the object accessed via file, and can be used to override it.
dirObject This attribute contains the object accessed via dir, and can be used to override it.
systemObject This attribute contains the object accessed via system, and can be used to override it.
dnsObject This attribute contains the object accessed via dns, and can be used to override it.
socketObject This attribute contains the object accessed via socket, and can be used to override it.
ipv4Object This attribute contains the object accessed via ipv4, and can be used to override it.
ipv6Object This attribute contains the object accessed via ipv6, and can be used to override it.
udpObject This attribute contains the object accessed via udp, and can be used to override it.
tcpObject This attribute contains the object accessed via tcp, and can be used to override it.
httpObject This attribute contains the object accessed via http, and can be used to override it.
httpsObject This attribute contains the object accessed via https, and can be used to override it.
inputsObject This attribute contains the object accessed via inputs, and can be used to override it.
bits An "encoding" that represents raw bits - ie no encoding.

LimaEncoding is a physical (bits) representation of what a string is in Lima. LimaEncoding can represent any string from any encoding, encapsulate the encoding within it if necessary.

LimaEncoding is based on Unicode. It represents all Unicode characters, with the exception of all the control characters other than the newline - the excluded codepoints are U+0000 - U+0009, U+000B - U+001F, and U+007F - U+009F. LimaEncoding always uses big-endian byte ordering, and thus does not have a byte-order mark. The Unicode characters LimaEncoding supports without using the alternate character markers (described below) are called LimaEncodings core characters. LimaEncoding-8 stores unicode characters in UTF-8 format.

It also adds the following codepoints:

Codepoint Name Description
U+000B Header Marker

Marks the beginning and ending of the LimaEncoding header, which contains the list of alternate encodings contained in the string. If this marker isn't the first codepoint of the string, the string has no alternate characters. The header is represented as:

{U+000B, <encodings>, U+000B}
  • <encodings> is an ordered list of encodings where each encoding is represnted in the following way:
    {<length>, <encodingName>}
    • <length> is a 16-bit length of the number of bytes in <encoding> minus 1.
    • <encodingName> is the string representation (ie the standard name) of the encoding represented only by core-characters of LimaEncoding-8 (ie. the subset of UTF-8 without control characters).

U+000C Space-extension codepoint

Not a character by itself, this codepoint is paired with a second byte indicating the width of the space minus 1. For example, a 4-space width space character would be written as {U+000C, 3}.

This is intended to replace the tab character. With extended spaces, you can specify how much space is being created (like a space) while also identifying as a single character (like a tab) so that programs can handle selections and backspaces like tabs.

The extended space character has one additional member:

width The width of the extended space. E.g. ""%4.width is 4 spaces long.
name name holds "extendedSpace".

U+000D Alternate character marker 1

Also not a character by itself, this codepoint indicates the beginning of a character that doesn't have a unicode representation (or is one of the excluded unicode control characters). LimaEncoding stores the name of the encoding that character is from with an index in a header section of the string format.

A non-unicode character would be written as:

{U+000D, <encodingByte>, <characterLength>, <characterBytes>}
  • <encodingByte> is an 8-bit index of the encoding (in the encoding-list in the header)
  • <characterLength> is an 8-bit length of the number of bytes in <characterBytes> minus 1 (so a binary value of 3 indicates that <characterBytes> is 4 bytes long).
  • <characterBytes> is the bytes of the canonical representation of the character in the encoding its from.

This allows any character from any encoding to be stored in a string encoded into LimaEncoding. Note that when the excluded Unicode control characters are transcoded to LimaEncoding, they are translated using the alternate character marker in the same way as characters from other encodings.

U+000E Alternate character marker 2

The alternate character marker allows for characters with representations longer than 256 bytes long. This is the same as U+000D except the character is represented as:

{U+000E, <encodingByte>, <lengthLength>, <characterLength>, <characterBytes>}
  • <lengthLength> is an 8-bit length of the number of bytes in <characterLength> minus 1.

Since most characters have a unicode representation, special characters will be very infrequently used. So strings encoded in LimaEncoding-8 will usually be identical to its UTF-8 counterpart.

LimaEncoding-16 Exactly like LimaEncoding-8 but the representation of the core-characters is UTF-16.

Parameterized Encodings

UTF-32 tabWidth endianness

UTF-32 with the given endianness. endianness can either be 'big' or 'little', defaulting to 'big' if not passed. If tabWidth is passed, tabs will be converted to extended-spaces with the width tabWidth. If tabWidth is not passed, tabs will not be decodable.

A note about unicode: Lima leaves any definitions for word/line/sentence separators or mid-word hyphening to outside libraries.

UTF-16 spacesInATab endianness UTF-16. The parameters mean the same thing as for UTF-32.
dosText encodingName This encoding handles windows-specific file encoding. When encoding, this first encodes with the encoding named encodingName, then converts regular newlines to dos newlines and adds the end-of-file characters (ascii value 26) at the end of the file. When decoding, this first converts dos newlines to regular newlines and ends the file at the first end-of-file character, then decodes using the encoding named encodingName. This replaces opening files in "text mode" in a language like C.
url encodingName When encoding, this encoding first url-encodes a string, then encodes with the encoding named encodingName. When decoding, this first decodes using the encoding named encodingName, then url-decodes the string.
  1. In some languages, you have special syntax for accessing items from the end of an array like this: someList[-4]. This would access the 4th to last element of the array. Lima doesn't have that syntax (since any value is already a valid key to use in an object), but you can do the same thing in one of two ways:
    {1 2 3 4 5}.sort[-v][3] ;; sort the list in revrse, then select the the 4th element (the 4th to last element of the original list - 2) x = {1 2 3 4 5} ;; .. x[x.len-4] ;; another way to access the 4th to last element
    Because lima should optimize this, using sort should be just as good as using the length.
  2. fn functionName int[a b]: ret a+b int a: ret fn.this[a 1] ;;[ here, if the function is called with only one argument, then b defaults to 1 ;;]
  3. var x = 5 ;; x1 var y = { ;; y1 x= 10 ;; x2 y= fn a: ;; y2 (naming the parameter x instead of a is illegal here) var x1~>self.x~ var y = { ;; y3 x2~>self.x~ x=10 ;; x3 y=fn b: ;; y4 (naming the parameter x instead of b is illegal here) self.x ;; references x3 x1 ;; references x1 ;; trying to access 'x' here is illegal } }
  5. ;; right stripping spaces and extended spaces var rstrip = fn aString: var str = aString df str v k: if v == ' ' $ == 'extendedSpace': str.=rm[k] else: break ret str ;; left strip var rstrip = fn aString: ret rstrip[aString.sort[-k]] ;; right strip the reversed string This pattern can be used to strip any type of characters.
  6. To emulate a do-while, you can have an infinite loop that has a break condition at the end of it:
    while 1: ;; the 'do' of C's do-while wout "I'm finally in the loop!"@ ;; .... if someCondition: break ;; the 'while' of the do while
  7. var x = {1 2 3} x.sort[-k] ;; sort by the negative of the index (key) ;; returns {3 2 1} var y = {1 5 2 5 93 0 -4} y.sort[-v] ;; sorts on the negative of the value ;; returns {93 5 5 2 1 0 -4} y.sort[[ !(v<v2) ]] ;; the complement of any expression in sort[[ ]] is a reverse sort ;; also returns {93 5 5 2 1 0 -4}
  8. Because lima will optimize away most unneccessary processing, finding a minimum can use the first value returned from a sort. It might seem nice to have a minimum function, but what happens when you need to find the 4th lowest value in a list? Or if you need all 4 lowest values? Using sort gives a good generalized way of finding this type of thing:
    var x = {5 2 87 9 6 8 4} x.sort[v][0] ;; sort by value and get the first element - the minimum x.sort[v][3] ;; find the fourth lowest value x.sort[v][0..3]] ;; get the 4 lowest values x.sort[-v][0] ;; find the maximum
  9. If you select a slice of a list or something, the keys are preserved. In such cases, you might want to rekey the array, so that all its members are elements. "hello"[[k>2]] ;; returns the object {3:'l' 4:'o'} "hello"[[k>2]].sort[k] ;; returns the object {0:'l' 2:'o'}, or equivalently "lo"
  10. You can emulate classical string join (implode in php) using the psuedo-method ins and the join method: var L2 = {1 2 3 4 5}.ins[[', ']] ;; this returns the list {1 ', ' 2 ', ' 3 ', ' 4 ', ' 5} wout L2.join[[b]] ;; this will print the list with commas
  11. The trick is to make a reference to the object itself x = { mythis ~> this ;; mythis points to this myself ~> self ;; myself points to self a=5 b=23 c=89 obj= { a = 'hi' b = a ;; b gets the value 'hi' func = fn [ var b=34 ;; errors, becuase b is already declared in the current object var c="nope";; errors, because c is already declared in a containing object wout a ;; errors, because a is an ambiguous name wout self.a ;; prints "hi" wout self.b ;; prints "hi" again wout mythis.a ;; prints 5 wout c ;; prints 89 wout mythis.c ;; also prints 89 wout myself.c ;; prints 89 again ] } }
  12. In Lima, internationalization is done using the concept of translators. A translator is a function that takes one input - a value, to translate - and returns a value in another language. The value would be in some standard format, say a string in english or an integer. The translator function itself could be as simple as a map that can only translate specific phrases (similar to localized property files in Java), or can be as complex as a full langauge translator able to translate arbitrary structures.

    dateTranslator.from['en'].to['en'].translate['monday'] dateTranslator.from['en'].to['en'].translate['am'] var spanishTranslator = translatorFactory.from['en'].to['es'] spanishTranslator.translate['one'] ;; would return 'uno' var romanjiTranslator = translatorFactory.from['en'].to['ja' 'romanji'] romanjiTranslator.translate['one'] ;; would return 'ichi' var smartTranslator = englishTranslatorFactory.context['businessMumboJumbo'] smartTranslator.translate["Let's discuss that later"] ;; might return "Let's table that for now" or "Lets put a pin in that" smartTranslator.translate["What is your request?"] ;; might return "Whats the ask?"

    They don't have to neccessarily be just for words, you could use them for time formats or other locale-specific formats for things.

    translator.translate[5] ;; could return "five" translator.translate[{animal="dog" feet=4}] ;; could return "perro tiene cuatro patas" currencyTranslator.lang['en' 'US'].translate[400345000.045] ;; would return "400,345,000.45" currencyTranslator.lang['en' 'CA'].translate[400345000.045] ;; would return "400,345,000.45" currencyTranslator.lang['no'].translate[400345000.045] ;; would return "4.294.967.295,000" moneyTranslator.lang['en'].currency['eur'].translate[500.34] ;; would return "�500.34" moneyTranslator.lang['lt'].currency['eur'].translate[500.34] ;; would return "500,34 �"

  13. x = { a = 5 t = fn [ ret 'x' ] } y = { use x![t] ;; using all x's members except t t = fn ;; override the old t [ ret privateOldt[].cat['y'] ] oldt = fn [ ret privateOldt[] ;;[ of course we could have just inherited x's t member as a public member and wouldn't have had to write this method ;;] ] private: use x[t : privateOldt] ;; using x's t member aliased as private member oldt } y.t ;; returns 'xy' y.oldt[] ;; returns 'x'
  14. Lima doesn't quite have the same kind of generics that a language like Java has, because values in Lima don't have a "type" (instead a given type-set can either include a value or not). However, you can still do most of the things you would want generics for using variables containing types: var Statistics = fn type T: ret { list[T] percentiles ;; 0, 20, 40, 60, 80, 100 T mean T median } var x = Statistics[int] ;; returns a statistics object where the type T is 'int'
  15. Generators and custom iterators aren't neccessary in lima, because optimizations like lazy list building (infinite or finite lists) and futures/promises, take the burden off the programmer, and puts it on the compiler/interpreter. Things done with coroutines should be done using normal list semantics (which makes code more consistent and allows the list results from these to be operated on by any function that operates on a list). Generators, custom iterators, and corountines can all be implemented either using function immediates, list semantics, or threads. For example, a custom iterator:

    var threeTimes = fn f: df 13: f[] threeTimes[fn: wout 'cry'@ ]
    or a generator:
    createInfiniteList = fn: var theList = {} var next = 1; while 1: infiniteList.=ins[next] next++ ret theList infiniteList = infiniteList[] wout[infiniteList[3]] ;; prints 4

    Lima doesn't need semantics like yield to impliment coroutines. In ECMAScript 4:

    function fringe (tree) { if (tree is like {left:*, right:*}) { for (let fringe(tree.left)) yield leaf for (let fringe(tree.right)) yield leaf } else { yield tree } } /­/;; tree var tree = { left: { left: 37, right: 42 }, right: "foo" } for ( let fringe(tree) ) { /­/;; Iterate over the tree trace(x); /­/;; 37, 42, "foo" }

    The same can be done much more simply in lima like this:

    fringe = fn tree: ;; returns a (possibly lazily constructed) list if like[tree {var[left right]}]: ret fringe[tree.left].cat[fringe[tree.right]] else: ret {tree} var tree = {left = {left=37 right=42} right='foo'} df fringe[tree] : x : wout x
  16. chooseset[int string fn!] ;; maybe this is wrong?
  17. Tho there would probably be no reason to ever create a linked list in Lima, it's informative to show how one of the most common interview questions is done: // head node of a linked list node = { ref? next } reverse = fn node! head: ref? previous ~> nil while head !~> nil: { previous head} ~> {previous head} head = previous main: var head = {"A" next~>{"B" next~>{"C" next~>{"D"}}}} reverse[head]
  18. aUnion = { chooseset[fra int string] value ;; using a set of types as a tuple type acc f ;; fra accessor [ ret ref[value] ] acc i [ ret self.f ;; int accessor ] acc s [ ret self.f ;; string accessor ] }
  19. You can use arb to implement don't cares in an if statment: if: a==5 $ arb[{true false}] ;; do something This means that it will always ;; do something if a equals 5, but if a does not equal 5, then the compiler can choose whether or not to execute that code (if it somehow simplifies the logic of the program)
  20. In SQL:
    select productid, sum(unitprice * quantity) as total from o in db.orders inner join rd in db.orderdetails on o.orderid == rd.orderid where o.zipcode == 94503 group by rd.productid

    and in Lima:[ {mix[ v db.orderdetails[[o: o.orderid == v.orderid]][0] ]} ] ;; join [[ v.zipcode == 94503 ]] ;; where .map[ {productId=v.productId price=v.unitprice*v.quantity][0].rd ;; .group[v.productId].map[{ v[0].productId v.join[a.price+b.price] }]
  21. In XQuery:
    for $b in $bs/book return <result> {$b/title} {$b/author} </result>
    And in Lima:[[ v[{'title' 'author'}.has[k]] ]] ;; one way[[ {title=v.title} ]] ;; another way
  22. ref.fn theFunc anonFuncCreator = fn list[string string] characters: ;; takes in a mapping from character to character theFunc ~> fn string a: ;; create anonymous function static list[string string] map = characters if {a} < map.keys: ;; if map has the key a ret map[a] else: ret a ;; returns input character if there is no mapping
  23. In hardware design, a wire is what connects an input of a device to the output of another device. In Lima, one can describe the same functionality as a wire using functions: main = fn: int[ a=2 b=5 c=13] fn! aWire = fn[ ret c+b*a ] ;; acts like a wire wout aWire[] ;; will print '23' (13+5*2) a+=4 wout aWire[] ;; will print '43' (13+5*6)
  24. There are two ways to do event-driven-like programming:
    • list iteration and slicing
    • variable observation

    In traditional programming langauges, programming with events is done by setting up callback handlers that are called when an event happens, for example in javascript:

    emitter.on('someEvent', function(eventData) { doSomethingWith(eventData) }) ... emtter.emit('someEvent', {your:'data'})

    But in Lima, we can do this kind of thing with standard lists and the tslice function: var events = [] ;; this event isn't printed because it happens before tslice was called events.=cat['1'] var stop = future var relevantEvents = events.tslice[stop] events.=cat['2'] df relevantEvents event: ;; this loop prints '23' wout[event] events.=cat['3'] stop.done ;; this event isn't printed because it happens after the 'stop' future is resolved events.=cat['4']

    Another way to do event handling is by doing variable observation using the 'change' function:

    var events = [] ;; this event isn't printed because it happens before the change handler is set up events.=cat['1'] var c = change[events:oldEvents]: var d = dif[oldEvents events] df d.elements.add event: ;; assume all changes are appends wout[event] ;; this loop prints '2' events.=cat['2'] c.stop ;; this event isn't printed because it happens after the 'change' handler is stopped events.=cat['3']

  25. In Lima, circular dependencies can not only happen for module dependency loading, but can also happen because of dependencies between variables in the code. For example:
    var x = 5 var a = {1 2 3} var slice = v.tslice[process.end] df slice v: if v == 4: x = 10 if x > 6: a.=cat[4]
    The above code can never finish because the if statement doesn't know the value of x. The loop is also waiting to see if a will have a 4 appended to it, which it might if x becomes more than 6 because of the loop. This behavior should throw an exception if detected.
  26. change[a:oldValue]: if oldValue == 0 && a==1: ;; reacts to a positive edge: 0 to 1 ;; do something oldValue == 1 && a==0: ;; reacts to a negative edge: 1 to 0 ;; do something
  27. var o = {1 3 9 12 15} var o2 = {var[x=90]} o.keys >= {2} ;; true (has the value 9 at index 2) o.keys >= {12} ;; false o2.keys >= 'x' ;; true since o2 has a defined member x
  28. Testing set comparisons between two lists can be done in multiple ways. For consistency, only one of those ways is preferred and the compiler will correct code that uses the un-preferred comparisons.
    1. How to test if an item is contained in an object/list

      • Preferred: {theItem} < theList asks "Is the object consisting only of theItem a subset of theList?"
        var o = {1 3 9 12 15} var o2 = {'H'} {3} <= o ;; true {2} <= o ;; false {"H"} <= o2 ;; true {"H"} < o2 ;; false because {"H"} is not a proper subset of itself
      • theList[[{v}==theItem]].join[a$b] asks "Does any member in theList equal theItem".
    2. How to test if a list is an improper subset of another

      • Preferred: list1 <= list2 asks "Is list1 a subset of list2 or equals list2?".
      • list1[[ list2[[v2:v == v2]].join[a$b] ]].join[a&b] asks "Does every member in list1 equal some item in list2?"
    3. How to test if a list is a proper subset of another

      • Preferred: list1 < list2 asks "Is list1 a proper subset of list2".
      • list1[[ list2[[v2: v==v2]].join[a$b] ]].join[a&b] & list2[[list1[[v1: v!=v1]].join[a$b] ]].join[a&b] == no asks "Does every member in list1 equal some item in list2? And is there some item in list2 that doesn't equal any item in list1?"
      • list1[[ list2[[v2: v==v2]].join[a$b] ]].join[a&b] & list1!<>list2 asks "Does every member in list1 equal some item in list2? And does list1 not contain all the members that list2 contains?"
    • Preferred:

      var conditions = fn k: (200285).has[k] var charactersToCut = aList[[conditions[k]]] aList.rm[[conditions[k]]]

      After this code is run, aList's elements with keys larger than 285 are shifted "left" so that an originally having the key 286 will now have the key 200, etc. This method works on members that are not elements, but no shifting happens.

    • var charactersToCut = aList[[(200285).has[k]]] aList = aList[ !({k} < charactersToCut.keys) ].sort[k] ;; get the list without the cut characters and rekey them (via sort)
    • var charactersToCut = aList[[(200285).has[k]]] df charactersToCut v k: aList[k] = nil ;; delete all the members that were cut
  29. Files are always opened as binary in Lima. Text-mode, in a language like C, encodes newlines in a platform specific on certain platforms. In reality, this is just another encoding, and Lima treats it that way. If you need to handle operating-system specific newlines, you should open a file with the appropriate decoder that handles those OS-specific newlines (e.g. dosText).
  30. The first means "Does every member in list equal some member of list2?", while the second means "Does any member in list2 equal every member of list2?". Example, {1 2 3}[[ {4 5 6}[[v2: v == v2]][$] ]][&] ;; is the same as: {4 5 6}[[v2: 1 == v2]][$] & {4 5 6}[[v2: 2 == v2]][$] & {4 5 6}[[v2: 3 == v2]][$] ;; which is the same as: (1==4 $ 1==5 $ 1==6) & (2==4 $ 2==5 $ 2==6) & (3==4 $ 3==5 $ 3==6) ;;while {4 5 6}[[ {1 2 3}[[v2: v == v2]][&] ]][$] ;; is the same as: {1 2 3}[[v2: 1 == v2]][&] $ {1 2 3}[[v2: 2 == v2]][&] $ {1 2 3}[[v2: 3 == v2]][&] ;; which is the same as: (4==1 & 4==2 & 4==3) $ (5==1 & 5==2 & 5==3) $ (6==1 & 6==2 & 6==3)
  31. list[[v==value]].len counts the number of members that equal value
  32. list[[v==value]].keys[0] finds the index of the first member of the list that equals value
  33. var x = 5.23456 var floor = x//1 ;; exactly like floor var ceiling = x//1+1 ;; exactly like ceiling
  34. The Haskal list comprehension syntax:
    s = [ 2*x | x <- [0100], x^2 > 3 ]
    and the equivalent python list comprehension syntax:
    s = [2*x for range(100) if x**2 > 3]
    can be done in Lima by doing:
    s = (0..100)[0 100][v^2 > 3][[v*2]]
  35. The filter function in python and other languages creates a new list of items from the items in a list that cause some function to return true. For example, "filter(list, GT4)" might return a list of elements that are greater than 4. In lima this can be done like this: list[v>4]
  36. To select a list of second-dimension items from a multi-dimensional list, you'd use the map method. E.g.:
    var rowFirstMatrix = { {1 2 3} {4 5 6} {7 8 9} } rowFirstMatrix[2] ;; selects the second row: {4 5 6}[v[2]] ;; selects the second column: {2 5 8}
  37. Lima doesn't have an enum construct, but you can still do enums using a pattern some lazy programers already use: returning strings. In most languages, the IDE and compiler don't have any insight into the values a function can return, but in Lima they do. Using simple strings as enums gives the benefits of not having to explicitly enumerate all possible values (duplicating the values you would already be using).
    var colors = fn int x: if x == ?0:: 1: ret 'blue' 2: ret 'green' 3: ret 'purple' var color = colors[1]; if color == 'green': wout['GREEN!'] color == 'purpel': wout['PURPLE!'] ;; IDE/compiler/interpreter should tell you this code path can never happen because the function will never return that misspelling 'purpel'
  38. Lima will attempt to find any variables that are un-initialized upon first read access. When a variable has a particular spot in your program that it is set, it is best to let it be set there, rather than setting it in both there and at the declaration. The reason for this is that you can find problems earlier if you don't build redundancies into your program. A redundancy can make your program work the first iteration of a process, but then it will fail the second time (or later) - which is a much more mysterious problem to debug.
  39. Lima doesn't have traditional inheritance. Lima instead takes the approach of languages like lua, lisp, and Ruby in providing "mixins" or "traits" rather than parent-child inheritance. Lima's form of inheritance is what's formally referred to as 'traits' (tho unlike tranditional traits, objects can inherit state) and are similar to mixins. Members from one object can be mixed by or used in another, but there is no sub-class/super-class relationship, and no "is-a".

    Any conflicting members must be explicitly disambiguated (by renaming one or both members, or excluding one or both members). A consequence of this is that there is no automatic solution to the diamond problem, and solving any diamond problem is left to the programmer (tho the compiler or interpreter will not fail silently but will force a solution to be made). Lima opts to not have inheritance for the same reason Go opts not to (, inheritance is too complicated and can cause insidious bugs. However, Lima's trait-like system gives the programmer the flexibility of multiple inheritance, with the safety of single.

  40. Lima doesn't offer an atexit function. This is because atexit functions are prone to cause scattered code. Instead, if you want to do something like this, create a function that does exit code. Whenever you need to For example:
    fn exitFunc [ 5+4 doSomeFunction[ ][ ] ;; etc exit ]
    Also, object desctructors could cover the use cases you would use atexit for. Or possibly you could use a try-finally block for this as well.
  41. Lima was built with the intention of being fully automatically opimized, and therefore doesn't have any specification of concepts like tail-calls that affect the speed of a program, but not the output. In short, any reasonable Lima implementation should implement proper tail calls, but it isn't part of the language.
  42. Lima doesn't do short circuit evaluation by default (tho it may optimize certain boolean operations such that they do short-circuit evaluation under the hood). So how do you do it when you want it?

    ;;In java, you might want to do things like this every once in a while: if( y != 0 && 5/y > 1 || x.equals("yes") && testFunctionWithSideEffects() || z != null && z.isTrue || w != null && w.method() ) { doSomething(); } ;; In lima: if if[y!=0: 5/y>1 else: false] ;; use if's return value $ if[x=='yes': testFunctionWithSideEffects[] else: false] $ z?.isTrue[] == true ;; use the safe-navigation operator $ z?.method?[] == true ;; have to check if its true because it could be nil

  43. String interpolation is really just another way to concatenate variables with strings On the surface, string interpolation seems like a nice feature that makes strings more concise. The downsides, however, far outweigh that small benefit. I'm lumping using special characters (like "/n" and "/t" etc) under the umbrella of string interpolation.
    • Increase cognitive load. Really interpolation is its own little mini language that does the exact same thing as the main language. Just using concatenation doesn't require extra knowlege. Writing cat["x is "x", f(x) is "f[x]] in perl is "x is $x, f(x) is @{[&f(x)]}" - yuck.

      And it is not only one-time knowlege, it requires constant vigilence. If you want to type literal strings, you need to make sure you escape anything used as special characters or interpreted in any way other than literally.

    • Its not that much shorter than lima:
      ;; ruby "the meaning of life is #{11+31}!" "#{a} and #{b} and #{c} and #{d}" ;; lima "the meaning of life is ",(11+31),"!" cat["the meaning of life is "11+31"!"] cat[a" and "b" and "c" and "d] ;; oops this is actually shorter in lima! ;; perl - perl's a little shorter tho "$a and $b and $c and $d"
    • Complicates escaping in pasted text and generated code. Lima's strings can contain any pasted text as long as quotes are escaped. Contrast that with ruby where not only do you have to escape any instance of "${" and "\" as well.
    • Many implementations of string interpolation are templates that are then filled in by values. An example of this is printr in C. The problem with this implementation is it visually separates the variables from the string, making it harder to read. Some may argue this separates presentation from data, but thats not correct. It only separates related code, which isn't good.
      ;; for example templateA = fn int[a b]: ret cat[a" and "b] ;; this is much harder to read templateB = fn int[a b]: ret "%s and %s".interpolate[a b] ;; hypothetical interpolation
    Embedding code in strings solves a non-existant problem. If you want to embed expressions inside strings, ask yourself why you aren't using variables.
  44. Lima's change and dif functions allows you to easily observe objects in an event-driven way
    var x = 3 change[x]: wout['x changed to:'x] x = 4 ;; 'x changed to : 4' is printed var y = {1 2 3} change[y:oldy]: var d = dif[oldy y] if d.set != nil: wout['y was set to a different object'] df d.elements v: if v.type == 'add': wout['y got a new element: 'v.val] v.type === 'move': wout['y got shuffled around'] v.type === 'remove': wout['y lost an element to the war :(']
  45. The attributes and allow a programmer to specify how external output may be grabbed before it's used. But for statments without this attributes, the optimizer can choose whether to grab a file lazily or not (it might not if it thinks starting loading some output will lower latency later) and there is no attribute to specify explicitly lazy data grabbing. So how do you specify that you want some external input to be grabbed lazily?

    You would need to use some kind of runtime behavior definitions - defining the latency/speed behavior in different segments of code. The way to define this is not yet specified, but it is definitely needed to define garbage collector behavior to prevent performance degredation during critical program events (like user input).

  46. A useful use of custom functionality is to modify normal syntax to mean something not-so-normal. For example, a custom function could be defined such that this:

    atomicAssignment [ a = getFromServer['a'] b = getFromServer['b'] c = getFromServer['c'] d = getFromServer['d'] e = getFromServer['e'] ]
    is transformed into:
    var temp_a = getFromServer['a'] var temp_b = getFromServer['b'] var temp_c = getFromServer['c'] var temp_d = getFromServer['d'] var temp_e = getFromServer['e'] atomic [ a = temp_a b = temp_b c = temp_c d = temp_d e = temp_e ]
    This would have the benefit of being much easier to write, while also allowing all those variables to be swiched at exactly the same time. This could be useful, for example, for programming a caching mechanism that refreshes periodically.

  47. The placement of the * and & symbols in C are not very intuitive, and force the reader to mentally parse a group of very similar looking syntax into very different semantics (values vs addresses). Lima's pointer syntax aims to reduce this cognitive load. As an (imperfect) quantatitive measure, we can count the number of "weird" symbols in this set of statements:
    ;; C Lima int a=5 int a = 5 int *p, *p2[p p2] int** p3, **p4[p3 p4] p = &a p ~> a p2 = p p2 ~> p p3 = &p p3 ~> p~ *p = a p = a *p = *p2 p = p2 *p = **p3 p = p3 p = *p3 p ~> p3 a = **p3 a = p3 p4 = p3 p4~ = p3~ *p4 = *p3 p4 ~> p3~~ **p4 = **p3 p4 = p3
    C has a count of 22 "weird" symbols (consisting of the structures: '*' and '&'), whereas Lima has a count of 13 weird symbols (consisting of the structures: 'ref', '~', and '~>'). This significantly lower number indicates that the reader doesn't have to do as much mental work to understand the statements. Lima's reference syntax is easier than C's when pointers are being pointed to normal values or are being used as the values they reference, but is harder when pointers are being pointed at other pointers, especially for higher level pointers (a pointer to a pointer to a pointer... etc). The argument here is that pointers are more often pointed at normal values and being used as those normal values, than being pointed at eachother. And higher level pointers are very rare.
  48. var sparseMatrix = { private var internal = {} access get else index: ret interal[index] | 0 set else index value: internal[index] = value }

  49. Warp the objects in your scene if you want to change the aspect ratio of the whole scene.
  50. In languages like C, a switch statement allows what's known as "fallthrough" where the statements under a 'case' continue onto the statements in the next 'case' unless you 'break' out of that case. Here's an example in C:

    switch( someValue ) { case 1: printf("One"); break; //.... case 20: printf("Twenty"); case 21: case 22: case 23: printf("Twenty-three"); }
    If someValue is 1, "One" will be printed, since it breaks out of that case. If someValue is 23, "Twenty-three" will be printed, because there are no cases after it. If someValue is 21 or 22, "Twenty-three" will still be printed, because they simply fall through to case 23's statments. If someValue is 20, "TwentyTwenty-three" will be printed, because case 20 has its own statement but still continues on to the statements in the next case (cases 21, 22, and 23). That last sequence of events for case 20 is generally considered pretty evil, because it's hard to read and it's easy to make mistakes that way.

    Lima doesn't have fallthrough, but there are a couple ways to write the same behavior in more readible ways. This Lima code does the same thing as the C code above, use the statement:

    cond fn[x: ret someValue==x] 1: wout['One'] ;;.... else: cond fn[x: ret x.has[someValue]] {20 21 22 23}: if someValue == 20: wout['Twenty'] ;; only print 'Twenty' if the value is 20 wout['Twenty-three'] ;; print 'Twenty-three' for all four values

  51. Here are four very similar examples that show how using threads and the attributes and affect program flow.
    ;; the order of grabbing those files is potentially asynchronous (order doesn't matter), ;; so they can all potentially be grabbed at once ;; however output is assumed to be sequential (order matters) so this will always output ;; the length of A.txt first, B.pdf second, and C.html third ;; example output if ;; A.txt is 100kb and takes 1 second to download, ;; B.pdf is 10 bytes and takes 1 ms to download, ;; C.html is 1000 bytes and 10 milliseconds to download, and ;; we're ignoring the trivial amount of time the rest of the code takes: ;;[ A.txt: 100000 - at 1 seconds B.pdf: 10 - at 1 seconds C.html: 1000 - at 1 seconds ;;] getLength = fn host file: tcp.connect[adr[host 80] 'httpText["utf-8"]'].send['GET 'file' HTTP/1.0'@ @].len main: fra startTime = time[] ;; time in seconds var lengths = {} df {"A.txt" "B.pdf" "C.html"} file: lengths[file] = getLength['' file] df lengths length file: wout[file': 'length' - at: 'time[]-startTime' seconds'@]

    ;; input is asynchronous here again, the default ;; since output is also asynchronous between threads (obviously), its sequential per thread, ;; the lengths will be printed as those files come in ;; As far as speed, this program won't complete any faster than the previous example, but the first file printed may happen faster ;; since A.txt might not be the first file to download, a file that took less time to download will be printed first ;; example output with same conditions: ;;[ B.pdf: 10 - at 0.001 seconds C.html: 1000 - at 0.010 seconds A.txt: 100000 - at 1 seconds ;;] getLength = fn host file: tcp.connect[adr[host 80] 'httpText["utf-8"]'].send['GET 'file' HTTP/1.0'@ @].len main: var lengths = {} df {"A.txt" "B.pdf" "C.html"} file: lengths[file] = getLength['' file] df lengths length file: thread: wout[file': 'length' - at: 'time[]-startTime' seconds'@]

    ;; input now waits on the last input to complete, meaning that those files are pulled in synchronous order ;; even tho output is running in separate threads, the output is forced to be sequential (A.txt first, B.pdf second, and C.html third) ;; This way will definitely be slower since the files can't download in parallel ;; example output with same conditions: ;;[ A.txt: 100000 - at 1.011 seconds B.pdf: 10 - at 1.011 seconds C.html: 1000 - at 1.011 seconds ;;] getLength = fn host file: tcp.connect[adr[host 80] 'httpText["utf-8"]'].send['GET 'file' HTTP/1.0'@ @].len main: var lengths = {} var nextFuture = future[nil] ;; an immediately resolved future df {"A.txt" "B.pdf" "C.html"} file: nextFuture.wait: nextFuture = future lengths[file] = getLength['' file] df lengths length file: thread: wout[file': 'length@]

    ;; the input is now marked 'ready' and so is ready whenever the system wants to get it - at best (and in this case) ;; that means compile-time ;; the output is still asynchronous, but it wouldn't matter if it wasn't - the running time and output would be the same ;; This way is the fastest since you don't have to do as much work at runtime (in this case its pretty much instant) ;; The output may still be sequential (as shown), but that isn't required ;; example output with same conditions: ;;[ A.txt: 100000 - at 0 seconds B.pdf: 10 - at 0 seconds C.html: 1000 - at 0 seconds ;;] getLength = fn host file: tcp.connect[adr[host 80] 'httpText["utf-8"]'].send['GET 'file' HTTP/1.0'@ @].len main: var lengths = {} df {"A.txt" "B.pdf" "C.html"} file: ready lengths[file] = getLength['' file] df lengths length file: thread: wout[file': 'length@]
  52. In node.js, npm's package.json has devDependencies and optionalDependencies. In lime, devDependencies aren't needed because those can simply be put in the "ignore" list so as to not be published at all. They can be accessed via the package's development repository if wanted.

    Optional dependencies aren't needed because Lima installs dependencies on run of a program that wants to load it, rather than having a separate install command needed. If a dependency fails to be loaded, the lima program trying to load it will see an exception and can deal with it appropriately if it wants to.

  53. Lima doesn't have any functions that exit the program because program exit should always be under the full control of the top-level module. Dependencies of a module shouldn't be allowed to hijack the program by killing it without going through proper error handling channels first.

  54. Languages like Java disallow certain types of equivalencies with generics. For example, List<Number> a = []; List<Integer> b = a; // fails because a Number might not always be an Integer

    In Lima, this isn't a problem. An exception would be thrown only if an incorrect value actually gets set on the list: list[real] w = {1 2 3} list[int] x = w ;; this is totally fine list[real] y = {1.2 3.4} list[int] z = y ;; this throws an exception because y does not only contain ints

  55. Think about the "variance" concept (described here ) where
  56. Tho some of these things would be better to be done in more than one line, this demonstrates how Lima uses some basic constructs to describle an infinity of possibilities. Going through the equivalents for PHP's associative array functions:

    ;; array_change_key_case(input, CASE_UPPER)[k.upper v] ;; array_chunk(input, size)[k/­/size] ;; keys preserve, so if you don't want that then do[k/­/size].map[v.sort[k2:k2]] ;; array_combine(keys, values) keys[[ values[c] v]] ;; values must be a list of elements ;; array_count_values(input)[v].map[v[0] v.len] ;; array_diff_assoc(array1, array2, ....) array1[[ !({k:v} <<= array2 $ {k:v} <<= array3 $ ....) ]] ;; array_diff_key(array1, array2, array3) array1[[ ! ((array2.keys & array3.keys).has[k]) ]] ;; array_diff_uassoc(array1, array2, keyCompareFunc) array1[[ !(array2{[v2 k2: keyCompareFunc[k v k2 v2]]}.join[a$b]) ]] ;; array_diff_ukey(array1, array2, keyCompareFunc) array1[[ !(array2{[v2 k2: keyCompareFunc[k k2]]}.join[a$b]) ]] ;; array_diff(array1, array2) array1[[ !array2.has[v] ]] ;; array_fill_keys(keys, value) keys[[k: value k]] ;; array_fill(5, 10, value) (514){[v:value]} ;; array_filter(input, callback) input[[ callback[v] ]] ;; array_flip(trans) trans[[v k]] ;; array_intersect_assoc(array1, array2, ....) array1[[{k:v} <<[....]]] ;; array_intersect_key(array1, array2, ....) array1[[[....].keys.has[k]]] ;; array_intersect_uassoc(array1, array2, ...., compareFunc) array1[[[....].map[] ]] ;; array_intersect_ukey($array1, $array2, ...., $compareFunc) array1[[ {k:v} << array2 $ {k:v} << array3 $ .... ]] ;; array_intersect($array1, $array2, $array3, ....) array1[[ (array2 & array3 & .....).has[v] ]] ;; array_key_exists($key, $array) array[key] != nil ;; or array.keys.has[key] ;; array_keys($array, $value, true) array[[v==value]].keys ;; array_map($callback, $array) array[[ callback[v] ]] ;; array_merge_recursive($array1, $array2, ....) ;; There is no simple equivalent to this difficult-to-understand php function - and there shouldn't be. ;; array_merge($array1, $array2, ....) ;; Like array_merge_recursive, this method doens't make any damn sense, but here it is: fn[x arrs...: var result = x df arrs arr: df arr v k: if IsValidIndex[k]: result.=ins[v] else: if result[k] == nil: result[k] = v else: result[k] = {k: {result[k] v} IsValidIndex = fn x: ret 0..00 ][array1 array2 ....] ;; array_multisort($array1, $array2) // heres another poorly designed php function array1.sort[] array2.sort[] ;; why do you need to sort them in one function? Cause you're function names are so verbose? ;; array_pad($input, $padSize, $padValue) df 0..padSize v: if input[v] == nil: input[v]=padValue ;; that wasn't so hard was it? ;; array_pop($array) var result = array[array.len-1] array.rm[array.len-1] result ;; array_product($array) array.join[a*b] ;; array_push($array, $val1, $val2, ....) array.=cat[ {val1 val2 ....}] ;;array_rand($input) input[rand] ;; array_rand($input, $num) 0..num[[ input[rand] ]] ;; array_reduce($array, $function, $initial) array.join[initial function[a b]] ;; array_replace_recursive($array, $array1, ....) fn[x arrs...: var result = x ;; create copy df arrs arr: df arr v k: if IsList[result[k]] & IsList[v]: ;; if both valures are lists result[k] = fn.this[result[k] v] else: result[k] = v var IsList = fn x: ret x?.IterList != nil ][array array1 ....] ;; array_replace($array, $array1, ....) fn[x arrs...: var result = x df arrs arr: df arr v k: result[k] = v ][array array1 ....] ;; array_reverse($array) ;; don't preserve keys array.keys.sort[-k] ;; reverse keys array.sort[k] ;; then rekey ;; array_reverse($array, true) ;; preserve keys array.sort[-k] ;; assuming keys are currently in order array.keys.sort[-k] ;; no assumptions ;; array_search($needle, $haystack, true) haystack[[v == needle]].keys[0] ;; array_shift($array) var result = array[0] array.rm[0] result ;; array_slice(array, offset, length, false) ;; don't preserve keys array[[(offset..(length-1)).has[k]]].sort[k] ;; array_slice(array, offset, length, true) ;; preserve keys array[[(offset..(length-1)).has[k]]] ;; array_splice($array, $offset, $length) var condition = fn k: (offset..(length-1)).has[k] var result = array[[condition]] array.rm[[condition]] ;; array_splice($array, $positiveOffset, $positiveLength, $replacements) df array[[(positiveOffset..(positiveLength-1)).has[k]]] v k c: v = replacements[c] ;; array_splice($array, $negativeOffset, $negativeEnd, $replacements) df array[[((array.len-negativeOffset)..(array.len-negativeEnd)).has[k]]] v k c: v = replacements[c] ;; array_sum array[v+v2] ;; array_udiff_assoc($array1, $array2, ...., $compare) array1[[ !( array2[[k2 v2: k==k2 & compare[v v2] ]].join[a&b] $ ....) ]] ;; array_udiff_uassoc($array1, $array2, ...., $dataCompare, $keyCompare) array1[[ !( array2[[k2 v2: keyCompare[k k2] & dataCompare[v v2] ]].join[a&b] $ ....) ]] ;; array_udiff($array1, $array2, ...., $compare) array1[[ !(array2[[v2: compare[v v2] ]].join[a&b] & ....) ]] ;; array_uintersect_assoc($array1, $array2, ...., $compare) array1[[ array2[[v2 k2: k==k2 & compare[v v2] ]].join[a&b] & .... ]] ;; array_uintersect_uassoc($array1, $array2, ...., $dataCompare, $keyCompare) array1[[ array2[[v2 k2: dataCompare[k k2] & keyCompare[v v2] ]].join[a&b] & .... ]] ;; array_intersect($array1, $array2, ...., $dataCompare) array1[[ array2[[v2: dataCompare[v v2] ]].join[a&b] & ..... ]] ;; array_unique(array) array.sort[v][[!({v}<s)]] ;; array_unshift(array, var1, var2, ....) array.=ins[0 {var1 var2}] ;; array_values(array)[v c] ;; array_walk_recursive var array_walk_recursive = fn inputs func userData: var walk = fn value: if value.len == nil: func[v k userData] else: df aList v k: func[v k userData] walk[inputs] ;; array_walk(array, callback, other) array.=map[callback[v k other] k] ;; array(....) {....} ;; arsort(array) array.keys.sort[-array[v]] ;; integers array.keys.sort[array[v]].sort[-k] ;; anything ;; asort(array) array.keys.sort[array[v]] ;; compact(varname, ...) fn.this[[{varname ....}.has[k]]] ;; count(array) array.len ;; extract(var_array) use varArray ;; in_array(needle, haystack) {needle} < haystack ;; krsort(array) array.keys.sort[v].sort[-k] ;; ksort(array) array.keys.sort[v] ;; list(a, b, c) = array('a','b','c') {a b c} = {'a' 'b' 'c'} ;; natcasesort(array)[v.lower].fsort[v.natcmp[v2]<0] ;; natsort(array) array.fsort[v.natcmp[v2]<0] ;; range(low, high, step) (low/step...high/step){[v*step]} ;; prev, pos, current, next, reset ;;[ function testArrayIteratorfunctions() { $array = array('step one', 'step two', 'step three', 'step four'); // by default, the pointer is on the first element echo current($array) . "
    \n"; // "step one" next($array); // skip two elements next($array); echo current($array) . "
    \n"; // "step three" prev($array); // rewind one echo current($array) . "
    \n"; // "step three" // reset pointer, start again on step one reset($array); echo current($array) . "
    \n"; // "step one" } ;;] testArrayIteratorfunctions = fn: var cur=0 var array = {'step one' 'step two' 'step three' 'step four'} var i = array.iterList ;; grab array's iterList, to iterate in the same order df would ;; echo first element wout[array[i[cur]] '
    '@] cur+=2 ;; skip two elements wout[array[i[cur]] '
    '@] cur-- ;; rewind one wout[array[i[cur]] '
    '@] cur = 0 ;; reset index, start again on 'step one' wout[array[i[cur]] '
    '@] ;; rsort(array) array.sort[-v] ;; numbers array.fsort[!fn[ret v < v2][v v2]] ;; strings ;; shuffle(array) array.sort[rand[]] ;; sizeof(array) array.len ;; sort(array) array.sort[v] ;; uasort(array, cmp_function) array.keys.fsort[cmpFunction[array[v] array[v2]]] ;; uksort(array, cmp_function) array.fsort[cmpFunction[k k2]] ;; usort(array, cmp_function) array.fsort[cmpFunction[v v2]]
  57. ;; intersperse '.' "MONKEY" ;; intersperse 0 [1,2,3,4,5,6] "MONKEY".join[['.' b]] {1 2 3 4 5 6}.join[{}[{0 b}]] ;; intercalate " " ["heyyy","youuu","guyyyyyys"] ;; intercalate [0,0,0] [[1,2,3],[4,5,6],[7,8,9]] {"heyyy" "youuu" "guyyyyyys"}.join[[" " b]] ;; notice that this is the same form as is used for string intersperse {{1 2 3} {4 5 6} {7 8 9}}.join[{}[{0 0 0 b}]] ;; transpose [[1,2,3],[4,5,6],[7,8,9]] {0..00}.map[k: {{1 2 3} {4 5 6} {7 8 9}}.map[v[k]] ] ;; fold (+) [1,2,3,4,5] {1 2 3 4 5}.join[a+b] ;; concat ["foo","bar","car"] ;; concat [[3,4,5],[2,3,4],[2,1,1]] {"foo" "bar" "car"}.join[[b]] {{3 4 5}{2 3 4}{2 1 1}}.join[[b] ;; concatMap function aList[function[v]].join[[b]] ;; and [True, False] {true false}.join[a&b] ;; or [True, False] {true false}.join[a$b] ;; any (==4) [2,3,5,6,1,4] {2 3 5 6 1 4}.any[v==4] ;; all (>4) [6,9,10] {6 9 10}.all[v>4] ;; iterate (*2) 2 {1..00}.map[v*2] ;; splitAt 3 "heyman" var x = "heyman" {x[[k<3]] x[[k>3]]} ;; takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1] ;; takeWhile (/=' ') "This is a sentence" {6 5 4 3 2 1 2 3 4 5 4 3 2 1}.split[{3}][0] "This is a sentence".split[" "][0] ;; dropWhile (/=' ') "This is a sentence" " ".cat["This is a sentence".split[" "][[k>0]].join[[" " b]]] ;; span (/=' ') "This is a sentence" var x = "This is a sentence" {x.split[" "][0] " ".cat[x.split[" "][[k>0]].join[[[" " b]]]]} ;; sort [8,5,3,2,1,6,4,2] {8 5 3 2 1 6 4 2}.sort[v] ;; group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7] var newList = {} df {1 1 1 1 2 2 2 2 3 3 2 2 2 5 6 7} x: if x!=newList.sort[k][0][0]: newList.ins[{}] newList[newList.len-1].ins[x] newList ;; map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7] {1 1 1 1 2 2 2 2 3 3 2 2 2 5 6 7}.group[v].map[{v[0] v.len}] ;; inits "w00t" var result = {""} df "w00t" x: result.ins[result.sort[-k][0],x] result ;; tails "w00t" var result = {"w00t"} var last = fn: return result.sort[-k][0] while last[].len > 0: var next = last[] next.rm[0] result.ins[next] result ;; "cat" `isInfixOf` "im a cat burglar" "im a cat burglar".find["cat"].len > 0 ;; "hey" `isPrefixOf` "hey there!" "hey there!".find["hey"].any[v.keys.has[0]] ;; "there!" `isSuffixOf` var x = "oh hey there!" x.find["there!"].any[v.keys.has[(x.len-1)]] ;; partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy" var result = "BOBsidneyMORGANeddy".group[('A'..'Z').has[v]] {result.true result.false} ;; find (>4) [1,2,3,4,5,6] {1 2 3 4 5 6}[[v>4]][0] ;; 4 `elemIndex` [1,2,3,4,5,6] {1 2 3 4 5 6}[[v==4]].k[0] ;; ' ' `elemIndices` "Where are the spaces?" "Where are the spaces?"[[v==' ']].keys ;; findIndex (==4) [5,3,2,1,6,4] {5 3 2 1 6 4}[[v==4]][0].keys[0] ;; lines "first line\nsecond line\nthird line" "first line"@,"second line"@,"third line".split[""@] ;; unlines ["first line", "second line", "third line"] {"first line" "second line" "third line"}.join[a,@b] ;; words "hey these are the words in this sentence" "hey these are the words in this sentence".split[" "] ;; unwords ["hey","there","mate"] {"first line" "second line" "third line"}.join[a," ",b] ;; delete 'h' "hey there ghang!" var x = "hey there ghang!" x.rm[ x[[v=='h']].k[0] ] ;; [1..10] \\ [2,5,9] ??? ;; [1..7] `union` [5..10] 1..7 $ 5..10 ;; [1..7] `intersect` [5..10] 1..7 & 5..10 ;; insert 4 [3,5,1,2,8,2] var x = {3 5 1 2 8 2} var y = 4 x.ins[x[[v>y]].k[0] y] ;; why would you want to make this a core function in your language??? ;; Map.fromList [(1,2),(3,4),(3,2),(5,5)] {{1 2}{3 4}{3 2}{5 5}}.map[{v[0]:v[1]}] ;; Map.empty {} ;; Map.insert 3 100 Map.empty {}[3][100] ;; Map.null Map.empty {} == {} ;; duh ;; Map.size Map.empty {}.len ;; Map.singleton 3 9 {3:9} ;; lookup 3 Map.fromList [(1,2),(3,4),(3,2),(5,5)] {{1 2}{3 4}{3 2}{5 5}}[3] ;; Map.member 3 $ Map.fromList [(1,2),(3,4)] {{1 2}{3 4}}.keys.has[3] ;; (*100) $ Map.fromList [(1,1),(2,4),(3,9)] {1:1 2:4 3:9}.map[v*100] ;; Map.filter (>4) $ Map.fromList [(1,1),(2,4),(3,9)] {1:1 2:4 3:9}[[v>4]] ;; Map.toList . Map.insert 9 2 $ Map.singleton 4 3 {4:3}.map[{k v}] ;; keys Map.fromList [(1,1),(2,4),(3,9)] {1:1 2:4 3:9}.k ;; elems Map.fromList [(1,1),(2,4),(3,9)] {1:1 2:4 3:9}.v ;;[ phoneBook = [("betty","555-2938") ,("betty","342-2492") ,("bonnie","452-2928") ,("patsy","493-2928") ,("patsy","943-2929") ,("patsy","827-9162") ,("lucille","205-2928") ,("wendy","939-8282") ,("penny","853-2492") ,("penny","555-2111") ] Map.lookup Map.fromListWith (\number1 number2 -> number1 ++ ", " ++ number2) phoneBook ;;] var phoneBook = {{"betty" "555-2938"} {"betty" "342-2492"} {"bonnie" "452-2928"} {"patsy" "493-2928"} {"patsy" "943-2929"} {"patsy" "827-9162"} {"lucille" "205-2928"} {"wendy" "939-8282"} {"penny" "853-2492"} {"penny" "555-2111"} }[v[0]].map[ v.join["" a,", ",b[1]] ]
  58. Tho some of these things would be better to be done in more than one line, this demonstrates how Lima uses some basic constructs to describle an infinity of possibilities. Going through the equivalents for Python's itertools library functions:

    ;; count(10, 2) (10..00)[[v%2==0]] ;; cycle([1,2,3,4]) {1 2 3 4}*00 ;; repeat(10) {10}*00 ;; chain([1,2,3], [4,5,6]) {1 2 3}.cat[{4 5 6}] ;; compress([1,2,3,4], [1,0,1,0]) {1 2 3 4}[[{1 0 1 0}[k]]] ;; dropwhile(lambda x: x<5, [1,4,6,4,1]) var x = {1 4 6 4 1} var elements = x[[!(v<5)]] ;; elements that don't match the criteria v<5 x[[ k>=elements.keys[0] ]] ;; groupby(alist lambda x: x%4)[v%4] .map[v.sort[k]] ;; to get rid of the key preservation that python doesn't have ;; ifilter(lambda x: x%2, [10,11,12,13]) {10 11 12 13}[[v%2]] ;; ifilterfalse(lambda x: x%2, [10,11,12,13]) {10 11 12 13}[[v%2 == 0]] ;; islice([1,2,3,4], 2, 100, 4) {1 2 3 4}[[ k in (2..100)[[k%2==0]] ] ;; imap(func, list, list2)[func[v list2[k]]] ;; starmap(func list)[func[v[0] v[1]]] ;; tee(alist, 4) (0..3).map[alist] ;; makes a list of copies of alist ;; takewhile(alist func) df alist v k: if !func[v]: alist.rm[[key: key>=k]] ;; izip(alist, anotherList)[if[anotherList[k] == nil: nil else: {v anotherList[k]}] ;; izip_longest(alist, anotherList, fillvalue='-')[if[anotherList[k] == nil: '-' else: {v anotherList[k]}]
  59. A program that simple takes each line and creates a new file where each line is labeled with line numbers.


    out = open('uid list'+str(absoluteCount)+'.txt','w') with open('mif_retarget_fbid_12_12_11.csv','r',encoding='utf-8') as f: for line in f: line = lines[n] out.write("Line "+str(n+1)+"'"+line+"'\n") if n%50000 == 0: out.close() out = open('uid list' + str(int(n/50000)+1) + '.txt','w') out.close()


    var input = file['mif_retarget_fbid_12_12_11.csv' 'utf-8'] df input.split[''@] line n: file['uid list' n/­/50000+1 '.txt' 'utf-8'].=cat[ "Line " n+1 ": '"line"'"@]

    Because Lima automatically handles file closure when there are no more references to that file, the programmer doesn't have to deal with that. And because Lima will optimize access to the output file, a reference to the file doesn't need to be explicitly stored, and can be conceptually opened on every iteration of the loop (although any reasonable optimization will leave the file open over multiple iterations).

  60. Taking an example, from lua's manual:


    function downloadAndPrintLength (host, file) local c = assert(socket.connect(host, 80)) local count = 0 -- counts number of bytes read c:send("GET " .. file .. " HTTP/1.0\r\n\r\n") while true do local s, status = receive(c) count = count + string.len(s) if status == "closed" then break end end c:close() print(file, count) end function receive (connection) connection:timeout(0) -- do not block local s, status = connection:receive(2^10) if status == "timeout" then coroutine.yield(connection) end return s, status end threads = {} -- list of all live threads function get (host, file) -- create coroutine local co = coroutine.create(function () downloadAndPrintLength(host, file) end) -- insert it in the list table.ins(threads, co) end function dispatcher () while true do local n = table.getn(threads) if n == 0 then break end -- no more threads to run local connections = {} for i=1,n do local status, res = coroutine.resume(threads[i]) if not res then -- thread finished its task? table.remove(threads, i) break else -- timeout table.ins(connections, res) end end if table.getn(connections) == n then end end end host = "" get(host, "/TR/html401/html40.txt") get(host, "/TR/2002/REC-xhtml1-20020801/xhtml1.pdf") get(host, "/TR/REC-html32.html") get(host, "/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt") dispatcher() -- main loop


    downloadAndPrintLength = fn host file: var c = tcp.connect[adr[host 80] 'httpText["utf-8"]'] c.send['GET 'file' HTTP/1.0'@ @] ;; sending a request wout[file c.len] ;; prints the length of the data returned (waits until the connection is closed from the other end, since c.len isn't known until then) get = fn host file: thread: downloadAndPrintLength[host file] main = fn: host = '' get[host "/TR/html401/html40.txt"] get[host "/TR/2002/REC-xhtml1-20020801/xhtml1.pdf"] get[host "/TR/REC-html32.html"] get[host "/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt"]

    The vast difference in size of these implimentations comes from Lima's simple thread syntax and automatic optimization (such that a thread may use coroutine-style under the hood), Lima's ubiquitous list syntax that is used for any list-like entity, and implicit futures (like c.len which blocks the execution of that thread until it is known).

  61. Encoding issues

* update everything that uses channels with *all* the 'chan' functionality like the || operator * still to go: tcp, udp, http * probability and value duality/multiplicity - maybe call it something like unsurity or .. i like multiplicit value * how do you describe a probability map that doesn't have full information? * Like lets say you know that a value from 0 to 3 is returned 90% of the time, but you're not sure what happens the other 10% of the time * It might have to provide a range of possibilities, especially if you want the probabilities over a range that covers some knowns and some unknowns, for example (using the same 90% 10% example) if you want to know the probability that you get a value from 0 to 5, the answer would be between 90% and 100%. * this goes back to an object representing a set of values * numbers could be extended to be sets, and operations like + and * can operate on all the members, while testing operations like < and == can mean "are all the members less than?" or "are all the members equal to?" and you'd have to have separate operations to describe "are some of the members less than" etc. * A scratchpad example of what it might look like for you to "know that a value from 0 to 3 is returned 90% of the time, but you're not sure what happens the other 10% of the time": probability.mass[ ret { 0 .. 3 : .9 ... 1 else : 0 ... .1 } ] or maybe probability.mass[ ret { 0 .. 3 : .9 else : 0 .1 } ] * optimize needs to have a way to specify things like "optimize this for the 80% most likely inputs" * mark which objects in the documentation are "core" (things must be part of the language to express everything) as opposed to "complex" (constructs that can be created from the "core" constructs) * consider getting rid of 'static' and 'fixed' attributes. Javascript and Ceylon don't have static variables. * This would need a lot of thought to determine lima shouldn't have static variables * probably functions don't need them, but you do want some variables you can access on a protoype object that aren't instance variables (maybe) * * custom needs to be fleshed out with functions that can be used to parse the character strings it gets * Should custom functions return strings that are re-evaluated in context? Probably * maybe rename custom to "macro" to be more consistent with like all of programming history? * Allow full code written in objects' "definition space", even variables you create are accessible in that scope (things like key:value aren't tho unless you access it via this) * make sure you can do things like stream backpressure eg: * Count the number of bytes that have been sent over the network for a file/stream * Figure out how you might do something like perhaps some kind of attribute? * Think about how you would be able to put in dependency constraints into a system, something like private/public kinds of things in classes, but at a higher level. Like saying that a particular module can access some set of other modules, but not others. Strong coupling is a huge problem in programming * remove and and add an encoding object for ASCII that has those available Add a URL object for handling URLs (like java's URL, but better obviously). Members: make = fn string urlString: ;; creates a url object - e.g.¶m=y¶m2=z#someFragment protocol host port=nil path: encode ;; url encode (move this from http.urlEncode) decode ;; url decode (move this from http.urlDecode) query objectMap ;; makes a urlencoded query string from an object (move from http.makeQuery) URL objects have members: protocol ;; e.g. http has additional member 'defaultPort' host ;; e.g. port ;; Gives default if no port was specified in the creation of the URL object - e.g. 80 path ;; e.g. /some/path.html query ;; e.g. {param={'x' 'y'} param2={'z'}} str ;; returns the url encoded query string frag ;; fragment identifier (called ref in Java's URL class) - the part after the hash mark (#) e.g. someFragment str ;; returns the url as a string (omits the port in the string if that port is default, even if the port was originally specified) Note that for URL, members are mutable, so you can change parts of a URL at will Ning asynchronous java http library What it does right: allows access to all major parts of the request response lifecycle Next question, how to do you do explicit futures? You can execute something in a thread and wait on the thread, but i can see there might be cases where you don't need (or want) to wait on the whole thread just for the result of one variable. Not sure what the cases are here, think about it. Think about a program's working directory and how to reference files relatively in different contexts (running main program from its directory, running main program from different directory, running supporting library scripts). In many cases I want scripts to reference files relative to the script location, and other times you might want the working directory of the user to be used. Think about how to enable all these scenarios easily and clearly. Maybe have a way for a script to change its own working directory? Three types of normal directories you could mean: * cwd - the directory where the user started the program from * main source directory - the directory of the executable being run * module directory - the directory of the module * df-else? A branch executed only if the loop was never taken? *
Last Updated: 2017-02-01