# Volatile functions

I’m writing a plugin function which is volatile – repeated calls to it return varying values – but I’m having trouble making this work.

As an example, consider the built-in rand() function. This returns a different value after each call (i.e. it has type “”IO double”” rather than “”double””, to borrow the Haskell convention).

Now if I write a Groovy function:

double groovy_rand() {

return new Random().nextDouble();

}

Then this behaves as I would expect, in that a 2D matrix with e.g. A1 = groovy_rand() produces a list of random numbers across category B.

But if I write a plugin function:

public class PluginRand extends AbstractQFunction {

public QValueList evaluate(QValueList[] args) {

return QValueFactory.createSingletonValueList(new Random().nextDouble());

}

public QType[] argumentTypes() {

return new QType[] {};

}

public QType returnType() {

return QType.cValueType;

}

}

then doing the same A1 = pluginrand() just produces a list across B of the same *first* random number.

How can I obtain the Groovy-like volatile behaviour from a plugin?

Many thanks.

Hi Apollo,

This is a consequence of Quantrix’s optimized calculation process. Quantrix will only recalculate a cell’s value if it detects a change to another cell on which that value depends. When you use your function to calculate a cell, that cell’s value isn’t derived from any other cells, so Quantrix decides that it can’t possibly need to be re-calculated because it has no dependencies. Since the cell isn’t being calculated, your function never gets evaluated and it doesn’t get the chance to offer up a new value.

Luckily, you can write your function so that it saves a reference to its owning formula, then listens for calculation events on the model and forces the formula to recalculate itself if the model calculates. Try this:

[code:165h4yu0]

public class PluginRand extends AbstractQFunction

{

private QFormulaContext formula;

private QuantrixEventListener calculationListener = new QuantrixEventListener()

{

@Override

public void notify(QuantrixEvent event)

{

if (event.getType() == QuantrixEventType.cModelWillCalculate)

formula.forceRecalc();

}

};

@Override

public void registerFormulaContext(QFormulaContext context)

{

formula = context;

Model model = formula.getModel();

if (model != null)

model.addListener(calculationListener);

}

@Override

public void unregisterFormulaContext(QFormulaContext context)

{

Model model = formula.getModel();

if (model != null)

model.removeListener(calculationListener);

formula = null;

}

@Override

public QValueList evaluate(QValueList[] args)

{

return QValueFactory.createSingletonValueList(Math.random());

}

@Override

public QType[] argumentTypes()

{

return new QType[]{};

}

@Override

public QType returnType()

{

return QType.cValueType;

}

}

[/code:165h4yu0]