Local Variables
Local variables, as opposed to class/object fields, are defined only for the life of a method. If a method has any parameters, those parameters are available to the method body as local variables, with names that match the declared method parameter names.
For object methods, as opposed to static methods, there is also an implicit local variable named “this”, that is a reference to the object that the method is being executed on.
Bindings
JFuncMachine allows you to define additional variables
using a Binding class. A binding is a list of variable
names and expressions, where each expression is evaluated
that it value assigned to a local variable with the
corresponding name. The binding also has an expression that
is executed in the context of all the bound variables.
Here is an example Binding that binds two variables, x and y,
each assigned a constant int value, then the binding expression
fetches the values and adds them together:
new org.jfuncmachine.compiler.model.expr.Binding(
new Binding.BindingPair[] {
new Binding.BindingPair("x", new IntConstant(20)),
new Binding.BindingPair("y", new IntConstant(22))
},
Binding.Visibility.Separate,
new InlineCall(Inlines.IntAdd,
new Expression[] {
new GetValue("x", SimpleTypes.INT),
new GetValue("y", SimpleTypes.INT)
}))The Binding has a visibility parameter, which in the previous
example is Binding.Visibility.Separate. The Separate parameter
means that each binding definition does not see any of the
other bindings in this binding. That is, the expression for y
in the previous example could not reference the variable x that
was defined just before it, although if there was already a
variable x visible to the binding, it would be able to access that x.
The other option visibility setting is Previous, which means
that a binding pair can see the variables that came before
it in the current binding, but not those that come after it.
Here is a similar example where the expression for y uses
the value bound to x:
new Binding(
new Binding.BindingPair[] {
new Binding.BindingPair("x", new IntConstant(20)),
new Binding.BindingPair("y",
new InlineCall(Inlines.IntAdd,
new Expression[] {
new GetValue("x", SimpleTypes.INT),
new IntConstant(2)
}))
},
Binding.Visibility.Previous,
new InlineCall(Inlines.IntAdd,
new Expression[] {
new GetValue("x", SimpleTypes.INT),
new GetValue("y", SimpleTypes.INT)
})));If the visibility is set to Previous then there is an error
that the x variable cannot be found.
Variables defined in a binding are not visible outside the binding. If a variable in a binding has the same name as a variable that was already visible to the binding, the new variable “shadows” the existing one and the existing one cannot be accessed from within the binding.
Getting Variable Values
As you have seen in the previous two examples, the GetValue
expression is used to fetch the value of a local variable. You
simply supply a variable name and type to fetch the value.
Setting Variable Values
While it isn’t quite the functional way to do things, JFuncMachine
allows you to set local variable using the SetValue expression,
which takes a variable name and an expression to store in
that variable.
The following example binds a value to a variable, then uses
SetValue to update the binding with a new value, and then
uses GetValue to return the value. This example also uses
the Block class which allows you to define an expression as
a sequence of expressions, where the Block return value
is the value of the last expression.
new Binding(
new Binding.BindingPair[] {
new Binding.BindingPair("x", new IntConstant(20))
},
Binding.Visibility.Separate,
new Block(new Expression[] {
new SetValue("x",
new InlineCall(Inlines.IntAdd, new Expression[]{
new GetValue("x", SimpleTypes.INT),
new IntConstant(22)
})),
new GetValue("x", SimpleTypes.INT)
})));Recursion with Named Bindings
Recursion is very common in functional languages to do things that would be done with loops in an imperative language. In Java, recursive function calls have a limit at how deep they can go because the stack size is limited.
JFuncMachine offers several ways to write code recursively
without having to do recursive function calls. One of those
is a named Binding combined with a BindingRecurse
expression.
The factorial function is a common example of a recursive function. A recursive function call version of factorial in Java might look like this:
public int factorial(int n) {
if (n < 3) {
return 1;
} else {
return n * factorial(n-1);
}
}This factorial function is simple to understand, but it is structured in a way that really needs stack-based recursion, because the function has to hold on to the n value, invoke factorial(n-1) and wait for the response before multiplying those values together.
If you were to implement this as a while loop in Java, you’d likely have an accumulator variable and do something like this:
public int factorial(int n) {
int acc = 1;
while (n > 1) {
acc = acc * n;
n = n - 1;
}
return acc;
}The named Binding + BindingRecurse version of factorial looks
very similar to the while-loop version:
new Binding("func", new Binding.BindingPair[] {
new Binding.BindingPair("n", new GetValue("n", SimpleTypes.LONG)),
new Binding.BindingPair("acc", new LongConstant(1l))
}, Binding.Visibility.Separate,
new If(new BinaryComparison(Tests.LT, new GetValue("n", SimpleTypes.LONG),
new LongConstant(2l)),
new GetValue("acc", SimpleTypes.LONG),
new BindingRecurse("func",
new Expression[] {
new InlineCall(Inlines.LongSub, new Expression[] {
new GetValue("n", SimpleTypes.LONG),
new LongConstant(1l),
}),
new InlineCall(Inlines.LongMul, new Expression[] {
new GetValue("n", SimpleTypes.LONG),
new GetValue("acc", SimpleTypes.LONG)
})
})))In this case, the first parameter to the Binding constructor
is a name. The n and acc variables are initially bound to
expressions. Then, in the body of the Binding the
BindingRecurse expression re-runs the binding expression
while updating the bindings. That is, the expressions in
the BindingRecurse expression correspond to the values bound
in the Binding, and it updates those bindings with the
new expression values before executing the binding expression
again.
Notice also that the BindingRecurse expression also includes
the name of the Binding to recurse to, so that if you
had several nested named bindings, you could choose how
far back to jump.