Declarations and expressions are mutually recursive; declarations will be presented first. A block is a sequence of declarations separated by semicolons, and every declaration occurs inside a block. A block has a tuple type; it contains a component for every non-private name defined in it (see below). IBAL contains the following kinds of declaration:
x = e, associates name x with expression e.
x:t = e, associates name x with expression
e, and asserts that its type matches type expression t.
f(x_1,...,x_n) = e, associates name f with the
function that takes x_1,...,x_n (all lidents) as arguments, and
has the body e.
f(x_1,...,x_n) : t = e, associates name f with the
function that takes x_1,...,x_n (all lidents) as arguments, and
has the body e. The definition also asserts that the function
f has type that matches type expression t.
f x = e, associates name f with the function that
takes the single argument x (an lident), and has the body
e.
f x = e, associates name f with the function that
takes the single argument x (an lident), and has the body
e. The definition also asserts that the function f has
type that matches type expression t.
Each of these forms can be preceded by the keyword private.
The private flag indicates that the name is not a component of
the tuple type of the block. Furthermore, declarations such as type
definitions within the associated expression are not visible outside
the block.
A decision defines a name whose value is to be decided by a utility-optimizing agent. The name is available for use in future expressions. The utility to be maximized is the sum of utilities earned in the block and those occuring in expressions associated with names in the block. The choices and information available to the decision maker are part of the decision declaration. The choices are always primitive values. There are three syntaxes for decisions:
decide x given [ cs ]
defines x to be a decision variable whose choices are the Booleans.
The decision maker has the information specified by
cs, which is a possibly empty list of longvars. Each chain
c specifies a variable whose value is available to the decision
maker at the time of making the decision.
decide x from ss given [ cs ]
is similar, except that the choices are the strings containes in the
non-empty list ss.
decide x from lb .. ub given [ cs ]
is similar, except that the choices are the integers from lb up
to ub.
As for definitions, the name defined by a decision is part of the
tuple type of the block, unless the decision is preceded by the
keyword private.
The keyword given, along with the list of given information,
can be omitted. In that case it is assumed that no information is
given.
An observation asserts that a variable, specified by a longvar, must have one of a set of values. The observation conditions the probability distribution over values on the variable being one of the allowed values. Syntaxes for observations are:
obs c asserts that c must be true.
obs ~c asserts that c must be false.
obs c = p asserts that c is accepted by pattern
p.
There are two kinds of reward declarations.
reward f where f is a float.
reward case e of # p_1 : f_1 ... # p_n : f_n
where e is an expression, the p_i are patterns, and the
f_i are floats.
Each p_i : f_i is called a clause.
The p_i must have the same type and be exhaustive, and e
must have the same type.
The hash before the first clause is optional. It is recommended when the conditional reward declaration is broken up over multiple lines, like here.
If the values of previously defined variables are fixed,
the meaning of the conditional reward declaration is as follows:
for any given value v of the appropriate type, define
f(v) to be f_1 if v is accepted by p_1,
f_2 is v is accepted by p_2 but not p_1
and so on.
Then the utility to the agent is the expectation, over the value
v of expression e using the fixed values of variables
defined in e, of f(v).
A parameter declaration defines a learnable probabilistic parameter that can be used in future expressions. A possible value of the parameter is a vector of floating point numbers adding to one, representing a multinomial probability distribution. Here, value is not used in the sense of IBAL value, and the parameter is not a component of the tuple type of the block.
The syntax of a parameter declaration is
param p = [ f_1, ..., f_n ], where p is an lident and
the f_i are posfloats.
This declares the parameter p to represent an
n-dimensional multinomial distribution, and assigns it a
Dirichlet prior with hyperparameters f_1, ..., f_n.
The actual value of a parameter is learned from observations. In the standard framework, the value computed will be the maximum a-posteriori (MAP) value, i.e., the one that maximizes the product of the prior and the likelihood of the observations.
One may define a new type to be a synonym for a type expression.
The syntax is
type U a_1, ..., a_n = t where U is a uident, each
a_i is an lident, and t is a type expression.
U is the new type name being defined, while the a_i are
arguments to the type. U can then be used in a type
expression, with type expressions t_1, ..., t_n supplied for
the arguments. It will then be replaced with t, with
t_i substituted for each a_i.
t may not contain free type variables other than the type
arguments.
The arguments may be surrounded by square brackets for readability.
One may create a new algebraic data type (ADT). An ADT provides one or more different constructors. Each constructor has zero or more fields, each with its own type. The syntax is
data U a_1, ..., a_n = C_1 fs_1 | ... | C_n fs_nwhere
U is a ulongvar naming the ADT,
the a_i are lidents representing type arguments,
the C_i are the constructors,
and the fs_i are their associated fields.
If a constructor C_i has zero fields, fs_i is empty.
If it has one field, fs_i is simply a type expression defining
the type of that field.
Otherwise fs_i has the form (t_1,...,t_m) where each
t_i is a type expression.
The fs_i may not contain free type variables other than the
type arguments.
The arguments may be surrounded by square brackets for readability.
Pragma declarations do not change the meaning of a program, but provide instructions on how it should be evaluated. The following pragma declarations are currently recognized:
A pragma declaration within a block
affects for the evaluation of the program from the point of the pragma
declaration onwards, and for all nested function calls.
However, a pragma declaration within a function does not affect the
calling function.
A pragma declaration can be overridden by another pragma declaration.
For example, suppose that function f calls g, which in
turn calls h, which in turn calls j.
The function g begins with a pragma eager declaration,
while j begins with a pragma lazy declaration.
The pragma eager declaration will affect the function g
in which it appears, as well as the called function h. However,
it does not affect j since it is overridden by the declaration
in j. It also does not affect f, since f is not
called by g.