The Brain4it programming language (BPL) is inspired in functional languages like LISP or Scheme, that have been widely used over the years in the development of artificial intelligence applications.
BPL is a simplified version of these languages that depite of being much smaller, offers similiar possibilities.
Its main features are:
BPL only supports 3 basic data types:
-5.72e9. The special values
NaN(not a number),
-Infinityare also supported.
BPL also defines the literal null.
All these data types are immutable, which means that its value cannot be changed.
A reference is a name, symbol or path that references an object of any type.
There are two types of references:
+, etc. Hard references are not assignable, so they always point to the same built-in function.
When a soft reference includes slashes (/) it is a path reference. Path references indicate a path through a list sctructure to access the data. The components of a path reference can be strings to access by name or integers to access by index within the list, as discuss below.
The name of a non path reference may contain any character except space, slash (/), double quote (") and parenthesis and the first character can not be a digit.
Soft references can not match the name operator
=>, a built-in function or the literals
All references are immutable because its name can not be changed.
The list is the only data structure supported in the Brain4it programming language.
Lists allow you to represent both data and code. They are expressed by parenthesis and may contain from 0 to N elements of any type, separated by spaces. Examples:
()an empty list.
(1 2 3 4)a list that contains 4 numbers.
("red" "green" "blue")a list containing 3 strings.
(12413 data-on ("on" "of") (3 8 5) 7.8)a list that contains elements of different types and other lists.
(+ (* 8 a) (* 2.4 b))a list containing the mathematical expression 8 * a + 2.4 * b.
(while (< a 10) (push elems a) (++ a))a list that encodes a fragment of a program.
Elements in a list can be labeled with a name. That name is a string that must be unique within the list.
The arrow (or name) operator
=> is used to set a name for an element. Examples:
("temp" => 25.32)"temp" is the name for the element 25.32
("us-dollar" => 105.42 "yen" => 0.34)a list that contains 2 numbers labeled with names "us-dollar" and "yen".
("red" => "FF0000" "green" => "00FF00" "blue" => "0000FF")a list that contains 3 strings labeled with names "red", "green" and "blue".
(number "100100100" "radix" => 2)a list with 3 elements where the last one has the name "radix".
Elements in a list can be accessed by position or by name.
For example, if we assign the variable
persons this list:
(("name" => "John" "age" => 34 "0" => 45) ("name" => "Mary" "age" => 31))
persons/0will point to
("name" => "John" "age" => 34)
persons/0/agewill point to
persons/1/namewill point to
persons/0/"0"will point to
Later we will see in more detail how to access the elements of a list.
A list is mutable because you can add and remove elements to it.
The Brain4it programming language has the following evaluation rules:
"Hello world"evaluates to
(1 2 3)evaluates to
(1 2 3)
(+ 3 4)evaluates to
7. + is the built-in sum function, and numbers 3 and 4 are the arguments passed to that function.
(if (> 3 2) "GREATER" "LOWER")evaluates to
(sort (4 1 7 2))evaluates to
(1 2 4 7).
(data 3 4)evaluates to
(data 3 4)because
datais not a function.
(fact 5)evaluates to
factis a user function that implements the factorial.
The Brain4it programming language has 2 types of functions:
BPL has built-in functions to perform basic operations like assigment of variables, conditionals and loops. Also offers a rich set of functions to work with lists, strings, numbers and dates. Next we will see some of the most common built-in functions:
The assigment of variables is done through the
(set result (+ 4 5))
In this example, the variable (or reference)
result takes the value 9.
set function, like many other built-in functions, do not evaluate
the reference that is going to assign a value (
result in the example).
Only when the first argument is not yet a reference the
set function will
evaluate it to get the final reference, like in this example:
(set (get result) 8) where the value
8 will be assigned
to the reference pointed by
A conditional instruction can be done with the
if function. Its syntax is:
(if condition action_when_true action_when_false)
(if (> level 7.5) "OK" "KO")
If the value of
level is greater than
7.5 the funtion will return
"OK", otherwise will return
A sequence of actions can be grouped with the
(if (> level 7.5)
When you have to code an if-then-else cascade its more convenient to use the
cond function only executes the actions of
when clause which condition evaluates to
(when (= state 1)
(set speed 4)
(when (= state 2)
(set speed -4)
(set speed 0)
In that example, if the initial value of
speed will take the value
For doing loops, BPL has the
while function, that works
while statement of many other programming languages:
while the condition evaluates to
true, the actions inside the
will be executed.
(while (>= a 0)
(-- a 2)
In that example, variable
a will be decremented by
until its value be less than
An alternative to the
while function is the
that has a syntax pretty similiar to the
for statement of C like languages:
that is equivalent to:
(for (set i 0) (< i 10) (++ i)
That code will execute
The more common built-in functions to manage lists are
list function creates a new list and adds to it the evaluation of its arguments:
(list (+ 3 4) "a" => (- 5))
(7 "a" => -5)
get function allows you to obtain an element of a list
accessing by index or by name.
Its general form is:
(get lst spec)
lst is the list, and
spec is the index or name.
(get (1 2 "a" => 3) 1)
In that example the function will return the element at index
which is the value
2 (the index of the first element is 0).
(get (1 2 "a" => 3) "a")
get function will return the element whose name is
which is the value
To put an element into a list, BPL has the
put function. Its syntax is:
(put lst spec value)
lst is the list,
spec is the index or name
value the element to put.
(set my_list (1 2 "a" => 3))
(put my_list 0 "Mary")
(put my_list "a" "Rick")
(put my_list 5 99)
After executing that code, variable
my_list will be:
("Mary" 2 "a" => "Rick" null null 99)
When the specified index is outside the size of the list,
that list is automatically enlarged to put the element, filling with
the new positions in the list.
Removing elements from a list is done with the
remove function, that has this syntax:
(remove lst spec)
lst is the list, and
spec is the index or name of the element to remove.
Built-in functions are organized in libraries. The Core library contains the basic functions that all programs need and is always present in the Brain4it servers.
Some other libraries may also be available to access external services or hardware resources. See the Library catalog for more information.
An anonymous user function is defined this way:
(function (parameter ... parameter)
parameter elements represent the
parameters that takes the function,
action elements the actions to execute.
The value returned by the function is the result of evaluating the last action.
(function (x y) (* x (sin y)))
That function takes two parameters x and y and will return the result of evaluating x * sin(y).
We can invoke an anonymous function using the
(call (function (x y) (* x (sin y))) 45.2 3.6)
To give that function a name, assign the function to a variable:
(set sin_product (function (x y) (* x (sin y))))
and then invoke the function with a calling list like this:
(sin_product 45.2 3.6)
The parameters of a user defined function can be defined in three ways:
x) : the value of the parameter (
x) is taken from the calling list accessing it by the position of this parameter.
"n" => x) : the value of the parameter (
x) is taken from the calling list accessing it by the name of the reference (
"x") : the value of the parameter (
x) is taken from the calling list accessing it by this string (
"x"). That is equivalent to
"x" => x.
When a parameter is not specified in the calling list, its value
For example, if we want to define a function that takes a mandatory
value and 2 optional parameters
we can define it like this:
(function (value "offset" "factor" => f)
(* f (+ value offset))
If we invoke that function with this calling list:
f will be
the function will return the value
7, but in this case:
(fn 7 "offset" => 5)
f will be
null and the result will be
12. In this other call:
(fn 7 "factor" => 2)
offset will be
null and the returned value
14 and here:
(fn 7 "offset" => 5 "factor" => 2)
we will obtain the value
User defined functions unlike built-in functions receive
its arguments already evaluated. If you need to pass an expression
to a user defined function without evaluating it, use the
(process_data (quote (+ 1 2))). The
may evaluate that argument with the
BPL supports recursion, that is functions that call themselves.
For example, the factorial function can be defined in this way:
(if (< x 1) 1 (* x (fact (- x 1))))
BPL supports exceptions as the Java programming language.
Exceptions are thrown in these cases:
Exceptions can be handled with the
try function, that works
like the Java try-catch-finally statement. Its general syntax is:
(ex_var catch_action ... catch_action)
Let's see an example:
(set res (get_resource))
"ConnectException" => (abort_process)
"SQLException" => (abort_process)
"IOException" => (process_resource res)
"*" => (notify_error ex)
When an exception occurs the ex_var variable (
ex in the example) will contain a list like this:
"Can't connect to http://brain4it.org"
"code" => (http "GET" url)
"stack" => (do_work process_resource)
where the first element is the exception type (usually the simple name of the Java Exception class) and the next elements (not always present) are the detail of the exception:
The executed catch_action is that whose name matches the exception type (
(abort_process) in the example).
If there is no catch_action that matches the exception type, then the catch_action
whose name is
"*" will be executed.
If the main_action throws an exception that is not handled by any catch_action that exception will be propagated to the superior function.
The finally_action will always be executed whatever an exception is thrown or not.
The value returned by the
try function is the result of
evaluating the main_action or in case that main_action throws an exception,
the result of evaluating the catch_action that handles that exception.
try function differs from the Java try-catch-finally statement in these aspects:
killfunction always force the interruption of the executor thread.
throw function allow us to throw an exception. Example:
(throw "UserError" "The value entered must be positive")
where the first argument is the exception type and the second argument (optional) is the message of the exception.
throw function can also accept an exception list as the first argument:
(throw ("UserError" "message" => "The value entered must be positive"))
The Brain4it programming language supports running code in multiple execution threads.
Every time a command is launched, the server creates an executor thread that will run that command until it ends.
We can list all the executors currently running within a module with the
the result could be a list like this:
("45" => (while true (eval rules) (sleep 1000)) "47" => (executors))
This list contains for each executor the code that it is running labeled with the executor_id, the executor identifier.
If we want to interrupt the executor identified by
"45", simple type:
This command invokes the Thread.interrupt() Java method on the executor thread to force its finalization.
Sometimes we want to run a code without waiting for the result. That
can be achived with the
(spawn (do (sleep 5000) (beep))
spawn creates the new executor thread and immedialety returns its executor_id.
To synchronize multiple executor threads, BPL offers functions similiar to those
that exists in the Java programming language:
See the core library documentation for details.
All executor threads running inside a Brain4it server see two variable scopes:
Every time we call a function, a new local scope is created. The arguments of the function are put into that scope. When the function returns, the scope is destroyed.
If a variable is set with a statement like this:
(set alfa 9)
that variable will be created in the global scope (also inside a function) and thus will be visible by other executors in the same module.
If we want to create a variable in local scope we must declare the variable this way:
That variable will last as much as the local scope where it was created.
Variable scopes in BPL are implemented as lists.
We can obtain the global scope list with the
that may return a list like this:
("alfa" => 9 "square" => (function (x) (* x x)))
The local scope is obtained with the
Both scopes, global and local, can be manipulated as normal lists.
To remove a variable from the scope where it is defined,
you can use the
To check if a variable is defined in the current scopes (local or global),