Here's my code:
class Eapproximator
var step : F64
new create(step' :F64) =>
step = step'
fun evaluate() :F64 =>
var total = F64(0)
var value = F64(1)
while total < 1 do
total = total + step
value = value + (value * step)
end
value
actor Main
new create(env: Env) =>
var e_approx = Eapproximator(0.00001)
var e_val = e_approx.evaluate()
env.out.print(e_val.string())
It works well and prints (as expected) 2.7183. However, if I replace class with actor in Eapproximator definition I get a bunch of errors:
Error:
/src/main/main.pony:18:34: receiver type is not a subtype of target type
var e_val = e_approx.evaluate()
^
Info:
/src/main/main.pony:18:17: receiver type: Eapproximator tag
var e_val = e_approx.evaluate()
^
/src/main/main.pony:6:3: target type: Eapproximator box
fun evaluate() :F64 =>
^
/src/main/main.pony:3:3: Eapproximator tag is not a subtype of Eapproxim
ator box: tag is not a subcap of box
new create(step' :F64) =>
^
Error:
/src/main/main.pony:19:19: cannot infer type of e_val
env.out.print(e_val.string())
What can I do to fix this?
The actor is the unit of concurrency in Pony. This means that many different actors in the same program can run at the same time, including your
MainandEapproximatoractors. Now what would happen if the fields of an actor were modified by multiple actors at the same time? You'd most likely get some garbage value in the end because of the way concurrent programs work on modern hardware. This is called a data race and it is the source of many, many bugs in concurrent programming. One of the goals of Pony is to detect data races at compile time, and this error message is the compiler telling you that what you're trying to do is potentially unsafe.Let's walk through that error message.
The receiver type is the type of the called object,
e_approxhere. The target type is the type ofthisinside of the method,Eapproximator.evaluatehere. Subtyping means that an object of the subtype can be used as if it was an object of the supertype. So that part is telling you thatevaluatecannot be called one_approxbecause of a type mismatch.e_approxis anEapproximator tag. Atagobject can neither be read nor written. I'll detail whye_approxistagin a minute.thisinside ofevaluateis anEapproximator box. Aboxobject can be read, but not written.thisisboxbecauseevaluateis declared asfun evaluate, which implicitly meansfun box evaluate(which means that by default, methods cannot modify their receiver.)According to this error message, a
tagobject isn't a subtype of aboxobject, which means that atagcannot be used as if it was abox. This is logical if we look at whattagandboxallow.boxallows more things thantag: it can be read whiletagcannot. A type can only be a subtype of another type if it allows less (or as much) things than the supertype.So why does replacing
classwithactormake the objecttag? This has to do with the data race problems I talked about earlier. An actor has free reign over its own fields. It can read from them and write to them. Since actors can run concurrently, they must be denied access to each other's fields in order to avoid data races with the fields' owner. And there is something in the type system that does exactly that:tag. An actor can only see other actors astag, because it would be unsafe to read from or write to them. The main useful thing it can do with thosetagreferences is send asynchronous messages (by calling thebemethods, or behaviours), because that's neither reading nor writing.Of course, since you're not doing any mutation of
Eapproximatorin your program, your specific case would be safe. But it is much easier to try to forbid every unsafe program than to try to allow every safe program in addition to that.To sum it up, there isn't really a fix for your program, except keeping
Eapproximatoras a class. Not anything needs to be an actor in a Pony program. The actor is the unit of concurrency, but that means it is also the unit of sequentiality. Computations that need to be sequential and synchronous must live in a single actor. You can then break down those computations into various classes for good code hygiene.