What is the explanation that instances of Data1 and Data2 have different capabilities?
The line where an object of Data1 class is instantiated compiles without a problem, but the line with Data2 get's the error saying that "right side must be a subtype of left side".
class Data1
let _x: U8 = 0
class Data2
let _x: U8
new create() => _x = 0
actor Main
new create(env: Env) =>
let d1: Data1 iso = Data1
let d2: Data2 iso = Data2
In Pony, there are many places where you can omit basic elements of syntax or structure, and expect them to be filled in with implicit defaults. The answer to your question here about the difference between Data1
and Data2
has to do with two examples of implicit defaults, which happen to not have the same capability.
The Data2
class has a single constructor, new create() => _x = 0
, which has a implicit default receiver capability of ref
. That is, it implicitly expands to new ref create() => _x = 0
.
The Data1
class has no constructor, so Pony creates an implicit default constructor for you, which is new iso create()
. The _x = 0
from your field declaration also gets implicitly transferred to the body of the constructor, but that's somewhat outside the scope of your question.
So, in this case, assigning let d1: Data1 iso = Data1
, since the created object will be of type Data1 iso^
, which can be assigned to Data1 iso
. Assigning let d2: Data2 iso = Data2
doesn't work, since the created object will be of type Data2 ref^
, which can't be assigned to Data2 iso
without breaking the isolation guarantees.
Changing your Data2
constructor to new iso create()
is the best solution for making your example code work. We don't use iso
as the implicit default capability for constructors because it would put additional constraints on the parameters to the constructor (they would all have to be sendable).
For completeness, note that there is another way around your problem, on the caller side. If you place the constructor call in a recover
block, you can "lift" to a capability that has stronger guarantees (for example, from ref
to iso
). This works because the recover
block enforces other constraints on object references used within it (for example, any references passing into the recover block must be sendable), that will uphold the guarantees you are lifting to. This assignment would look like:
let d2: Data2 iso = recover Data2 end